EQTY LabDocs

Upload plugin artifact

Uploads a compiled plugin as multipart form data and records its SHA-256. Uploading bytes already stored returns the existing artifact with the new signature. The Control Plane signs the artifact when a publisher signer is configured; otherwise supply signature and signer_key_id, and the gateway loads only artifacts signed by a trusted key. Admin only; API keys are refused.

posthttps://<host>/api/v1/plugins/artifacts
bash
curl --request POST \
  --url 'https://<host>/api/v1/plugins/artifacts' \
  --header 'Authorization: Bearer <token>' \
  --form 'artifact=@<file>' \
  --form 'version=<version>' \
  --form 'abi_version=<abi_version>' \
  --form 'filename=<filename>' \
  --form 'signature=<signature>' \
  --form 'signer_key_id=<signer_key_id>' \
  --form 'status=<status>'
javascript
const form = new FormData();
form.append("artifact", file); // a File or Blob
form.append("version", "<version>");
form.append("abi_version", "<abi_version>");
form.append("filename", "<filename>");
form.append("signature", "<signature>");
form.append("signer_key_id", "<signer_key_id>");
form.append("status", "<status>");

const response = await fetch("https://<host>/api/v1/plugins/artifacts", {
  method: "POST",
  headers: {
    "Authorization": "Bearer <token>"
  },
  body: form
});

const data = await response.json();
typescript
declare const file: Blob;

interface ArtifactResponse {
  correlation_id?: string;
  item?: Record<string, unknown>;
}

const form = new FormData();
form.append("artifact", file); // a File or Blob
form.append("version", "<version>");
form.append("abi_version", "<abi_version>");
form.append("filename", "<filename>");
form.append("signature", "<signature>");
form.append("signer_key_id", "<signer_key_id>");
form.append("status", "<status>");

const response = await fetch("https://<host>/api/v1/plugins/artifacts", {
  method: "POST",
  headers: {
    "Authorization": "Bearer <token>"
  },
  body: form
});
if (!response.ok) throw new Error(`${response.status} ${response.statusText}`);

const data: ArtifactResponse = await response.json();
python
import requests

response = requests.post(
    "https://<host>/api/v1/plugins/artifacts",
    headers={
        "Authorization": "Bearer <token>",
    },
    data={
        "version": "<version>",
        "abi_version": "<abi_version>",
        "filename": "<filename>",
        "signature": "<signature>",
        "signer_key_id": "<signer_key_id>",
        "status": "<status>",
    },
    files={"artifact": open("<file>", "rb")},
)
response.raise_for_status()
print(response.json())
go
package main

import (
	"bytes"
	"fmt"
	"io"
	"mime/multipart"
	"net/http"
	"os"
	"path/filepath"
)

func main() {
	var body bytes.Buffer
	form := multipart.NewWriter(&body)
	file, err := os.Open("<file>")
	if err != nil {
		panic(err)
	}
	defer file.Close()
	part, err := form.CreateFormFile("artifact", filepath.Base(file.Name()))
	if err != nil {
		panic(err)
	}
	if _, err := io.Copy(part, file); err != nil {
		panic(err)
	}
	if err := form.WriteField("version", "<version>"); err != nil {
		panic(err)
	}
	if err := form.WriteField("abi_version", "<abi_version>"); err != nil {
		panic(err)
	}
	if err := form.WriteField("filename", "<filename>"); err != nil {
		panic(err)
	}
	if err := form.WriteField("signature", "<signature>"); err != nil {
		panic(err)
	}
	if err := form.WriteField("signer_key_id", "<signer_key_id>"); err != nil {
		panic(err)
	}
	if err := form.WriteField("status", "<status>"); err != nil {
		panic(err)
	}
	if err := form.Close(); err != nil {
		panic(err)
	}

	req, err := http.NewRequest(http.MethodPost, "https://<host>/api/v1/plugins/artifacts", &body)
	if err != nil {
		panic(err)
	}
	req.Header.Set("Authorization", "Bearer <token>")
	req.Header.Set("Content-Type", form.FormDataContentType())

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()

	data, err := io.ReadAll(res.Body)
	if err != nil {
		panic(err)
	}
	fmt.Println(res.Status, string(data))
}
java
import java.io.ByteArrayOutputStream;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;

public class Example {
    public static void main(String[] args) throws Exception {
        String boundary = "GuardianBoundary" + System.nanoTime();
        ByteArrayOutputStream form = new ByteArrayOutputStream();
        Path file = Path.of("<file>");
        form.writeBytes(("--" + boundary + "\r\nContent-Disposition: form-data; name=\"artifact\"; filename=\"" + file.getFileName() + "\"\r\n\r\n").getBytes(StandardCharsets.UTF_8));
        form.writeBytes(Files.readAllBytes(file));
        form.writeBytes("\r\n".getBytes(StandardCharsets.UTF_8));
        form.writeBytes(("--" + boundary + "\r\nContent-Disposition: form-data; name=\"version\"\r\n\r\n<version>\r\n").getBytes(StandardCharsets.UTF_8));
        form.writeBytes(("--" + boundary + "\r\nContent-Disposition: form-data; name=\"abi_version\"\r\n\r\n<abi_version>\r\n").getBytes(StandardCharsets.UTF_8));
        form.writeBytes(("--" + boundary + "\r\nContent-Disposition: form-data; name=\"filename\"\r\n\r\n<filename>\r\n").getBytes(StandardCharsets.UTF_8));
        form.writeBytes(("--" + boundary + "\r\nContent-Disposition: form-data; name=\"signature\"\r\n\r\n<signature>\r\n").getBytes(StandardCharsets.UTF_8));
        form.writeBytes(("--" + boundary + "\r\nContent-Disposition: form-data; name=\"signer_key_id\"\r\n\r\n<signer_key_id>\r\n").getBytes(StandardCharsets.UTF_8));
        form.writeBytes(("--" + boundary + "\r\nContent-Disposition: form-data; name=\"status\"\r\n\r\n<status>\r\n").getBytes(StandardCharsets.UTF_8));
        form.writeBytes(("--" + boundary + "--\r\n").getBytes(StandardCharsets.UTF_8));

        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("https://<host>/api/v1/plugins/artifacts"))
            .header("Authorization", "Bearer <token>")
            .header("Content-Type", "multipart/form-data; boundary=" + boundary)
            .method("POST", HttpRequest.BodyPublishers.ofByteArray(form.toByteArray()))
            .build();

        HttpResponse<String> response = HttpClient.newHttpClient()
            .send(request, HttpResponse.BodyHandlers.ofString());
        System.out.println(response.statusCode() + " " + response.body());
    }
}
csharp
using System.Net.Http.Headers;

using var client = new HttpClient();
using var request = new HttpRequestMessage(HttpMethod.Post, "https://<host>/api/v1/plugins/artifacts");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "<token>");
using var form = new MultipartFormDataContent();
form.Add(new StreamContent(File.OpenRead("<file>")), "artifact", Path.GetFileName("<file>"));
form.Add(new StringContent("<version>"), "version");
form.Add(new StringContent("<abi_version>"), "abi_version");
form.Add(new StringContent("<filename>"), "filename");
form.Add(new StringContent("<signature>"), "signature");
form.Add(new StringContent("<signer_key_id>"), "signer_key_id");
form.Add(new StringContent("<status>"), "status");
request.Content = form;

using var response = await client.SendAsync(request);
Console.WriteLine($"{(int)response.StatusCode} {await response.Content.ReadAsStringAsync()}");
php
<?php

$ch = curl_init('https://<host>/api/v1/plugins/artifacts');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer <token>',
    ],
    CURLOPT_POSTFIELDS => [
        'artifact' => new CURLFile('<file>'),
        'version' => '<version>',
        'abi_version' => '<abi_version>',
        'filename' => '<filename>',
        'signature' => '<signature>',
        'signer_key_id' => '<signer_key_id>',
        'status' => '<status>',
    ],
    CURLOPT_RETURNTRANSFER => true,
]);

$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
echo $status . " " . $response . PHP_EOL;
ruby
require "net/http"

uri = URI("https://<host>/api/v1/plugins/artifacts")
request = Net::HTTP::Post.new(uri)
request["Authorization"] = "Bearer <token>"
request.set_form([
  ["artifact", File.open("<file>")],
  ["version", "<version>"],
  ["abi_version", "<abi_version>"],
  ["filename", "<filename>"],
  ["signature", "<signature>"],
  ["signer_key_id", "<signer_key_id>"],
  ["status", "<status>"],
], "multipart/form-data")

response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == "https") do |http|
  http.request(request)
end
puts "#{response.code} #{response.body}"
rust
// Cargo.toml dependencies: reqwest (features "multipart"), tokio (features "full")

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let form = reqwest::multipart::Form::new()
        .part("artifact", reqwest::multipart::Part::bytes(std::fs::read("<file>")?).file_name("<file>"))
        .text("version", "<version>")
        .text("abi_version", "<abi_version>")
        .text("filename", "<filename>")
        .text("signature", "<signature>")
        .text("signer_key_id", "<signer_key_id>")
        .text("status", "<status>");

    let response = reqwest::Client::new()
        .post("https://<host>/api/v1/plugins/artifacts")
        .header("Authorization", "Bearer <token>")
        .multipart(form)
        .send()
        .await?;

    println!("{} {}", response.status(), response.text().await?);
    Ok(())
}

Authorizations

  • BearerAuth · API key in header "Authorization" — Type "Bearer" followed by a space and a sign-in token: a Guardian Platform JWT, or an OIDC ID token in a standalone deployment.

Parameters

Form parameters

NameTypeDescription
artifact
required
file

Compiled plugin

version
required
string

Plugin version

abi_versionstring

Plugin ABI version

filenamestring

File name; defaults to the uploaded file's name

signaturestring

Artifact signature

signer_key_idstring

Key ID of the signature

statusstring (enum)

Artifact status

One ofactivedisabled

Default "active"

Responses

200Artifact already stored

application/json
  • correlation_idstring
  • itemplugins.PluginArtifact
    Show plugins.PluginArtifact
    • abi_versionstring
    • created_atstring
    • download_url_templatestring
    • filenamestring
    • idinteger
    • sha256string
    • signaturestring
    • signer_key_idstring
    • size_bytesinteger
    • statusplugins.ArtifactStatus

      One ofactivedisabled

    • versionstring
Examplejson
{
  "correlation_id": "string",
  "item": {
    "abi_version": "string",
    "created_at": "string",
    "download_url_template": "string",
    "filename": "string",
    "id": 0,
    "sha256": "string",
    "signature": "string",
    "signer_key_id": "string",
    "size_bytes": 0,
    "status": "active",
    "version": "string"
  }
}

201Created

application/json
  • correlation_idstring
  • itemplugins.PluginArtifact
    Show plugins.PluginArtifact
    • abi_versionstring
    • created_atstring
    • download_url_templatestring
    • filenamestring
    • idinteger
    • sha256string
    • signaturestring
    • signer_key_idstring
    • size_bytesinteger
    • statusplugins.ArtifactStatus

      One ofactivedisabled

    • versionstring
Examplejson
{
  "correlation_id": "string",
  "item": {
    "abi_version": "string",
    "created_at": "string",
    "download_url_template": "string",
    "filename": "string",
    "id": 0,
    "sha256": "string",
    "signature": "string",
    "signer_key_id": "string",
    "size_bytes": 0,
    "status": "active",
    "version": "string"
  }
}

400Bad Request

application/json
  • codestring
  • correlation_idstring
  • messagestring
Examplejson
{
  "code": "string",
  "correlation_id": "string",
  "message": "string"
}

401Unauthorized

application/json
  • codestring
  • correlation_idstring
  • messagestring
Examplejson
{
  "code": "string",
  "correlation_id": "string",
  "message": "string"
}

403Forbidden

application/json
  • codestring
  • correlation_idstring
  • messagestring
Examplejson
{
  "code": "string",
  "correlation_id": "string",
  "message": "string"
}

500Internal Server Error

application/json
  • codestring
  • correlation_idstring
  • messagestring
Examplejson
{
  "code": "string",
  "correlation_id": "string",
  "message": "string"
}

503Plugin or artifact store not configured

application/json
  • codestring
  • correlation_idstring
  • messagestring
Examplejson
{
  "code": "string",
  "correlation_id": "string",
  "message": "string"
}