Publishing Guide
This guide walks a plugin author through publishing a signed plugin to the
Network Plugin Registry (@streetjs/registry-server) using the
street registry publish command. The registry hosts, indexes, and serves
signed plugins over a /api/v1 REST API; every published version is verified
with an Ed25519 signature over its manifest before it is stored.
See also the Installation Guide for the consumer side of the flow.
Prerequisites
- A running registry endpoint. Point the CLI at it with
--registry <url>or theSTREET_REGISTRY_URLenvironment variable. Defaults tohttp://localhost:8787. - A publisher bearer token issued by the registry operator. The registry
stores only the SHA-256 hash of your key — never the raw token. Supply it with
--token <apiKey>or theSTREET_REGISTRY_TOKENenvironment variable. - Namespace ownership. A publisher may only publish plugins whose namespace
it owns. The namespace is the segment before the first
/, with a leading@removed — so@acme/widgetsandacme/widgetsboth belong to namespaceacme. Publishing outside an owned namespace is rejected withUNAUTHORIZED.
1. Author the manifest
Create a manifest.json describing the plugin. Required fields are name and a
semver version (MAJOR.MINOR.PATCH); capabilities and dependencies are
optional but recommended for discovery and resolution.
1
2
3
4
5
6
{
"name": "acme/widgets",
"version": "1.2.0",
"capabilities": ["widgets"],
"dependencies": {}
}
The manifest metadata is validated before anything is stored. A missing
required field, a malformed version, or a duplicate name@version is rejected
with an error that identifies the offending field.
2. Generate a signing key
Each publisher signs manifests with an Ed25519 private key. Generate one with the Node runtime that ships with StreetJS:
1
node -e "const {generateKeyPairSync}=require('node:crypto');const {privateKey}=generateKeyPairSync('ed25519');require('node:fs').writeFileSync('publisher.key.pem',privateKey.export({type:'pkcs8',format:'pem'}));"
Keep publisher.key.pem secret. The CLI derives the matching public key from it
automatically and sends the public key with the publish request so the registry
can verify the signature. (You may also supply a public key explicitly with
--public-key <path>.)
3. Package the plugin
Build a tarball of your plugin’s distributable files:
1
npm pack # produces acme-widgets-1.2.0.tgz
4. Publish
1
2
3
4
5
6
7
8
9
street registry publish \
--registry https://registry.example.com \
--token "$STREET_REGISTRY_TOKEN" \
--manifest ./manifest.json \
--tarball ./acme-widgets-1.2.0.tgz \
--key ./publisher.key.pem \
--categories ui,tools \
--tags widget \
--description "A widget plugin"
On success the CLI prints the published identity, the stored tarball checksum, and the publish timestamp:
1
2
3
[street] Published acme/widgets@1.2.0
[street] tarball checksum: 9f86d0818884...
[street] published at: 2026-01-01T00:00:00.000Z
What the CLI does
- Loads
manifest.jsonand signs it with your Ed25519 private key — the signature covers a deterministic, key-sorted canonical form of the manifest body and is recorded alongside a SHA-256checksum. - Derives the public key from your private key (or reads
--public-key). - Base64-encodes the tarball.
POSTs{ manifest, publicKeyPem, tarballBase64, … }to/api/v1/pluginswithAuthorization: Bearer <token>.
What the registry does
In order, the registry: authenticates the bearer token → validates the manifest
metadata → authorizes the publisher for the namespace → rejects duplicate
name@version → verifies the Ed25519 signature and checksum → stores the signed
manifest, public key, tarball, and indexed metadata. Any rejection leaves the
store untouched, preserving previously published valid versions.
Error reference
| Exit | Code | Meaning |
|---|---|---|
| 1 | UNAUTHENTICATED |
No valid publisher token was presented. |
| 1 | UNAUTHORIZED |
The token does not own the plugin’s namespace. |
| 1 | INVALID_MANIFEST |
A required metadata field is missing or malformed (the offending field is named). |
| 1 | DUPLICATE |
name@version already exists. |
| 1 | INTEGRITY_FAILED |
The signature, checksum, key, or tarball failed validation. |
A non-zero exit code makes the command safe to gate a release pipeline on.