Quickstart
Reference plugins ship in-repo under examples/plugins/. They are the artifacts these
docs are written against. Start from deno-reference — the canonical sandboxed
package, the shape you actually install.
1. The canonical plugin — deno-reference (a sandboxed package)
Section titled “1. The canonical plugin — deno-reference (a sandboxed package)”A sandboxed package is a directory with a manifest.json + a main.ts entry module
(plus optional ui/). The manifest declares the capabilities the jail may use;
main.ts exports the lifecycle handlers Lowkey runs in the Deno sandbox.
{ "id": "deno-reference", "version": "0.1.0", "name": "Deno Reference Plugin", "scopes": ["cards", "focus", "side-panel"], "surface": { "slot": "focus", "url": "http://127.0.0.1:4317/focus" }, "capabilities": [ { "id": "net.egress", "domains": ["api.github.com"] }, { "id": "fs.project", "mode": "read" }, { "id": "events.turn", "events": ["turn.completed"] }, { "id": "mount.focus" }, { "id": "mount.side-panel" }, { "id": "cards.emit" } ]}// main.ts — runs in the Deno jail with exactly the granted capabilitiesimport { emitCard, type LowkeyPluginTurnCompleteContext } from '@lowkey/plugin-sdk';
export async function onTurnComplete(ctx: LowkeyPluginTurnCompleteContext): Promise<void> { // `fetch` works only for a granted net.egress domain; Deno.readTextFile only // under fs.project. Undeclared access is denied by the sandbox. await emitCard({ sessionId: ctx.event.sessionId, card: { type: 'status', level: 'success', text: `Turn ${ctx.event.turnId} seen.` }, });}Install (self-serve, admin)
Section titled “Install (self-serve, admin)”An admin installs the package through the web — no host access required:
- Plugins settings → Install uploads the package files (
manifest.json,main.ts,ui/*). This callsPOST /api/plugins/install, which validates the package and returns the declared capabilities + a content hash. - The UI shows the capabilities for consent; approving calls
POST /api/plugins/install/confirm, which records the grant and stores the package under<project>/.lowkey/plugins/<id>/. - Enable it for the project (the per-project toggle). Enabling mints the installation’s scoped token used by any declarative seams.
The installer requires the main.ts entry — a manifest with no code cannot be
installed this way. See Sandboxed packages for the full
capability vocabulary and the sandbox enforcement.
2. Declarative-seam examples (operator sideload)
Section titled “2. Declarative-seam examples (operator sideload)”hello-world and driven-surface-reference are manifest.json + service.mjs with
no main.ts. They exercise the declarative seams in isolation, but
because they ship no entry module they are not web-installable — an operator
sideloads them by dropping the directory into a plugins root (~/.lowkey/plugins/<id>/
or <project>/.lowkey/plugins/<id>/; the directory name must equal the id) and
enabling it. Use them to learn the seams; use a sandboxed package to ship.
hello-world — the smallest declarative plugin, one scope, one card:
{ "id": "hello-world", "version": "0.1.0", "name": "Hello World", "scopes": ["cards"] }Its service.mjs POSTs to the cards seam with the installation’s
scoped token (LOWKEY_PLUGIN_TOKEN). driven-surface-reference adds three more
blocks — surface (iframe), mcp (agent-facing tools), webhook (inbound trigger) —
all wired straight from manifest.json, no core code. See the
per-seam reference for each.
3. Validate before you install
Section titled “3. Validate before you install”Every manifest validates through @lowkey/shared’s validateManifest() and the
published JSON Schema — parallel sources of truth. Validate
before installing; an invalid manifest is rejected whole, never partially registered.
Where to go next
Section titled “Where to go next”- The jail, the capability vocabulary, and install-with-consent → Sandboxed packages.
- All declarative seams, request/response shapes, and limits → Per-seam reference.
- A personality / external-runtime agent → agent contribution
- What stays stable as Lowkey evolves → Compatibility policy.