Capsule Session Lifecycle
Overview
Section titled “Overview”Every role container runs the jackin’ Capsule (jackin-capsule) as PID 1. The daemon owns the PTY sessions, renders the in-container multiplexer, serves /jackin/run/jackin.sock, and exits when the last live session ends. The host attaches with a separate docker exec -it <container> /jackin/runtime/jackin-capsule client. See jackin’ Capsule for the control-plane design and wire protocol.
There are two session types:
| Session type | Agent slug | Command source |
|---|---|---|
| Agent pane | claude, codex, amp, kimi, or opencode | /jackin/runtime/entrypoint.sh |
| Shell pane | none | /bin/zsh |
JACKIN_AGENT is per agent pane. The daemon sets it only when spawning /jackin/runtime/entrypoint.sh; shell panes do not receive it.
Container Startup (jackin load)
Section titled “Container Startup (jackin load)”When jackin load provisions a new instance, the container launch happens in two steps:
- The launcher writes
~/.jackin/sockets/<container>/agent.toml; Docker mounts that directory at/jackin/run, so PID 1 reads the same file as/jackin/run/agent.toml. docker run -d ... <image> <agent>starts the container detached. The image entrypoint is/jackin/runtime/jackin-capsule; the trailing<agent>argv tells PID 1 which initial agent tab to spawn. It is not exported as a container environment variable.- The launcher checks that PID 1 did not exit before attach. If it exited, the launcher captures the last 40 lines of container logs and surfaces an actionable error.
- The launcher runs
docker exec -it <container> /jackin/runtime/jackin-capsule. The client connects to the daemon socket and blocks the operator terminal for the duration of the attach.
Implementation: src/runtime/launch.rs (launch_role_runtime, diagnose_premature_exit) and crates/jackin-capsule/src/main.rs (resolve_initial_agent).
Reconnecting (jackin hardline)
Section titled “Reconnecting (jackin hardline)”jackin hardline inspects the target container and chooses the reconnect path:
- Container is running: attach with
docker exec -it <container> /jackin/runtime/jackin-capsule. - Container is running and the operator requested a new agent session: run
docker exec --workdir <workdir> -it <container> /jackin/runtime/jackin-capsule new <agent>. The agent slug travels as argv;JACKIN_AGENTis set later inside the spawned PTY. - Container is running and the operator requested a shell: run
docker exec -it <container> /jackin/runtime/jackin-capsule new. No agent slug means a shell pane. - Container is stopped with crash state: restore the required DinD sidecar, network, and certs when needed, restart the role container, then attach.
- Container is gone but indexed recoverable state exists: rebuild the runtime around jackin-managed local state.
Implementation: src/runtime/attach.rs (reconnect_or_create_session, spawn_agent_session, spawn_shell_session).
Session Inventory
Section titled “Session Inventory”jackin’ queries the daemon through its socket by executing:
/jackin/runtime/jackin-capsule statusThe command opens the Capsule control channel, sends a typed Status request, and prints a line-oriented summary that older host paths parse into AgentSession records. Host console previews use the same control channel with a JSON Snapshot request: same-kernel Docker hosts connect directly to the bind-mounted socket, while Docker Desktop hosts fall back to docker exec ... /jackin/runtime/jackin-capsule snapshot because the host cannot connect to a Linux VM Unix socket directly. If the container is not running, inventory is reported as sessions:not_running without contacting Docker.
Implementation: src/runtime/attach.rs (inspect_agent_sessions) and crates/jackin-capsule/src/client.rs (run_status).
Container Shutdown
Section titled “Container Shutdown”Normal Exit
Section titled “Normal Exit”- An agent or shell process exits.
- The daemon removes the pane and redraws the multiplexer.
- If no live sessions remain, PID 1 drains the final frame and exits.
- The host finalizer observes the stopped container and runs cleanup:
docker rm -f <role-container>docker rm -f <dind-container>docker volume rm <certs-volume>docker network rm <network>
- Keep-awake state is reconciled after the managed containers are gone.
The palette Exit command deliberately drives this same shutdown shape. It asks for confirmation, terminates every live pane, lets PID 1 exit once the session set is empty, and then the reconnect/attach finalizer removes the Docker resources with the normal eject cleanup path.
Detach
Section titled “Detach”When the operator detaches or the terminal closes while sessions are still alive, the attach client exits but PID 1 and the session PTYs continue running. jackin hardline can reconnect to the daemon later. The DinD sidecar, network, and certs volume remain while the role container is preserved. This is separate from Exit: detach is the keep-running path, while Exit is the stop-and-clean-up path.
Crash Or Forced Stop
Section titled “Crash Or Forced Stop”If the role container exits non-zero or is OOM-killed, jackin’ records crash state and removes the DinD sidecar, network, and certs volume. If Docker sends SIGTERM to the role container, PID 1 shuts down the daemon and sessions as the container namespace exits.
What Persists Across Session Exit
Section titled “What Persists Across Session Exit”| Thing | Persists? | Notes |
|---|---|---|
| Agent conversation history | Yes | Stored in the per-instance durable home mount on the host |
| Agent auth tokens | Yes (per auth mode) | Depends on the configured auth forwarding mode |
| Files written in the mounted workspace | Yes | Host-side mount |
| In-container packages installed ad hoc | No | Writable layer is lost on container stop/removal |
| DinD images | No | DinD state is recreated with the sidecar |
| Capsule session layout/state | No | Sessions are recreated by hardline --new or the next load |
Interaction Diagram
Section titled “Interaction Diagram”jackin load -> docker run -d ... <image> claude -> jackin-capsule PID 1 spawns initial Claude PTY -> docker exec -it <container> jackin-capsule -> attach client renders the daemon session
running container -> hardline -> docker exec -it <container> jackin-capsule -> hardline --new --agent codex -> docker exec -it <container> jackin-capsule new codex -> daemon spawns Codex PTY with JACKIN_AGENT=codex -> hardline --shell -> docker exec -it <container> jackin-capsule new -> daemon spawns zsh PTY without JACKIN_AGENT
last live PTY exits -> daemon exits -> Docker reports container stopped -> host cleanup removes role container, DinD, cert volume, and network