ainb v2 plugins — overview
What a v2 subprocess plugin is, conceptually. New here? Read README.md first — it disambiguates from Claude Code plugins.
For the user CLI flow, jump to user-guide.md. To write one, authoring.md. For the wire contract, spec-v2.md.
What is a plugin?
A plugin is a self-contained capsule that adds a screen, CLI subcommand, sidebar entry, or statusline segment to ainb without recompiling the host. A v2 plugin is a native executable the host spawns as a child process; the two talk JSON-RPC 2.0 over framed stdio.
Plugins only see the host capabilities they declare in their manifest, and they cannot reach the network, filesystem, or subprocess launcher unless you grant those capabilities.
Architecture at a glance
The host (ainb-core) spawns each plugin as a child process and drives it over JSON-RPC 2.0 / Content-Length-framed stdio. Host→plugin methods: plugin/init (with the granted capabilities), plugin/render (host sends a Viewport, plugin returns a WireBuffer), plugin/handle_key, plugin/handle_event, plugin/cli_dispatch, plugin/shutdown. Plugin→host (reverse) calls: host/snapshot/publish + host/snapshot/subscribe (the event bus) and host/action/invoke. The ainb-plugin-runtime enforces capabilities — an ungranted host-fn call comes back as JSON-RPC -32001 (CAPABILITY_DENIED).
Two ways a plugin screen renders
- In-process
WireBuffer— the host owns the terminal; the plugin paints a sparse cell grid the host blits each frame. Integrated and themeable. This is howburndowndraws the Analytics dashboard. - Host-embedded foreign TTY — for an interactive program that has no machine-readable render (only its own TUI), the host suspends and hands the whole terminal to the external binary, resuming when it exits — the same mechanism ainb uses to attach to agent sessions. This is how
witropens its all-process browser (witr -i): there’s no JSON for witr’s live process list, so pressingwrunstmux new-session -A -d -s ainb-witr "witr -i"and attaches full-screen.
What a plugin can own
- A TUI screen — implement
Plugin::renderand paint aWireBufferper frame; the host blits it onto the terminal. - A CLI subcommand tree — claim a
cli_namespacesentry in your manifest; the host dispatchesainb <ns> ...invocations throughPlugin::cli_dispatch. - Snapshot topics (publish/subscribe) — push data on a topic via
HostClient::snapshot_publish; subscribers (other plugins or the host) getplugin/handle_eventdeliveries. - A statusline segment — own a slice of the persistent status bar.
- Its own state — write under
~/.agents-in-a-box/plugins/<name>/(gated bywrite_plugin_data). - Host actions — invoke host-owned operations via
host/action/invoke(gated by capability).
Reference plugins
Four plugins ship in-tree as the canonical examples:
burndown— screen-owner reference. Owns the Analytics screen (renders a ratatui dashboard into aWireBuffereach frame) and theainb usageCLI tree; subscribes tosessions.usage_datafromsession-reader.session-reader— pure-publisher reference. No screen. Scans~/.claude/projects/**,~/.codex/sessions/**(and more) and chunk-publishes usage snapshots onsessions.usage_dataforburndownto render.witr— subprocess-wrapper reference. Theainb witr <target>CLI +/witrslash runwitr --json <target>and parse the ancestry JSON; its screen is a host-embedded foreign TTY (whands the terminal towitr -i— see the two render paths). Declaresspawn_subprocess+event_bus.learnings— read-only-browser reference. A path-scoped fs reader +qmdsubprocess: it browses, searches and graphs thereflectknowledge base under~/.learnings(Markdown notes +.entities.yamlsidecars + the nano-graphrag cache + the qmd index). Opens withm//recall//memory; its Graph tab renders an entity neighbourhood, community clusters, and a deterministic radial ego local-graph. Declaresread_paths+spawn_subprocess+event_bus.abtop— subprocess-wrapper plugin forabtop(top-for-agents). Theainb abtopCLI execsabtop --once; pressingtattaches the terminal full-screen toabtop --exit-on-jump(same foreign-TTY hand-off pattern as witr). First launch shows a one-time rate-limit--setupconsent dialog. Declaresspawn_subprocessonly.
Each links to its own page with a /fireworks-tech-graph diagram of how it works.
Not a plugin: the Inbox screen +
ainb-notifyddaemon are sometimes mistaken for an in-tree plugin (the crate is namedainb-plugin-notifyd). They are host code compiled intoainb-core— no manifest, no JSON-RPC, no capability gate. They’re documented under TUI → Inbox & notifications, not here.

The burndown plugin rendering the full analytics dashboard against real ~/.claude/projects data.
Where plugins live on disk
The host discovers plugins from a flat staging directory:
dist/plugins/├── abtop/│ ├── abtop (native executable, ad-hoc signed on macOS)│ └── manifest.toml├── burndown/│ ├── burndown (native executable, ad-hoc signed on macOS)│ └── manifest.toml├── session-reader/│ ├── session-reader│ └── manifest.toml└── witr/ ├── witr └── manifest.toml(notifyd is not here — it’s a daemon compiled into ainb-core, not a staged subprocess plugin; see TUI → Inbox & notifications.)
That layout is what just stage-plugins produces from in-tree crates, and what the host walks on startup. The AINB_PLUGIN_ROOT env var overrides it (defaults to <workspace-root>/dist/plugins).
Plugin-writable state lives under ~/.agents-in-a-box/plugins/<name>/ (override with AINB_HOME), gated by the write_plugin_data capability.
Capability model (summary)
Every plugin declares the host capabilities it needs in its manifest.toml:
[capabilities]read_sessions = true # ~/.agents-in-a-box/sessions/**read_claude_logs = true # ~/.claude/projects/**/*.jsonlread_codex_logs = false # ~/.codex/sessions/**/*.jsonlwrite_plugin_data = true # ~/.agents-in-a-box/plugins/<name>/ writableevent_bus = true # publish/subscribe across pluginsspawn_subprocess = false # exec child processesnetwork = [] # bool or hostname allow-listDefault for every flag is deny (false / []). The runtime rejects host-fn calls against a capability the manifest doesn’t grant with JSON-RPC error code -32001 (CAPABILITY_DENIED).
Full semantics: spec-v2.md §1 and spec-v2.md §9.
Versus the deprecated v1 wasm contract
v1 used wasm32-wasip1 cdylibs in a wasmi host runtime with linker-omitted host-fn imports for capability gating. v2 dropped wasm entirely — a normal cargo build binary, native code, OS-process boundary. The wasm sandbox added implementation cost without buying any safety property the OS process boundary doesn’t already provide for ainb’s threat model.
Next steps
- End user? user-guide.md — every
ainb plugincommand. - Building a plugin? authoring.md — Rust SDK, scaffolding, debugging.
- Implementing a host? spec-v2.md — the wire contract.