Trust Model
Plugins run inside the SSG dashboard — same origin as the daemon, same access
to /api/json/*, same window. The trust model has to make that safe.
What gets verified at install
When a user runs ssg plugins install <id> (or clicks Install in the dashboard
marketplace), the daemon performs four independent checks before any plugin
code is extracted to disk:
| # | Check | Failure mode |
|---|---|---|
| 1 | manifest_url is on an allow-listed host (raw.githubusercontent.com or hub.sigmashake.com) | Refuse install. |
| 2 | SHA-256 of the downloaded .tar.gz ≡ manifest.artifact_sha256 | Refuse install. |
| 3 | Ed25519 signature over artifact_sha256 verifies against manifest.signing_fingerprint | Refuse install. |
| 4 | manifest.signing_fingerprint ≡ the hub-recorded author_public_key for the pack | Refuse install. |
Check #2 ensures the tarball wasn't swapped on the CDN. Check #3 ensures only the key holder could have produced this artifact. Check #4 anchors the key to a specific GitHub identity in the hub — so even if attacker-controlled GitHub infra served a different artifact, the signing key cross-check would fail.
The four checks are independent: any one of them passing doesn't help an attacker bypass the others. All four must pass.
Why GitHub releases
Plugin artifacts live on GitHub release assets because:
- GitHub gives every publisher a stable, content-addressable URL that doesn't require us to operate object storage.
- GitHub's tag-based versioning matches plugin semver naturally.
- Publishers already have an identity (GitHub login) the hub uses for attribution.
The hub does not mirror plugin artifacts. If a publisher takes down a release, that version becomes uninstallable. This is intentional — plugin authors retain the right to unpublish.
Publisher keys
Every plugin author signs with one Ed25519 keypair, generated and stored locally:
~/.sigmashake/keys/signing.key # private key (mode 0600)
~/.sigmashake/keys/signing.pub # public key (SPKI)
The private key never leaves the machine that ran ssg keys generate. To
publish from a second machine, copy ~/.sigmashake/keys/ to it manually — do
not re-run ssg keys generate on the second machine (that produces a new
key, which won't match the one your hub account has registered).
The corresponding public key is uploaded to the hub via ssg keys register.
The hub stores it under your GitHub identity (users.public_key column). At
plugin install time, the daemon refuses to install if the manifest's claimed
signing_fingerprint doesn't match the public key on file for the plugin's
listed author.
Key rotation
If your signing key is compromised:
- Stop publishing from the compromised machine immediately.
- Generate a new key on a clean machine, then re-register it with the hub
(
ssg keys generatefollowed byssg keys register). - Open a support ticket (
ssg support) so the old key can be revoked from the hub. The hub keeps an audit log of every key rotation. - Re-publish each of your plugins under the new key. Users will see signature
mismatches on
ssg plugins installuntil they uninstall + reinstall the updated artifact.
What plugins can and cannot do
Plugins run inside the SSG dashboard origin (http://127.0.0.1:5599). They:
- ✅ Can call
/api/json/*endpoints with the dashboard token - ✅ Can fetch from any remote URL (CORS rules apply)
- ✅ Can persist client-side state via
localStorage - ❌ Cannot register new server-side routes (v1)
- ❌ Cannot read or write outside their own
/plugins/<id>/namespace - ❌ Cannot access the user's filesystem directly
- ❌ Cannot escalate to root or modify SSG's signing keys
That said, the dashboard origin is trusted by the daemon. A malicious plugin
could in principle make authed requests to /api/json/eval, /audit, or any
other endpoint. That's why the verification chain matters: a plugin is only as
trustworthy as its signing key.
The threat we don't defend against
We don't sandbox plugins from each other or from the host dashboard. That means: don't install a plugin you don't trust the publisher of. Treat plugins the way you'd treat a VS Code extension or a Chrome extension — vet the author, check their reputation, prefer plugins from publishers you recognize.
Future versions will add an iframe sandbox + capability manifest so plugins can declare and prompt for the permissions they need. Until then, the publisher's signing key + GitHub identity is what you're trusting.
Audit trail
Every plugin install/uninstall is recorded in ~/.sigmashake/audit.db with:
- Plugin id + version
signing_fingerprint(so you can later verify which key was trusted at install time)- Source pack id (hub) or local path (sideload)
- Timestamp
You can review this from the dashboard's Activity tab, or with
ssg audit search --decision=plugin_install.
See also
- Manifest reference — exact fields used at verification
- Publishing — the publish flow that produces signed artifacts