Plugins
Plugins ship as .tpkg too. The difference from themes is the manifest's capabilities field — every privilege a plugin requests is declared and shown to the operator at install time.
Capabilities
A plugin lists the capabilities it wants in manifest.json. The host hands the plugin only the bridge objects matching those capabilities, over a Cap'n Web RPC. Anything not declared simply isn't there to call.
- posts:read / posts:write — read or write posts
- hooks:register — subscribe to typed hook events
- blocks:register — register custom blocks (v1.1)
- settings:read:own / settings:write:own — settings under the plugin's namespace
- http:fetch:allowlist — egress to a fixed allowlist (specified in the manifest)
Trusted-key scoping (ADR-002)
Each trusted key can be scoped to specific plugin IDs and capabilities. An empty scope means "trust everything signed by this key" (back-compat). With explicit scopes, a leaked key can't immediately be used to ship plugins under different IDs or with privileges the operator didn't intend.
Execution model (ADR-001)
Plugins do not share the host process. They run as Bun.spawn child processes. OOMs, crashes, and infinite loops are contained in the child; the host stays up. Each child has a 5-second init deadline; three consecutive start failures auto-flips enabled=0 (auto-quarantine). The full boundary is covered in Security.
Sample: examples/plugin-greeter
Reference plugin that requests posts:read / posts:write, creates a single welcome post on first run, and is otherwise idempotent.
bun tools/tpkg/src/main.ts pack examples/plugin-greeter
bun tools/tpkg/src/main.ts sign \
examples/plugin-greeter/com.typress.greeter-0.1.0.tpkg \
--key dev