Developer / Extensions
Build a plugin that extends a Lowkey instance — a focus surface an agent drives, an inbound trigger that dispatches a turn, a personality agent, an MCP toolset, a side-panel that reads the session — without forking the platform or running code inside its process.
This section is the integration contract. It is written so that a developer — or their agent — can ship a working plugin from these pages and the published JSON Schema alone, without reading Lowkey’s source.
The delivery method: a sandboxed package
Section titled “The delivery method: a sandboxed package”A plugin is a sandboxed package: a manifest.json + a main.ts entry module
whose handlers Lowkey runs on the host inside a Deno sandbox (a locked-down
child process — a “jail”, never the daemon’s own process), granted only the
capabilities an admin consents to at install. The web
installer is the only self-serve way in, and it requires the main.ts entry —
there is no self-serve path for a manifest with no code.
- Author-hosted services plug in through a shim. Lowkey does not host your app or
heavy compute; the installed plugin is a thin
main.tsthat reaches your service via thenet.egresscapability (and renders your UI in asurface, if any). - Declarative seams (
surface,mcp,webhook,agent,cards,session-read) wire an integration straight from the manifest. A package may declare them alongside its capabilities. A plugin using only these, with nomain.ts, is operator sideload only (a directory dropped on the host by someone with filesystem access) — not a self-serve delivery method.
The invariant across all of it: no third-party code runs inside the daemon process. The jail runs on the host but out of process.
Audience. Third-party developers and their agents. Reference voice: dense and factual. For end-user and operator questions, see the rest of the manual.
Read in order
Section titled “Read in order”| Page | What it covers |
|---|---|
| Concept | What a plugin is, the contribution archetypes, the governing rule. |
| Manifest reference | Every field, the closed scope set, section↔scope coupling, discovery. |
| Per-seam reference | Mount points, cards, MCP, webhook, callback, turn event, agent, session-read. |
| External turn protocol | The versioned protocol for runtime: external:<id> agents. |
| Sandboxed packages | Host-run plugins: the Deno sandbox, the capability vocabulary, install-with-consent. |
| Trust & scopes | Scoped tokens, the scope taxonomy, consent, first-party-only seams. |
| Quickstart | The reference plugins, end to end. |
| Compatibility policy | The frozen vocabulary — what stays stable. |
The shape of a plugin
Section titled “The shape of a plugin”A plugin is a directory containing a manifest.json and a main.ts entry
module (plus optional ui/ assets). The manifest declares an id, a version, the
capabilities it requests, and one block per declarative seam it uses. On
install Lowkey validates the manifest, records the consented capabilities + a content
hash, and runs the entry in a Deno jail; declared seams are also wired into its
existing extension points and mint a per-installation scoped token.
{ "id": "my-plugin", "version": "0.1.0", "name": "My Plugin", "capabilities": [ { "id": "events.turn", "events": ["turn.completed"] }, { "id": "cards.emit" } ]}// main.ts — runs in the Deno jailimport { emitCard } from '@lowkey/plugin-sdk';export async function onTurnComplete(ctx) { await emitCard({ sessionId: ctx.event.sessionId, card: { type: 'status', level: 'info', text: 'Turn seen.' } });}See Sandboxed packages for the capability vocabulary and
the install flow, and the Quickstart for a full worked package. A
manifest with no main.ts (only declarative seams) is valid but installable only
by operator sideload, not through the web.
The schemas
Section titled “The schemas”Two published JSON Schemas (draft 2020-12) are the machine-readable contract:
| Schema | $id / served path |
In-repo source |
|---|---|---|
| Manifest | …/plugins/manifest.schema.json (/schemas/plugins/manifest.schema.json) |
packages/shared/src/plugins/manifest.schema.json |
| External turn protocol | …/plugins/external-turn-protocol.schema.json (/schemas/plugins/external-turn-protocol.schema.json) |
packages/shared/src/plugins/external-turn-protocol.schema.json |
The manifest schema and Lowkey’s TypeScript validator are parallel sources of
truth — either rejects an invalid manifest with the same coupling rules. Validate
against the schema before you enable. The protocol schema is the stability boundary
for external runtimes. Both are also indexed for agents
in llms.txt.