Skip to content

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.

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.ts that reaches your service via the net.egress capability (and renders your UI in a surface, 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 no main.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.

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.

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.

manifest.json
{
"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 jail
import { 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.

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.