Plugin SDK
The Praxrr Plugin SDK lets you package an observe-only extension as a
directory containing a praxrr.plugin.json manifest and a WebAssembly
module. This page is the map for the SDK: what a plugin is, what the host
proves today, and where the contract is (and is not) stable.
What a plugin is
Section titled “What a plugin is”A plugin is a directory placed directly under PLUGINS_DIR. Each immediate
subdirectory holds exactly one praxrr.plugin.json manifest plus the .wasm
module named by the manifest’s entry field. The manifest declares which
extension points the plugin subscribes to and which capabilities it needs.
Plugins are observe-only and deny-by-construction: the capability union is closed and read-only, so there is no capability id for credentials, auth or sessions, secrets, network or HTTP, filesystem, database, or environment access, nor any write or mutate action. Those grants are structurally unrepresentable — you cannot even name them in the manifest.
A minimal, valid manifest for the wired preview point looks like this:
{ "apiVersion": "1", "id": "com.example.observer", "name": "Example Observer", "version": "1.0.0", "runtime": "wasm", "entry": "plugin.wasm", "extensionPoints": ["sync.previewComputed.observe"], "capabilities": ["read:sync-preview"]}See the Manifest Reference for every field, allowed value, and error code.
What works today vs. what is pending
Section titled “What works today vs. what is pending”Phase-1 reaches only the front of the lifecycle: a manifest is discovered,
validated, and then either registered or rejected. The registry only ever
stores plugins in the registered state; the runtime states (activated,
failed, unloaded) exist only for the future runtime. The table below tracks
the forward, happy-path progression — the pending “Observed” stage is what the
Phase-2 runtime adds.
| Stage | Status |
|---|---|
| Discovered | Works today (when plugins are enabled in the UI) |
| Validated | Works today (when plugins are enabled in the UI) |
| Registered | Works today (when plugins are enabled in the UI) |
| Observed (executed) | Pending Phase-2 (#262) |
The Phase-2 runtime is a documented, reasoned deferral — not an omission and
not a “never”. The evaluated Extism JavaScript SDK on Deno is a no-go for
Praxrr’s isolation requirements: it offers no active cancellation, no
fuel/instruction limit, and memory.maxPages is not a total-guest-memory cap
(a worker timeout is not fuel). Execution stays deferred until a compliant
backend lands.
Feature flag and directory
Section titled “Feature flag and directory”The whole subsystem is off unless you opt in.
- UI Enable plugins — master switch on Apps → Plugins / Settings → Plugins; default off.
When off, the host is a hard no-op:
host.initializereturns immediately and never statsPLUGINS_DIR. PLUGINS_DIR— the directory scanned forpraxrr.plugin.jsonmanifests; default<APP_BASE_PATH>/plugins. It is never auto-created. When plugins are enabled but the directory is missing, the host warns and degrades to an empty registry rather than failing boot.
Malformed or invalid manifests are skipped and logged; they never abort boot. The registry is rebuilt from a fresh scan each boot (no database), so an enable, disable, rollback, or upgrade cannot resurrect a plugin validated under an incompatible contract version.
The stable public surface
Section titled “The stable public surface”Some parts of the SDK are stable contract you can build against now; others are explicitly provisional.
Stable in Phase-1:
- The manifest contract — the exact allowed keys, value rules, and error codes accepted by the hand-written validator.
- The two wired observe points,
config.profileCompiled.observeandsync.previewComputed.observe. - The capabilities those points consume and the allow-list snapshot shapes they would project (once the Phase-2 runtime lands).
Not yet stable:
- The seven declared-but-unwired extension points and any
capabilities/snapshots tied to them. Dispatching an unwired point throws
PluginPointNotWiredError. - The host-to-guest ABI. The example’s guest export name and its argument/return encoding are provisional because no runtime ABI is finalized. The provable win — discover, validate, and register of the JSON manifest — is independent of that ABI.
Where to go next
Section titled “Where to go next”Start with the worked example, then drill into each part of the contract:
- Build and Install the Example — build the
shipped
sync-preview-observerwith TinyGo and install it locally. - Manifest Reference — the 11 allowed keys, value rules, and every rejection code.
- Capabilities — the closed, observe-only, credential-free capability union.
- Extension Points — all nine points, their kinds, and which two are wired.
- Observe Snapshots — the allow-list projection and the redacted shapes plugins would receive.
- Lifecycle and Registry — discovery, validation, registration, and graceful degradation.
- API Versioning — single-version support and the registry namespace.
These pages must not contradict the authoritative contract mirror, the internal architecture note at docs/architecture/plugins.md. For broader context, see the architecture hub, the configuration guide, and the WASM Plugin System status section of the ROADMAP.