Plugin Development
Publishing
Sign a release, host it, and submit it to a plugin index, plus updates and key rotation.
Once a plugin works locally, you publish it by packaging a signed release, hosting it somewhere you control, and adding an entry to an index. StreamNook never hosts plugin artifacts. Authors do. The index just points at your files and pins your key.
The signing scheme, the full index.json field reference, and how key pinning works in detail live in Signing and the index format. This page is the workflow that uses them.
Which index
Official index
A focused, curated set of tier A and B plugins (emote and badge providers, chat tools, overlays, notifiers, integrations, local features). The app trusts it out of the box.
Community sources
Everything else, including tier C power-user add-ons that run their own background work or use the user's login. Anyone can run a community source. The format is identical. Users add one explicitly and confirm its key fingerprint.
The official index is the streamnook-plugins repository.
Your tier (declared in the manifest, verified by the curator) decides where you can be listed. You cannot self-certify into a lower tier. See the tier table in The manifest.
Note
The app.streamnook. id prefix is reserved for first-party plugins and is rejected from third parties. Pick a handle-based id like yourhandle.my-plugin.
Publish a release
Create your signing key
minisign -GKeep the secret key offline and treat it like a password. Your public key identifies you to every StreamNook user. The first install of your plugin pins it, and every future update must be signed with the same key. Losing or leaking it is a real problem.
Warning
There is no recovery if you lose this key. Back up the secret key somewhere safe and offline before you publish anything.
Package the release
Zip the plugin folder contents so the manifest sits at the zip root, not inside a subfolder:
my-plugin-1.0.0.zip
├── plugin.toml
├── my-plugin.exe (or dist/main.js for a ui plugin)
└── assets/Sign and hash
minisign -Sm my-plugin-1.0.0.zip
certutil -hashfile my-plugin-1.0.0.zip SHA256The first command produces my-plugin-1.0.0.zip.minisig. The second prints the SHA-256 you put in the index entry.
Host the files
Upload the zip and the .minisig to a stable, direct-download location you control. A GitHub release on your plugin's own repository is the expected shape.
Info
StreamNook never hosts plugin artifacts. The index only references URLs you own, so the download has to come from you.
Open a pull request to the index
Add your entry to index.json:
{
"id": "yourhandle.my-plugin",
"name": "My Plugin",
"version": "1.0.0",
"tier": "A",
"description": "One or two plain sentences about what it does.",
"homepage": "https://github.com/yourhandle/my-plugin",
"host_min": "8.0.0",
"released_at": "2026-06-10T00:00:00Z",
"author": {
"name": "yourhandle",
"pubkey": "RW...your minisign public key..."
},
"artifact": {
"url": "https://github.com/yourhandle/my-plugin/releases/download/v1.0.0/my-plugin-1.0.0.zip",
"sha256": "...",
"signature_url": "https://github.com/yourhandle/my-plugin/releases/download/v1.0.0/my-plugin-1.0.0.zip.minisig"
}
}Make the detail page worth opening by adding the optional presentation fields, which the marketplace renders:
"icon_url": "https://raw.githubusercontent.com/yourhandle/my-plugin/main/assets/icon.png",
"readme_url": "https://raw.githubusercontent.com/yourhandle/my-plugin/main/README.md"The README renders as plain markdown in the app (headings, lists, images, code blocks). Raw HTML shows as text, not rendered. Lead with what the plugin does and a screenshot.
Review
Before an entry merges, the curator checks:
- The manifest inside the artifact matches the index entry exactly (id, version, tier).
- The declared tier matches what the plugin actually does, including its network behavior.
- The signature verifies against the public key in the entry.
- The id does not squat another author's namespace. The
app.streamnook.prefix is reserved for first-party plugins and rejected from third parties.
Run the pre-publish checklist below before you open the PR. Everything the curator verifies, you can verify first.
Multi-platform builds
A process plugin is a native binary, so ship one build per OS. An index entry can carry a platforms map keyed by <os>-<arch>, each with its own url, hash, size, and signature. The supported keys are:
| Key | Platform |
|---|---|
windows-x86_64 | Windows on Intel or AMD |
macos-x86_64 | macOS on Intel |
macos-aarch64 | macOS on Apple Silicon |
linux-x86_64 | Linux on Intel or AMD |
linux-aarch64 | Linux on ARM |
The host installs the build matching the user's platform and shows the plugin as unavailable where there is no build. id, version, and tier must be identical across platforms.
A ui plugin is one artifact everywhere, so it needs no platforms map. The full shape is in Signing and the index format.
Updating and rotating keys
Ship a new version by uploading a new signed zip and opening a PR that bumps version, artifact, and updated_at.
Warning
Sign every update with the same key you used for the first release. The host pins your key on first install and rejects updates signed by a different one.
An update that requests new capabilities re-runs the consent flow on the user's machine. Adding capabilities is a visible event, not a silent one.
Rotating your key
If you must change your key, the index entry lists the old key in previous_pubkeys, and the artifact carries a second signature by the old key (<artifact>.minisig.prev). With that rotation proof the host re-pins silently.
Without the proof, the update is blocked for everyone who has your plugin installed, and they see a prominent warning naming both fingerprints. So guard your key, and if you rotate, provide the proof.
Tip
The exact previous_pubkeys field layout and the .minisig.prev second-signature flow are documented in Signing and the index format.
Pre-publish checklist
-
idis a unique lowercase dotted id (e.g.yourhandle.my-plugin), not underapp.streamnook. -
versionbumped (semver) since the last release -
[capabilities]lists only what the plugin actually uses -
networkhonestly reflects whether the plugin connects out -
tiermatches what the plugin does -
host_minset to the lowest StreamNook version you tested against - Plugin responds to
initializefast, answersping, handlesshutdown/exit(process kind) - Plugin degrades cleanly when a capability is denied or a credential is revoked
-
uibundle aliasesreact,react/jsx-runtime,framer-motionto the host shims - README leads with what it does and a screenshot
- Zip has
plugin.tomlat the root, signed with minisign, SHA-256 computed - Artifact and
.minisighosted at stable URLs you control - Index entry matches the manifest exactly
Related
Signing and the index
The minisign scheme, full index.json fields, key pinning and rotation in depth.
The manifest
Every plugin.toml field and the tier table that decides where you can list.
Official plugins
What makes a plugin official and how the curated index is reviewed.
Quickstart
Build and run a plugin locally before you package it for release.