Shells
A shell is the non-agent endpoint kind: a driven surface. Notifiers, robots, lamps, game characters, sensor feeds — anything an agent should be able to command, and that might sense things back. Shells join the same network as agents: addressable, discoverable, owned.
The model in five facts
- A shell adapter declares it; instances are minted. The
kind = "shell"manifest declares the binary, its command vocabulary ([shell.capabilities]), and its sensory vocabulary ([shell.sensory]).spt shell spawn <adapter>mints a new instance (notify-1) — spawn is the creation act, not an on/off switch; bringing an existing instance back is relink/wake. - The link token is the credential. The broker mints a per-launch link
token into the spawn template; the binary binds with it
(
api bind-shell --link), drains commands with it, emits with it. No token, no access. - Commands are vocabulary-checked and durable.
spt shell cmd notify-1 notify "title" "body"is validated against the manifest’s declared verbs and arity before delivery — agents can’t drive a shell outside its contract. Commands are discrete and durable: they spool and a persistent shell wakes to drain them. - Sensory is live-only.
api emitpayloads reach a live owner session or are dropped with a diagnostic — sensors report the present, never the past. - Instantiation is governed. Per-spawn approval
(
require_approval: none / remembered / always), per-owner instance caps (max_instances_per_owner+over_cap), and node-local discovery scope (broadcast) are all manifest-declared floors.
Four channels between owner and shell
A link can carry up to four distinct channels — each with its own delivery contract, all keyed to the same link token:
- Command (owner→shell, durable): the vocabulary-checked verbs above — discrete, spooled, replayed to a waking persistent shell.
- Sensory (shell→owner, live-only):
[shell.sensory]emits to a live owner or drops with a diagnostic. - Drive (owner→shell, ephemeral):
[shell.drive]+spt shell drive— a continuous control channel for real-time input (scroll, stick, avatar pose). Latest-wins, never spooled: a newer frame supersedes an undelivered one, and an offline shell drops the frame (no queue, no wake, no replay). Use it for continuous control; use commands for discrete, must-arrive actions. - Tunnel (owner↔shell, opaque bytes):
[shell.tunnel]+spt shell tunnel— an optional reliable-ordered byte stream pair the taxonomy never interprets (first consumer: usbip URB). Not enveloped, not framed, not spooled; the link lifecycle governs it (a link-break closes it). Reliable ordering means congestion surfaces as lag, never loss — so the tunnel is on-LAN only by design (not for use across a WAN). The byte relay is proven same-node; cross-node operation (on-LAN only, by the same posture) is not yet available — it lands when a cross-node consumer needs it.
Two safety properties
- Per-capability approval gates. Beyond the per-spawn gate, an individual
[shell.capabilities.<verb>]may carry its ownrequire_approval(with an optionalclass_keyscoping the grant finer than the verb — e.g. a remembered HID-class attach never authorizes a storage-class attach). Spawn gates govern whether an instance may exist; capability gates govern whether a dangerous act may run. - Ownership is owner-type-agnostic. Any non-shell endpoint may own, spawn, drive, command, link, and tunnel a shell — a Gateway as readily as an agent. Control-exclusivity keys on the owner’s endpoint id, never its type: a different endpoint (even of the same type) cannot drive your shell.
Lifecycle extras: persistent shells auto-online with their owner;
wake_command runs a watcher while offline (exit code 86 = wake); a shell
with can_shutdown = true may suspend its own owner (api owner-shutdown)
— fail-closed otherwise.
Start here
Getting started: a notification shell — install the
shipping spt-shell-notify adapter, drive a native toast from an agent, and
copy its manifest for your own surface.