Trusted realm bundles
Marketplace plugins run sandboxed by default (cross-origin iframe,
capability bridge). The realm tier is for plugins the platform has
reviewed and signed: they load into the app realm and use every
registry first-party plugins use. This guide is the operator + publisher
view; the architecture is docs/PLUGIN_LOADING.md in the repo.
Build against the host ABI
Use the template's build config: your react, react/jsx-runtime, and
@aglyn/aglyn imports compile to lookups on
globalThis.__AGLYN_PLUGIN_HOST__ — the app injects its own singletons
there, which is what keeps your components on the app's React and your
registrations in the app's registries. Export register(host) (client
surfaces) and, only if you truly need server handlers, registerApi().
Declare hostAbi in your manifest; a bundle built for another generation
never loads.
The chain that runs before a byte executes
- Content pin — the install doc's sha256 must match the fetched bytes (immutable content-addressed artifacts).
- Platform signature — Ed25519 over the sha, granted by super staff after review; verification fails closed.
- Kill switch — a
revocations/{listingId}doc beats everything. - ABI check — declared
hostAbimust equal the host's generation.
Granting trust (staff)
Admin → Plugin reviews has a Listed plugins — realm trust section: every listed and verified plugin with its recent versions, each showing a Realm-trusted or Sandboxed chip and a Latest marker, with per-version Grant realm trust and Revoke realm trust buttons. Trust is granted to a specific reviewed version, not to the listing — a newer version stays sandboxed until it is reviewed and granted in turn.
Both actions are super-staff only and adminAudit'd. The equivalent API is
POST /api/admin/sign-plugin { listingId, version }, with { action: 'revoke' } to
revoke; a hard-kill still needs a revocation doc. Signing requires
PLUGIN_TRUST_PRIVATE_KEY on the console deployment — generate the pair with
tools/scripts/generate-plugin-trust-key.mjs.
Where realm bundles load
- Console: before the shell renders, from the org's trusted installs.
- Published sites: post-hydration (additive — first paint never waits on a marketplace CDN).
- Server (rare): only on deployments with
PLUGIN_REMOTE_SERVER=enabledplus a per-deploylistingId@versionallowlist; every load is audited.
Key rotation
generate-plugin-trust-key.mjs → resign-realm-plugins.mjs (re-signs
every granted version with the new key, --dry-run first) → deploy the
new public keys → retire the old private key. Re-sign before the
public-key swap so nothing stops loading mid-rotation.
Troubleshooting
- Loads in dev loop, not from the marketplace: dev loop skips verification. Check the browser console for the loader's reason — usually "missing signature" (not signed yet) or "sha256 mismatch".
__AGLYN_PLUGIN_HOST__ is not set: your bundle executed outside a host surface — the console gate and site effect set the ABI before loading; don't import the bundle yourself.- Server bundle didn't register: the dispatcher logs which chain link
failed (
not realm-trusted,built for host ABI N, fetch, signature) and skips — check the deployment logs and the allowlist entry.