Write a plugin
A plugin extends Speedwave with a containerized MCP worker Speedwave can call, or with a bundle of skills, commands, agents, and hooks. This page covers what a plugin is made of and how to get from source to a package Speedwave loads. Plugins are written, developed, and shipped exclusively by Speednet: a plugin built from this guide reaches users only after Speednet signs and releases it. If your plugin ships a worker, the Write an MCP server guide covers its boot contract and tool conventions.
What it does
Section titled “What it does”Writing a plugin means producing source, not a finished package. You write a manifest and, for an MCP plugin, worker code and a container definition. Speednet turns that source into a signed ZIP, and Speedwave builds the image locally from verified contents.
What it consists of
Section titled “What it consists of”A plugin.json manifest with a service_id, a Containerfile, and the worker source. It may also ship a claude-resources directory.
A plugin.json manifest with neither service_id nor Containerfile, plus claude-resources: skills, commands, agents, and hooks. There is no worker container.
The manifest needs name, slug, version, and description. The slug is the identifier everything else derives from: install directory, config key, compose service name, worker environment variable, and token directory. It must match ^[a-z][a-z0-9-]{0,63}$ and cannot collide with a built-in service, whether by ID (slack, github, office), by derived compose name (mcp-<slug>), or bare (a slug of claude is rejected). For an MCP service plugin, service_id must equal slug.
Optional fields cover credentials (auth_fields), a settings form (settings_schema), setup notes (instructions), an OAuth flow (oauth), a desktop companion link (host_bridge), and resource caps (mem_limit, cpu_limit). See Plugin mechanism for size limits and validation rules behind each field.
How it works
Section titled “How it works”For an MCP plugin, your Containerfile builds a worker that inherits Speedwave’s container hardening: dropped capabilities, no new privileges, a read-only root filesystem, and a noexec tmpfs at /tmp. None of this is configurable, so design the worker to run unprivileged with nothing written to the root filesystem, within a cap of 16 GiB memory and 4 CPUs.
Two mounts cross the container boundary: /tokens, always read-only, where the worker reads credentials from /tokens/<key> and can never write; and /workspace, read-write, the only surface for state the worker needs to persist.
If the plugin authenticates through OAuth, declare an oauth block: Speedwave’s own oauth worker drives authorization and refresh, so long-lived secrets stay host-side and only a short-lived bearer token reaches the container. Speedwave supports three grant types, each tied to a required manifest field: authorization_code needs oauth.authorize_url, device_code needs oauth.device_authorization_url, and client_credentials needs oauth.client_secret_field. Any endpoint URL you provide is validated before Speedwave accepts the manifest.
Set it up
Section titled “Set it up”-
Write
plugin.json, theContainerfile(for an MCP plugin), the worker source, and anyclaude-resources. -
Send the source to Speednet for review and signing. Speednet builds the ZIP, signs it with the Ed25519 private key, and adds a
SIGNATUREfile. -
Install the signed package in Speedwave, which verifies the signature against its embedded Speednet public key.
-
Speedwave runs install-time validation, then builds a local image from the verified source.
-
Enable the plugin for a project and fill in any credentials the manifest asks for.
Before you send the source off, test it locally: build the Containerfile, mount /tokens read-only and /workspace read-write, and confirm the worker starts unprivileged against a read-only root. That is the shape Speedwave enforces after install, so a mismatch caught now saves a signing round trip.
Security boundaries
Section titled “Security boundaries”A plugin can reach its own /workspace and read its own /tokens. It cannot write credentials, escalate privileges, or reach another plugin’s token directory. Speedwave re-verifies the signature every time it renders compose, builds the image, or lists the plugin in Desktop, so an altered package stops loading and nothing in plugin.json lets you self-sign or relax the hardening.
For how signature verification, the manifest schema, and storage work underneath, see Plugin mechanism.