Skip to content

Plugin wire protocol

Verified against v0.21.0 · sdk/abi/v1.json, internal/plugin/hostfn.go, internal/plugin/cron.go, internal/plugin/kv.go, internal/plugin/kinds.go, internal/plugin/settings.go, sdk/plugin-sdk/src/host.js

The plugin wire protocol is frozen at version 1. The schema lives in sdk/abi/v1.json. A guest talks to the host through Extism host functions; the host talks back by calling exported hooks. JSON envelopes (ok, error, data) wrap every host-function result.

Manifest manifest is 1 as well. That number is the package format, not a second protocol.

A guest can be written in any language with an official Extism PDK: Rust, JavaScript, Go (TinyGo), Haskell, AssemblyScript, C, Zig, and .NET.

Python is not a guest PDK — do not plan on it. The Go PDK targets TinyGo, so a guest cannot use goroutine-heavy or reflection-heavy parts of the standard library.

Grouping and dot-vote work as one key-value document per session. There is no list or prefix API on kv. Relational storage is not required for a ceremony plugin.

parley_kv_set today writes a whole value. A bounded write — compare-and-swap on kv_set, an expected field the host checks before replacing the document — is deliberately not closed by freezing v1. Concurrent guests racing on one session document need it. Adding that field is a compatible extension of protocol 1, not a v2.

parley_kv_get, parley_kv_set, parley_fetch, parley_secret_get, parley_log, parley_emit, parley_session_get, parley_session_patch, parley_job_enqueue, parley_settings_get. Each checks the grant inside the host function against the install record, not against the bundle.

parley_settings_get takes no input and returns the install’s non-secret settings as a JSON object, defaults applied. It needs no grant: the values are the plugin’s own admin-set configuration. A secret setting is read with parley_secret_get and its field name. It is a compatible extension of protocol 1, not a v2. The SDK wraps both as getSettings() and getSecret(name).

parley_job_enqueue takes kind, optional payload, and either delay_ms or a five-field cron (minute hour day-of-month month day-of-week, numbers only). A cron is converted to the next run_at at schedule time; the existing job claim still runs the work. cron is a compatible extension of protocol 1, not a v2. An invalid expression is refused with an error that names the cron, and no row is stored.

on_session_state, on_session_action, on_event, on_job. Event and job handlers must be idempotent: delivery is at-least-once.

on_session_action may answer {"refused":"<code>"} with invalid, forbidden, not-found or conflict to decline an action without failing. This is a compatible extension of protocol 1: a guest that never refuses is unchanged, and any output without a refused key is still an accepted action.

on_session_state and on_session_action inputs carry spaceKey: hex HMAC-SHA256 of the room’s space, keyed per install. It is stable for one install and space across restarts and replicas, differs between installs, and never carries the space id, slug or org. It is never sent to a panel frame. This is a compatible extension of protocol 1.

The request, response and input shapes of all of these are in the Plugin API reference.