jackin❯ Capsule: In-Container Control Plane
Status: Partially implemented — Phases 1–3 are shipped in full: jackin-capsule is the PID 1 in-container PTY multiplexer (built on jackin-term DamageGrid), tmux is fully removed from the derived image and the host CLI, and every terminal-fidelity item once tracked as "remaining" (resize, mouse, focus events, OSC/CSI passthrough, prefix-key input, git-context-driven title/branch bar) is live. Phase 4 (host daemon integration and Desktop Agent Hub bridge) remains open, alongside a smaller set of real control-channel and passthrough gaps below.
The canonical description of Capsule's current architecture and behaviour lives in:
- jackin❯ Capsule — architecture, process model, wire protocol, distribution, input routing.
- Multiplexer design rules — the passthrough contract and per-feature verification checklist.
- Capsule Terminal Model — why the VT emulator was replaced twice (hand-rolled →
vt100fork →jackin-term). jackin consoleandjackin hardline— operator-facing prefix keys, palette, tabs.- Research: Multiplexer rewrite design history — why the Phase 3 rewrite happened (the five defects in the first attempt), the Zellij and Herdr borrows, and the tmux/Ghostty compatibility rationale, kept for the design-history record now that this page is lean.
Shipped facts
crates/jackin-capsule/ is a standalone workspace member sharing wire types with the host CLI through jackin-protocol (crates/jackin-protocol/src/control.rs, attach.rs). The binary is compiled for linux/amd64 and linux/arm64, published to GitHub Releases pinned to the same version as the jackin CLI, and resolved by crates/jackin-image/src/capsule_binary.rs in priority order: JACKIN_CAPSULE_BIN override, cache hit, packaged Homebrew resource, then download.
As PID 1, the daemon reaps zombies, forwards SIGTERM/SIGINT to a clean exit, and persists across individual session crashes — it no longer exits when all sessions die; it exits only on SIGTERM or when the last live session ends (both by explicit operator action and by every pane exiting naturally). The socket at /jackin/run/jackin.sock carries both the one-shot JSON control channel and the persistent tag-plus-length binary attach channel on the same connection, selected by the first byte. The agent runtime status authority is fully shipped and owns the working/blocked/done/idle/unknown vocabulary, including the two-stage done→idle operator-acknowledgement split (SessionStatus::acknowledge() in crates/jackin-agent-status/src/lib.rs); PTY output only schedules reevaluation, it never authors a state.
The control channel today is request/reply only (Status, Snapshot, Agents, ReportRuntimeEvent, StatusCapture, UsageFocused/UsageRefreshFocused/UsageAccountList, ExecCommand, TokenUsage — crates/jackin-protocol/src/control.rs). PaneSnapshot carries the authoritative per-pane state and evidence report straight from the daemon, so crates/jackin-runtime/src/runtime/snapshot.rs and the console read that state directly rather than re-inferring it from polled terminal output. Session creation happens through the attach channel's Hello { spawn } frame rather than a standalone control method; session kill and title updates are handled by in-container operator actions (palette, tab-strip glyph), not by host-callable control methods.
Remaining work
- Control-channel API surface. Add structured
session.kill/session.titlecontrol methods and a streaming event channel (EventsSubscribe-shaped:AgentStateChanged,SessionSpawned,SessionExited,TokenUsageChanged,WorkspaceStatusChanged) so the host and the future daemon can react to state changes instead of pollingSnapshot. This is the one piece of the original Phase 3 API sketch that never shipped, and it is the concrete blocker for Agent attention prompts and for Phase 4 below. - Host-side per-instance status roll-up.
crates/jackin-agent-status/src/arbitrate.rsalready exposes aroll_up_stateshelper (blocked > done > working > idle > unknown) with unit tests but no production caller. The console currently shows per-pane state inside an expanded instance's pane list; wire the roll-up into the workspace list row itself so a blocked pane in a collapsed instance is visible without expanding it. - Kitty graphics (APC) and Sixel passthrough. Designed to forward unchanged for the focused pane (same rule as OSC passthrough) but never implemented —
jackin-termhas noPassthroughEventvariant for APC or Sixel, so both fall under the default-deny CSI/APC policy and are silently dropped today. An agent that emits inline images or Sixel output loses that output. - Phase 4 — host daemon integration and Desktop Agent Hub bridge (deferred). The daemon connects to each running container's socket and maintains a live session index across containers; the desktop app reads from that aggregated view. Both are gated on item 1 above (there is nothing to aggregate without an event stream) and on jackin❯ daemon shipping its own lifecycle first.
Out of scope
- Concurrent multi-client attach. A new attach client takes over from the previous one (which receives a
Shutdownframe); this is deliberate — jackin❯'s single-operator model doesn't need zellij's multi-client cursor/input arbitration. - Persistent exited-tab retention after the last session ends. An intermediate Phase 3 design considered keeping exited tabs around for replay until
SIGTERM; it was rejected because it weakened the "daemon exits when the last live session ends" contract the host relies on to tear down the container, DinD sidecar, cert volume, and network. - A
windowlayer between tab and pane, or aworkspacelayer above session. tmux's window layer predates the tabs-and-splits idiom jackin❯ starts from; herdr's workspace layer only makes sense when one server hosts many projects, and jackin❯'s server hosts exactly one container.
Related work
- Research: Multiplexer rewrite design history — why the rewrite happened, Zellij/Herdr borrows, tmux/Ghostty compatibility rationale.
- Research: Agent Orchestration Program — the full Herdr-vs-jackin❯ comparative table.
- Agent runtime status authority — owns the shipped state vocabulary and arbitration; item 1 above is the delivery mechanism for pushing its transitions to the host instead of polling.
- Agent attention prompts — blocked on item 1 above to replace host PTY polling with real events.
- jackin❯ daemon — Phase 4 above is designed together with this item's per-container endpoint.
- jackin❯ Desktop Agent Hub — consumes the daemon's aggregated view once Phase 4 ships.
- Console agent session control — already consumes the shipped agent runtime status authority for its live pane list; its own Phase 4 (live session reconciliation) is separate, unblocked work.
- jackin-remote — remote containers will need a
jackin-capsulesocket reachable over the tunnel; the socket mount and auth model still need to account for the remote path.