Concept
A plugin is a sandboxed package
Section titled “A plugin is a sandboxed package”A Lowkey plugin is a package installed into a project:
- A manifest —
manifest.json: id, version, the capabilities it requests, and one block per declarative seam it uses. Static and machine-validated. - An entry module —
main.ts(ormain.js) exporting lifecycle handlers. Lowkey runs it on the host inside a Deno sandbox — a locked-down child process (a “jail”), never the daemon’s own process — with only the capabilities an admin consented to at install. - Capabilities — the closed set of host-mediated doors the jail may use
(
net.egress,fs.project,session.fork,cards.emit, …), each granted explicitly. Undeclared access is denied by the sandbox, not by good behavior.
This is the one delivery method. The web installer — the only self-serve way a
plugin reaches a Lowkey instance — requires a main.ts entry module and walks
the admin through capability consent. There is no self-serve way to register a
plugin that is only a manifest. See Sandboxed packages.
Author-hosted services plug in through a shim
Section titled “Author-hosted services plug in through a shim”Lowkey does not host your app, your model, or your heavy compute. If your plugin is
backed by a service you run elsewhere — a web app, an API, a GPU job, a non-JS
stack — the installed plugin is a thin main.ts shim that reaches your
service over the network with the net.egress capability (and, if it has a UI,
renders it in a mount point via a surface). The jail stays light; your
infrastructure does the heavy lifting. “Author-hosted” is this shim pattern, not a
second kind of plugin.
The governing rule
Section titled “The governing rule”Expose the outcome, seal the mechanism.
Every capability hands the jail a result and hides the machinery that produced it. You get a scoped, authenticated egress — not the credential. You get a forked observer turn — not the provider resume id or session path. You get a closed card schema — not a DOM. You get the resulting system-prompt mode — not the prompt-assembly code.
Consequences for plugin authors:
- Adopt an external protocol where one exists. “I want my agent to do X” is almost always write an MCP server and register it — a borrowed, externally maintained protocol.
- Typed capability where Lowkey adds value; opaque escape hatch otherwise. Cards
are a typed, enumerated schema; a sandboxed iframe
surfaceandnet.egressare the escape hatches for what Lowkey cannot model. - The frozen set grows only under demonstrated demand. Once a capability is observable, it is depended on (Hyrum’s Law); see Compatibility.
Declarative seams
Section titled “Declarative seams”Besides running code in the jail, the manifest can wire a set of declarative seams — an integration Lowkey performs from the manifest, without hosting your logic:
- a
surfaceiframe (your served URL in a host-owned mount point), - an
mcpserver (a command Lowkey spawns as a host subprocess at agent launch), - a
webhookinbound trigger (a POST that dispatches an agent turn), - an
agentcontribution (a roster agent — on a built-in runtime, orruntime: external:<id>where Lowkey POSTs each turn to your endpoint), cardsandsession-readHTTP routes.
A sandboxed package may declare these alongside its capabilities — the reference
package renders its UI through a surface. But a plugin that uses only
declarative seams and ships no main.ts cannot be self-installed: it is
operator sideload only — a directory dropped into a plugins root by someone with
host filesystem access (Seams, Quickstart). Note
the mcp seam spawns your command on the host unsandboxed, which is why a
manifest-only MCP plugin is an operator decision, not a self-serve one.
Contribution archetypes
Section titled “Contribution archetypes”Validated shapes. Each is realized as a sandboxed package (the shim + capabilities), or — for the pure-declarative variants — as an operator-sideloaded manifest.
Driven surface
Section titled “Driven surface”An external app the agent drives and that renders in a mount point. As a package: a
main.ts shim + a surface + an mcp agent face + a webhook trigger + cards.
→ surface, MCP, webhook, cards.
Inbound trigger / callback
Section titled “Inbound trigger / callback”An external service dispatches a turn (webhook) and receives the output over a
scoped callback; the conversation may live entirely outside Lowkey. Lowkey never
calls your service except through the callback you supply per request.
→ webhook source, scoped callback, turn-lifecycle event.
Pipeline / agent contributor
Section titled “Pipeline / agent contributor”The plugin is (or reshapes) an agent — an agent contribution with its own
systemPrompt, platformPromptMode, and translator: none (a personality agent).
It renders as an ordinary roster agent. Two tiers:
- Tier A — personality-as-profile. A roster agent on a built-in runtime with a
custom prompt,
platformPromptMode: minimal,translator: none. No service code. - Tier B — bring-your-own-agent.
runtime: external:<id>. Lowkey proxies each turn to your endpoint over the versioned protocol; your service owns the model, prompt, and voice.
→ agent contribution, external runtime.
Introspection
Section titled “Introspection”A package that reaches inward: an always-on side-panel over the session’s own
corpus, recomputed on each turn. It reads the session’s own transcript + lineage
(session.read / session-read), never platform internals.
→ side-panel,
session-read,
turn-lifecycle event.
What stays sealed
Section titled “What stays sealed”Plugins get capabilities, events, scoped APIs, and tokens — never these:
- the credential store (the
tokencapability injects auth at the network boundary; the jail never holds a raw secret); - provider resume ids, absolute session paths, and the session JSONL format
(
session.read/session.forkreturn outcomes, resolved host-side); - the prompt-assembly mechanism (you get the resulting mode, not how it is built);
- Lowkey’s internal
AgentEventtype (wrapped by the external protocol); - run supervision, worktree isolation, and the auth stack;
- in-process code execution in the daemon — the seal that makes every exposure
safe. Your
main.tsruns on the host but out of process, in a Deno jail; it never runs inside the daemon.