Persistent, self-healing Claude Code sessions in tmux — across a fleet of machines.
A single daemon per machine keeps a fleet of long-running agent sessions alive in tmux:
it heals crashed ones, brings them back on reboot, and resumes the same conversation by a
pinned uuid. Sessions are full interactive claude processes (your subscription, Remote
Control, slash-commands, statusline) — ccmux supervises them, it does not reimplement them.
┌─ daemon (launchd/systemd) ─ heals every 30s, self-updates ─┐
│ tmux: cc-api cc-web cc-infra … (each = `ccmux _run` → claude, auto-restart)
└────────────────────────────────────────────────────────────┘
▲ ccmux list / new / attach / send / restart … ▲ interactive TUI (bare `ccmux`)
One command — installs bun if missing, downloads the latest verified bundle, drops a
ccmux shim on your PATH, and starts a self-updating daemon:
curl -fsSL https://github.com/max-listov/ccmux/releases/latest/download/install.sh | bashSet the boot label / RC prefix with CCMUX_RC_PREFIX=prod (default local). Re-running is
safe — it just refreshes to the latest release. Requires macOS (launchd) or Linux (systemd)
and tmux.
ccmux # interactive fleet TUI (add -f for fullscreen)
ccmux list # managed sessions + live status/uptime
ccmux new cc-api ~/code/api # create + start a session (pins a fresh uuid)
ccmux send cc-api '/compact' # type into a session (text or a /slash command)
ccmux restart cc-api # bounce it (survives killing the caller)
ccmux stop|start|rm cc-api # lifecycle (rm keeps the jsonl history)
ccmux transcript cc-api --json --tail 50 # conversation history as JSON
ccmux doctor # health check: bins, config, daemon
ccmux help # full command listAttach to a session like any tmux pane: tmux attach -t cc-api (detach with Ctrl-b d), or
press Enter on it in the TUI.
A claude you started by hand (outside ccmux) shows up in the TUI under external. Adopt it
to let the daemon manage it:
ccmux adopt <uuid> --fork # safe copy under a new uuid (original untouched)
ccmux adopt <uuid> --takeover # take over the original (kills the live writer)- One daemon per machine (launchd
com.<prefix>.ccmux/ systemdccmux.service) heals the fleet every 30s and starts it on boot. It runs the prod bundle, not your source. - Each session is a tmux session whose foreground process is
ccmux _run <name>— a tiny supervisor loop that launchesclaudeand relaunches it on crash (exponential backoff). So an agent crash just comes back; killing a session is the only way to stop it. - Deterministic resume: every session pins a fixed uuid (
--session-idfirst,--resumeafter) → no resume-picker, no forked conversation. - jsonl is the source of truth for the conversation (transcript, tokens, "where it stopped"); the pane is scraped only for live status.
Both paths share one safe core: download → sha256-verify against the manifest → preflight
(bun <candidate> version must load and report the right version) → atomic swap of the prod
bundle (.bak kept) → bounce the daemon. Sessions survive the bounce (tmux is independent of
the daemon); each picks up the new code on its next restart. A boot-guard reverts to .bak if a
bad bundle crash-loops the daemon.
ccmux update # update now to the latest published release
ccmux update --check # is there a newer version?
ccmux update --rollback # revert to the previous bundle (.bak)With autoUpdate on (wired at install via --release-url), the daemon checks every
updateCheckInterval seconds (default 300) and applies a newer release on its own — hands-off
across the whole fleet.
ccmux is a Bun + TypeScript app; the TUI is Ink (React → terminal).
bun install
bun run dev # run the CLI/TUI from source (this is `ccmux-dev`)
bun run smoke # headless TUI e2e in a throwaway tmux pane
bun test # tests
bun run typecheck # tsc --noEmitThe dev source and the prod daemon are decoupled — editing source never touches the running prod
bundle. See docs/architecture/ for the TUI, IO/perf model, and dev flow.
The release tooling lives in the source checkout only — clients ship a single bundle, no repo:
bun run stage # build → ~/.ccmux/staged/ccmux.js, then `ccmux update` to test locally
bun run release:publish "notes" # build → sha256 → GitHub Release vX.Y.Z (3 assets, atomic)A release is a tag vX.Y.Z with three assets: the ccmux.js bundle, a release.json manifest
(version + sha256 + versioned bundle url), and install.sh. Tags are immutable — bump the
version in package.json to cut a new one. The fleet tracks
releases/latest/download/release.json.
MIT © ccmux contributors