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.
post
https://<host>/api/v1/plugins/artifactsbash
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
| Name | Type | Description |
|---|---|---|
artifactrequired | file | Compiled plugin |
versionrequired | string | Plugin version |
abi_version | string | Plugin ABI version |
filename | string | File name; defaults to the uploaded file's name |
signature | string | Artifact signature |
signer_key_id | string | Key ID of the signature |
status | string (enum) | Artifact status One of Default |
Responses
200Artifact already stored
application/json
correlation_idstringitemplugins.PluginArtifactShow plugins.PluginArtifact
abi_versionstringcreated_atstringdownload_url_templatestringfilenamestringidintegersha256stringsignaturestringsigner_key_idstringsize_bytesintegerstatusplugins.ArtifactStatusOne of
activedisabledversionstring
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_idstringitemplugins.PluginArtifactShow plugins.PluginArtifact
abi_versionstringcreated_atstringdownload_url_templatestringfilenamestringidintegersha256stringsignaturestringsigner_key_idstringsize_bytesintegerstatusplugins.ArtifactStatusOne of
activedisabledversionstring
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
codestringcorrelation_idstringmessagestring
Examplejson
{
"code": "string",
"correlation_id": "string",
"message": "string"
}401Unauthorized
application/json
codestringcorrelation_idstringmessagestring
Examplejson
{
"code": "string",
"correlation_id": "string",
"message": "string"
}403Forbidden
application/json
codestringcorrelation_idstringmessagestring
Examplejson
{
"code": "string",
"correlation_id": "string",
"message": "string"
}500Internal Server Error
application/json
codestringcorrelation_idstringmessagestring
Examplejson
{
"code": "string",
"correlation_id": "string",
"message": "string"
}503Plugin or artifact store not configured
application/json
codestringcorrelation_idstringmessagestring
Examplejson
{
"code": "string",
"correlation_id": "string",
"message": "string"
}