The system, end to end
The crates
The wire types — every JSON-RPC request, response, and payload that crosses the stdio boundary is defined here.
The Rust SDK that plugins build on. Implement its Plugin trait and you have a conformant plugin.
The host side. Discovers plugins, spawns them, routes JSON-RPC in both directions, and enforces the capability gate.
The conformance test suite — 14 axes a plugin (and the runtime) must satisfy to be considered correct.
How a frame is drawn
The methods that cross the wire
plugin/init carries the granted_capabilities the manifest declared. plugin/render sends the plugin a Viewport and gets back a WireBuffer — a sparse Vec<(Coord, Cell)> cell grid the host blits onto the terminal. plugin/handle_key forwards a normalized KeyEvent; plugin/handle_event delivers snapshots; plugin/cli_dispatch routes ainb <namespace> <argv> to the plugin and pipes back stdout / stderr / exit; plugin/shutdown ends the session.
A plugin calls back into the host with host/snapshot/publish and host/snapshot/subscribe — together these form the event bus — and host/action/invoke to trigger host-side actions.
Capabilities are declared in manifest.toml and enforced by the runtime. A host-fn call the manifest didn't grant returns JSON-RPC error -32001 (CAPABILITY_DENIED). The set is read_sessions, read_claude_logs, read_codex_logs, write_plugin_data, event_bus, spawn_subprocess, and network. Deny-by-default — if it isn't granted, it's blocked.
Three in-tree reference plugins
Owns the Analytics screen and the ainb usage CLI. Renders a full ratatui dashboard into a WireBuffer each frame; the host blits it. Fed by snapshots from session-reader over the event bus.
No screen. Scans ~/.claude/projects and ~/.codex/sessions, then chunk-publishes usage snapshots on topic sessions.usage_data.
Declares spawn_subprocess and execs the external witr CLI. Its ainb witr <target> CLI and /witr slash run witr --json <target> and parse the ancestry JSON. But its interactive all-process browser is not a WireBuffer render — that browser only exists in witr's own bubbletea TUI (there's no JSON for the full process list), so pressing w does a host-level full-screen handoff: tmux new-session -A -d -s ainb-witr "witr -i", suspend ainb's TUI, tmux attach, and resume on quit.
The centerpiece: two render paths a plugin screen can take
In-process WireBuffer
The host owns the terminal. The plugin paints cells the host blits. Themeable, integrated, and rendered inside the ainb TUI.
Host-embedded foreign TTY
The host suspends and hands the whole terminal to an external interactive binary, resuming when it exits. Full fidelity to the foreign app — but not ainb-themed.
Authoring a plugin
Implement the SDK Plugin trait — render, handle_key, handle_event, cli_dispatch, on_init, on_shutdown. Declare your capabilities plus cli_namespaces / screens in manifest.toml. Build the binary and stage it at dist/plugins/<id>/<id>.
Pick the reference closest to what you're building: burndown is the screen-owner reference, session-reader the pure-publisher reference, and witr the subprocess-wrapper reference.