# jackin❯ Capsule: In-Container Control Plane (https://jackin.tailrocks.com/roadmap/jackin-capsule/)



**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](/reference/capsule/) — architecture, process model, wire protocol, distribution, input routing.
* [Multiplexer design rules](/reference/capsule/multiplexer-design-rules/) — the passthrough contract and per-feature verification checklist.
* [Capsule Terminal Model](/reference/capsule/terminal-model/) — why the VT emulator was replaced twice (hand-rolled → `vt100` fork → `jackin-term`).
* [`jackin console`](/commands/console/) and [`jackin hardline`](/commands/hardline/) — operator-facing prefix keys, palette, tabs.
* Research: [Multiplexer rewrite design history](/reference/research/agent-orchestration/jackin-capsule-multiplexer-rewrite/) — 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 [#shipped-facts]

`crates/jackin-capsule/` is a standalone workspace member sharing wire types with the host CLI through `jackin-protocol` (<RepoFile path="crates/jackin-protocol/src/control.rs">crates/jackin-protocol/src/control.rs</RepoFile>, `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 <RepoFile path="crates/jackin-image/src/capsule_binary.rs">crates/jackin-image/src/capsule\_binary.rs</RepoFile> 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](/roadmap/agent-runtime-status/) is fully shipped and owns the `working`/`blocked`/`done`/`idle`/`unknown` vocabulary, including the two-stage `done`→`idle` operator-acknowledgement split (`SessionStatus::acknowledge()` in <RepoFile path="crates/jackin-agent-status/src/lib.rs">crates/jackin-agent-status/src/lib.rs</RepoFile>); 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` — <RepoFile path="crates/jackin-protocol/src/control.rs">crates/jackin-protocol/src/control.rs</RepoFile>). `PaneSnapshot` carries the authoritative per-pane `state` and evidence report straight from the daemon, so <RepoFile path="crates/jackin-runtime/src/runtime/snapshot.rs">crates/jackin-runtime/src/runtime/snapshot.rs</RepoFile> 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 [#remaining-work]

1. **Control-channel API surface.** Add structured `session.kill` / `session.title` control 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 polling `Snapshot`. 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](/roadmap/agent-attention-prompts/) and for Phase 4 below.
2. **Host-side per-instance status roll-up.** <RepoFile path="crates/jackin-agent-status/src/arbitrate.rs">crates/jackin-agent-status/src/arbitrate.rs</RepoFile> already exposes a `roll_up_states` helper (`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.
3. **Kitty graphics (APC) and Sixel passthrough.** Designed to forward unchanged for the focused pane (same rule as OSC passthrough) but never implemented — `jackin-term` has no `PassthroughEvent` variant 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.
4. **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](/roadmap/jackin-daemon/) shipping its own lifecycle first.

## Out of scope [#out-of-scope]

* **Concurrent multi-client attach.** A new attach client takes over from the previous one (which receives a `Shutdown` frame); 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 `window` layer between tab and pane, or a `workspace` layer 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 [#related-work]

* Research: [Multiplexer rewrite design history](/reference/research/agent-orchestration/jackin-capsule-multiplexer-rewrite/) — why the rewrite happened, Zellij/Herdr borrows, tmux/Ghostty compatibility rationale.
* Research: [Agent Orchestration Program](/reference/research/agent-orchestration/program-research/) — the full Herdr-vs-jackin❯ comparative table.
* [Agent runtime status authority](/roadmap/agent-runtime-status/) — 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](/roadmap/agent-attention-prompts/) — blocked on item 1 above to replace host PTY polling with real events.
* [jackin❯ daemon](/roadmap/jackin-daemon/) — Phase 4 above is designed together with this item's per-container endpoint.
* [jackin❯ Desktop Agent Hub](/reference/research/desktop/agent-hub/jackin-desktop-agent-hub/) — consumes the daemon's aggregated view once Phase 4 ships.
* [Console agent session control](/roadmap/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](/roadmap/jackin-remote/) — remote containers will need a `jackin-capsule` socket reachable over the tunnel; the socket mount and auth model still need to account for the remote path.
