Packages
backplane_plugin_protocol
1.10.71
1.10.77
1.10.76
1.10.75
1.10.74
1.10.73
1.10.72
1.10.71
1.10.70
1.10.69
1.10.68
1.10.67
1.10.66
1.10.65
1.10.63
1.10.62
1.10.61
1.10.60
1.10.59
1.10.58
1.10.57
1.10.56
1.10.55
1.10.54
1.10.53
1.10.52
1.10.51
1.10.50
1.10.49
1.10.48
1.10.47
1.10.46
1.10.45
1.10.44
1.10.43
1.10.42
1.10.41
1.10.40
1.10.39
1.10.38
1.10.37
1.10.36
1.10.35
1.10.34
Agent Plugins 1.0 package loading and Backplane distribution client
Current section
Files
Jump to
Current section
Files
backplane_plugin_protocol
README.md
README.md
# Backplane Plugin Protocol
Standalone Agent Plugins **1.0.0** package inspection and Backplane distribution
client. No Repo, Phoenix, application supervisor, process execution, filesystem
installation, credential storage or network adapter is required.
```elixir
{:ok, package} = Backplane.PluginProtocol.load(%{
"plugin.json" => JSON.encode!(%{
"$schema" => "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
"name" => "example"
})
})
{:ok, package} = Backplane.PluginProtocol.load_directory("/path/to/example")
{:ok, artifact} = Backplane.PluginProtocol.Archive.pack(package.files)
digest = Backplane.PluginProtocol.Archive.digest(artifact)
```
`load/2` returns `{:ok, package}` or `{:error, diagnostics}`. Package fields:
- `manifest`: original JSON object, including unknown fields and extensions.
Unknown fields are reported and ignored for interpretation; original bytes stay
in `files`. Missing `version` and arbitrary version strings are supported.
- `skills`: immediate `skills/*/SKILL.md` entries with `path`, `root`, `name`,
`description`, raw `content`, Markdown `body`, YAML `metadata`, and resource
`files` keyed by their full package-relative paths.
- `mcp_servers`: server-name map of validated raw string-key configurations.
Types are `stdio`, `streamable-http`, and optional legacy `sse`.
- `diagnostics`: `%{severity: :error | :warning, code: atom, path: string,
message: string}`. Component errors can appear in a successful package.
- `files`: the complete original relative-path-to-binary snapshot.
Manifest errors reject the package, except unknown top-level fields and non-object
`extensions`. Unimplemented extension values are not validated. Invalid individual
skills or MCP servers are skipped. Invalid `mcp.json` disables only MCP. Schemas
are bundled as references; loading selects locally implemented rules and never
downloads a schema. `transports: ["stdio", "streamable-http"]` restricts discovery
to supported transports, with diagnostics for skipped transports.
MCP values remain unexpanded. Runtime hosts must expand only `${PLUGIN_ROOT}` and
`${PLUGIN_DATA}`, once, in args/env values/cwd; provide both reserved variables;
recheck resolved command/cwd containment; enforce HTTP origin and header rules;
and handle connection failures per server. The loader validates reserved env
names, token commands, cwd containment, HTTPS/loopback URLs and HTTP headers.
Arguments and environment values remain opaque, including unknown placeholders.
## Snapshots and safety
`Archive.pack/2`, `unpack/2`, `digest/1`, and `read_directory/2` are shared by
publishers and installers. Archives use sorted deterministic USTAR in gzip,
fixed uid/gid/mtime, and regular files with **0644** default permissions.
`pack(files, modes: %{relative_path => 0o755})` preserves executable resources.
`unpack_with_metadata/2` and `read_directory_with_metadata/2` return
`{:ok, %{files: files, modes: modes}}`, preserving low permission bits only;
privileged setuid/setgid/sticky bits are never retained. File maps alone carry
bytes, so publishers and installers should use the metadata APIs. Paths that cannot fit
USTAR name/prefix fields fail packing. Unpacking accepts USTAR regular files and
directories, ignores empty directories, and rejects links, special files, PAX/GNU
extensions, duplicates, invalid checksums, absolute paths, traversal, Windows
separators/drive prefixes, file/directory conflicts and trailing nonzero data.
Decoding happens in memory and never extracts filesystem paths.
Defaults: 32 MiB compressed, 80 MB decompressed tar, 64 MiB total file bytes,
16 MiB per file, 10,000 files, 20,000 entries and 64 filesystem levels. Override
with `max_archive_bytes`, `max_uncompressed_bytes`, `max_total_bytes`,
`max_file_bytes`, `max_files`, `max_entries`, `max_depth`. Strict `Archive` directory
readers reject all links and special files. `PluginProtocol.load_directory/2`
instead resolves safe links within the filesystem-resolved plugin root, retaining
their target bytes under the original package-relative alias paths. An escaping
manifest rejects the plugin; an escaping fixed component location disables that
component, an escaping skill document skips that skill, and other escaping
resources are omitted with diagnostics. Link and directory cycles are denied.
The file-map and archive snapshot APIs remain unchanged. Callers must keep the
directory stable during reads; these APIs are not an OS sandbox against concurrent
hostile filesystem mutation.
## Distribution client
```elixir
client = Backplane.PluginProtocol.Client.new(
base_url: "https://backplane.example",
auth: access_token,
transport: fn request -> MyHTTP.request(request) end
)
{:ok, catalog} = Backplane.PluginProtocol.Client.catalog(client)
{:ok, release} = Backplane.PluginProtocol.Client.resolve(client,
plugin_id: plugin_id, channel: "stable")
{:ok, download} = Backplane.PluginProtocol.Client.download(client,
release["artifact_digest"])
```
Transport accepts `%{method: method, url: string, headers: pairs, body: nil | binary}` and
returns `{:ok, %{status: integer, body: binary | map}} | {:error, reason}`. It must
enforce TLS, timeouts, response limits, cancellation and safe redirect policy.
`auth` also accepts a zero-arity header provider; `headers` supplies extra pairs.
Catalog and resolve return the decoded API object. Download checks the exact
SHA-256 before unpacking/loading, then returns `%{artifact, artifact_digest,
files, modes, package}`. HTTP failures return `{:error, {:http_status, status}}` without
exposing raw provider bodies. Endpoints are `/plugin-protocol/v1/catalog`,
`/resolve` (GET query: plugin_id/release_id/channel), and `/artifacts/:digest`.
This is Backplane's API, not an official Agent Plugins registry protocol.
`Client.request(client, method, path, body \\ nil)` makes a JSON request under
the same prefix for installation listing/reporting. `path` starts with `/` and
cannot contain traversal, a query or an alternate origin. A supplied body is
JSON-encoded with `content-type: application/json`; responses are decoded objects.
Specification references, checked 2026-10-23:
- https://agent-plugins.org/specification
- https://agent-plugins.org/plugin-authors/manifest
- https://agent-plugins.org/client-implementers/loading-and-discovery
- https://agent-plugins.org/client-implementers/mcp-runtime
- https://agentskills.io/specification