Getting Started
This guide walks you from zero to a published, installable plugin in under five minutes. By the end you'll have:
- A scaffolded plugin project on disk
- A signed
.tar.gzartifact - A GitHub release hosting it
- A hub listing other SSG users can install from
Prerequisites
| Requirement | Why |
|---|---|
ssg installed — see Getting Started (Pro subscription required) | The plugin CLI lives in ssg. |
GitHub CLI (gh auth login) | Plugin publish creates a repo + release. |
Ed25519 signing key (ssg keys generate) | All plugins are signed; the key is what ~/.sigmashake/keys/ already contains. |
Hub account with key registered (ssg keys register) | The hub cross-checks your signing key against your account. |
tar with -tzf and -tvzf support (GNU tar or bsdtar) | Local install checks the tarball before it extracts it. ssg refuses busybox tar. |
If you have already signed in with ssg auth login and gh auth login, you have everything
except possibly the registered signing key — run ssg keys register to be sure.
1. Scaffold
ssg plugins init my-cool-tool
cd my-cool-tool
You get a minimal project:
my-cool-tool/
├── plugin.toml
├── README.md
├── .gitignore
└── src/
├── index.html
├── app.js
└── style.css
Open plugin.toml and edit the metadata to taste:
id = "my-cool-tool"
version = "0.1.0"
name = "My Cool Tool"
description = "Surfaces blocked tool calls in a heatmap."
category = "observability"
entry = "index.html"
min_ssg_version = "0.29.0"
[[nav]]
label = "Block Heatmap"
path = "/plugins/my-cool-tool/"
section = "tools"
order = 100
Then edit src/index.html, src/app.js, and src/style.css to build whatever
you want. The template app.js sends its request with the scoped plugin
credential that the daemon injects into window.__SSG_PLUGIN_CONFIG__.token.
That credential is not the dashboard token (see the trust model).
A plugin can always read four endpoints: GET /api/health,
GET /api/state/counters, GET /api/json/rules-stats and GET /api/json/mode.
The current template fetches /api/json/status, which is not one of those four
and is not a grantable route. Change that path to /api/json/mode to get a
working call. Other routes need a capability grant from the operator.
2. Build
ssg plugins build
This:
- Validates
plugin.toml - Bundles
src/→dist/ - Tars + gzips
dist/intodist/my-cool-tool-0.1.0.tar.gz - Computes the SHA-256
- Signs the SHA-256 with your local Ed25519 key
- Writes the canonical
dist/plugin.jsonmanifest
You should see something like:
✓ Built my-cool-tool v0.1.0
Artifact: /Users/you/my-cool-tool/dist/my-cool-tool-0.1.0.tar.gz (8.2 KB)
Manifest: /Users/you/my-cool-tool/dist/plugin.json
SHA-256: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
Signed by: 302a300506032b6570032100…
Optional: a custom build step
If you use Vite, esbuild, Webpack, etc., add a [build] section to
plugin.toml:
[build]
command = "pnpm install --silent && pnpm build"
ssg plugins build will run that command before packaging. Whatever it writes
into dist/ becomes the plugin bundle. The entry field still applies —
dist/<entry> must exist or build fails.
3. Test locally
Install the build locally:
ssg plugins install --from dist/
--from accepts one of these paths:
- The project directory. The command uses its
dist/directory. - The
dist/directory. It must containplugin.jsonand<id>-<version>.tar.gz. - A
.tar.gzfile. The command readsplugin.jsonfrom the tarball root. If the root has noplugin.json, it reads theplugin.jsonbeside the tarball. Without either file, the install fails.
A directory with plugin.json and dist/<entry> also works.
A local install is unsigned. The CLI prints a warning each time. Use --force
to replace an installed plugin with the same id.
To serve dist/ during development, symlink it into the SSG plugins root so the
dashboard can serve it:
mkdir -p ~/.sigmashake/plugins/my-cool-tool
ln -sfn "$PWD/dist" ~/.sigmashake/plugins/my-cool-tool/dist
cp dist/plugin.json ~/.sigmashake/plugins/my-cool-tool/plugin.json
cat > ~/.sigmashake/plugins/my-cool-tool/.installed.json <<JSON
{
"id": "my-cool-tool",
"version": "0.1.0",
"installed_at": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
"source_pack_id": null,
"source_repo": null,
"content_hash": "local",
"signing_fingerprint": "$(ssg keys show)",
"enabled": true,
"category": "observability",
"author_github": null,
"homepage_url": null,
"repo_url": null
}
JSON
ssg serve # opens http://127.0.0.1:5599
Your plugin shows up at /plugins/my-cool-tool/ and in the sidebar.
4. Publish
When the plugin is ready to share:
ssg plugins publish
This:
- Re-runs the build to make sure your artifact and manifest are in sync
- Verifies you're authenticated with
gh - Creates
github.com/<you>/ssg-plugin-my-cool-tool(private by default — you can flip it public on GitHub afterwards) - Pushes your plugin source to that repo
- Creates a release
v0.1.0and uploads the tarball +plugin.jsonas release assets - Opens hub.sigmashake.com/submit-plugin in your browser with every field pre-filled
The submit form double-checks that the signing fingerprint on the form matches the Ed25519 key registered to your hub account, then writes the plugin into the hub's marketplace index.
5. Install from the hub
Once submitted, your plugin is immediately installable from any SSG dashboard:
ssg plugins install my-cool-tool
Or from the dashboard: go to Plugins → search my-cool-tool → click Install. The daemon verifies the signature against the hub's recorded fingerprint before extracting any code.
Updating
To ship a new version:
# 1. Bump version in plugin.toml
sed -i '' 's/^version *=.*/version = "0.2.0"/' plugin.toml
# 2. Rebuild and publish — `publish` is idempotent
ssg plugins publish
Users uninstall + reinstall to pick up the new version (auto-upgrade lands in a later release).
Next steps
- Manifest reference — the full
plugin.tomlschema - Trust model — how signatures work and what they prove
- Publishing — alternate publish paths (REST, GitHub Actions)
- Examples — annotated example plugins to crib from