tmux vs cmux: Terminal Multiplexer or Agent Workspace?

l9c l9c

TL;DR — cmux is two programs sharing one name: a macOS terminal app built on libghostty that behaves like a dashboard for coding agents, and a separate Rust TUI published on npm as cmux that is a genuine tmux-style multiplexer with a Ctrl+b prefix and attach/detach. tmux is the portable server both can lean on: the cmux app can mirror a remote tmux server into its sidebar, and it can use a local tmux server to keep sessions alive when the app quits. Choose tmux for ubiquity, portability, and text-protocol automation. Choose cmux when the terminal itself should surface agent state, notifications, and a browser.

At a glance

tmux 3.8cmux 0.65.2.2
First release20072025 (0.1.0 in January)
ImplementationC, libevent and ncursesSwift and AppKit with libghostty; a separate TUI in Rust
LicenseISCGPL-3.0-or-later for the app; Business Source License 1.1 for server components; npm TUI package declares MIT; optional paid plans
Design centreAny terminal workload, on any UnixA macOS terminal for AI coding agents
Object modelSessions → windows → panesWindows → workspaces → panes (splits) → surfaces (tabs); the TUI uses sessions → workspaces → screens → panes → tabs
Configuration~/.tmux.conf, a command language~/.config/cmux/cmux.json plus Ghostty config; TUI uses cmux-tui.json
Default prefixCtrl+bNone in the app (⌘ chords); Ctrl+b in the TUI
MouseOn by default (set -g mouse off)Mouse-first GUI; the TUI accepts clicks and right-click menus
Agent statePrimitives only: OSC 133 events, pane commands, hooksHooks, lifecycle states, sidebar glyphs, and an input-state check before automation types
AutomationShell commands, formats, hooks, text control modeJSON CLI over a local control socket, cmux events stream, --json on most commands
Remote hostsSSH, then attach to the server therecmux ssh workspaces, ssh-tmux mirroring (beta), mosh-tmux, cloud VMs
BrowserNoneBuilt-in browser pane with a Playwright-style automation REPL
ExtensibilityTPM plugins, hooks, formats, shell scriptscmux.json commands, skills, socket API, TUI plugins
PlatformsBSD, Linux, macOS, Solaris; Windows via WSLApp: macOS, iOS. TUI: macOS and Linux, Windows planned
Restore after rebootNot native; add tmux-resurrect/continuumLayout, cwd, best-effort scrollback, and supported agent resumes; processes do not survive

One name, two programs

The first thing to get right is that the cmux repository ships more than one front end, and they behave very differently.

Only the second is a multiplexer in the tmux sense. The GUI app has no detach key and no attach command: it is the terminal or it is not running. If you ask "can cmux replace tmux on my server?", the answer is the TUI, not the app — and the TUI is much younger than tmux.

Architecture and who owns the processes

tmux is a client/server system. A long-lived server owns the pseudo-terminals, session state, and layout; clients draw that state and can come and go. The server is native C, has no GUI dependency, and can be reached over a Unix socket, including through tmux -CC control mode.

The cmux app is a GUI process that owns the PTYs and renders them with libghostty. Automation talks to the running app over a local control socket with a trust mode you choose: cmuxOnly accepts only processes started inside a cmux terminal, password requires a secret, and allowAll is explicitly labelled unsafe. There is no separate local session daemon to attach to.

cmux-tui restores the server/client split. cmux server start --session agents runs a headless owner, cmux attach --session agents opens a client, and the server keeps running when the client detaches. The docs keep the durable owner separate from the client, and note that server stop also tears down the owner and its workspaces.

Persistence: detach, restore, and the tmux bridge

This is where the marketing descriptions diverge most from daily use, so it is worth separating three different promises.

tmux 3.8cmux app 0.65.2.2cmux-tui
SSH connection dropsServer keeps runningAuto-reconnects with backoffServer keeps running
Client quits or detachesEverything survivesProcesses die with the appEverything survives
Machine rebootsNothing restores without a pluginLayout, cwd, scrollback and agent sessions restoreTopology is saved; processes are not
Live processes across an app updateRestart the server and lose panesOpt in to cmux local-tmuxRestart the owner and lose panes

The app's session restore is real but bounded. It writes a versioned snapshot, rebuilds windows, workspaces, panes and working directories, reopens browser tabs, restores scrollback on a best-effort basis, and resumes supported agents through their native resume commands. It does not checkpoint arbitrary live process state: ordinary shells, vim, and tmux itself reopen as new terminals.

For live detach/reattach, the app delegates to tmux rather than reimplementing it:

# cmux: a tmux server owns the shell, cmux owns only the client surface
cmux local-tmux start work --cwd ~/src/project --command 'npm run dev'
cmux local-tmux list
cmux local-tmux detach work
cmux local-tmux attach work

The app's own docs are candid about the limits: the owner survives quitting, crashing, or updating cmux, and normally survives sleep, but not logout, restart, shutdown, or power loss. That is not a defect unique to cmux — it is the same boundary tmux has — but it means "cmux keeps my sessions alive" is only true when tmux is installed and this opt-in profile is used.

The TUI is closer to tmux out of the box. A plain cmux run starts or reuses a detached owner and attaches to it, so detaching (Ctrl+b d) never ends the session.

Object model and navigation

tmux's hierarchy is sessions, windows, and panes, and every navigation action goes through the prefix. cmux keeps the same ideas but renames two levels and moves most navigation to the mouse or to ⌘ chords.

Concepttmux 3.8cmux appcmux-tui
Top containerSessionWindow (app instance)Session (one socket)
Project containerWindow (named)WorkspaceWorkspace
Layout containerWindow layoutPaneScreen
View containerPaneSurface / tabTab
New viewCtrl+b c⌘+TCtrl+b c or t
SplitCtrl+b % / "⌘+D / ⌘+⇧+DCtrl+b % / "
Focus paneCtrl+b o or arrows⌥+⌘ arrowsCtrl+b o or h/j/k/l
Jump between viewsCtrl+b w (choose-tree)⌃+1–9 (surfaces), ⌘+1–9 (workspaces)Ctrl+b 0–9
DetachCtrl+b dNo equivalentCtrl+b d

One naming trap: a cmux pane is the split region, and surfaces (tabs) live inside it. new-split creates a pane; new-surface adds a tab to one.

The TUI deliberately matches tmux for the shared verbs: c, n, p, &, ,, z, o, {, }, and number keys all mean what they mean in tmux, and pressing the prefix twice sends a literal Ctrl+b. Two differences bite anyway: Ctrl+b x closes a tab while Ctrl+b X closes the pane, and Ctrl+b w selects the next workspace instead of opening a picker. The TUI also has no paste buffers, so Ctrl+b ] is unbound.

The app has no prefix at all. Its own actions are ⌘ chords, and terminal-level keybindings come from your Ghostty config, including leader sequences. That means muscle memory from tmux works only inside the terminal programs, not for window management.

Agent awareness: detection vs. lifecycle

tmux treats every pane as an opaque terminal. It exposes strong primitives — #{pane_current_command}, pane titles, capture-pane, hooks, and, in 3.8, OSC 133 events for command start and finish plus wait-for -E — but nothing in tmux knows that the thing in pane 2 is waiting for approval. You build that state machine yourself.

cmux ships the state machine and exposes it to scripts. Agent hooks capture native session IDs and report lifecycle. The sidebar summarizes each workspace with a single glyph whose states include running, needs input, waiting on a background command, working through subagents, idle, and error. Notifications arrive through standard OSC 9/99/777 sequences or cmux notify, and show as pane rings, a notification panel, and macOS banners.

The most tmux-unlike piece is the input-state check. Before typing, cmux send asks the app whether the surface is empty, holds a human's draft, has a dialog open, or is unknown (the reported state names), and refuses to type over a draft or an open permission prompt:

# tmux: the send always lands, whoever was typing
tmux send-keys -t agents:0.1 'fix the failing test' Enter

# cmux: refused when a person is mid-draft or a dialog is open
cmux send 'fix the failing test'
cmux send-key enter

The same information is available to your own programs through surface.input_state, which returns the input state together with the agent's lifecycle (unknown, running, idle, or needsInput) and a blocks_typing flag, alongside the workspace status lanes used by the sidebar: todo, working, needs-attention, review, and done.

Prior art check: if you only want blocked/working state from a terminal tool, herdr classifies panes directly. cmux's version is app-level and broader — notifications, browser, and resume — but macOS-bound.

Automation: text protocol vs JSON API

tmux automation is a text protocol with two decades of scripts behind it. Commands return text, formats such as #{session_name} form a small query language, and control mode (tmux -C) frames replies with %begin, %end, and %error while streaming notifications.

cmux automation is JSON-first. The CLI is a thin client over a documented socket API, selectors accept typed IDs, and cmux rpc calls a raw method when no wrapper exists. cmux capabilities reports what the server supports, and cmux events streams newline-delimited JSON instead of %output blocks.

Tasktmux 3.8cmux app 0.65.2.2
Run a command in a panesend-keys -t … 'cmd' Entercmux send 'cmd', cmux send-key enter
Read the screencapture-pane -p -t …cmux read-screen --surface …
Split a panesplit-window -h/-vcmux new-split right
Machine-readable outputFormats and control-mode text--json on most commands
Event streamControl-mode notifications, hooks, monitorscmux events as NDJSON
Schema discoveryManual pagecmux capabilities, cmux rpc, cmux guide
Guarded inputNoneRefuses to type over drafts and dialogs

cmux-tui has its own noun-first CLI (cmux workspace create, cmux terminal … screen read, cmux session current events --jsonl) and returns opaque typed IDs rather than tmux's index-plus-name addressing. If your existing tooling parses tmux formats, porting is real work; if you are writing new orchestration, the JSON surface is easier to consume.

Remote machines: attach vs. mirror

tmux's remote story is SSH plus attach. The server lives on the host, you connect, attach, and detach. It assumes nothing and works on machines where you cannot install anything else.

cmux has three remote modes, and one of them is explicitly a tmux client:

Remote tmux is a beta feature, off by default, and requires tmux 3.2 or newer on the host. That requirement is the clean dividing line: cmux does not replace remote tmux, it consumes it. The TUI takes a different route, using SSH to start a matching cmux-tui owner on the remote host and reconnecting without nesting a second TUI.

License, platforms, and ecosystem

tmux is ISC-licensed, available from the package manager of essentially every Unix-like system, part of the OpenBSD base system, and runs on FreeBSD, NetBSD, Linux, macOS, and Solaris, with WSL on Windows. It needs no account, no cloud, and no companion service.

cmux is GPL-3.0-or-later, with the server directories under the Business Source License 1.1, which requires a commercial license for production self-hosting. One inconsistency to watch: the root license says each directory keeps its own terms, and the npm-distributed TUI package declares MIT in its manifest, so check the component you actually redistribute. The app itself is free, and optional paid tiers cover cloud machines, team features, and early access. That matters if you plan to embed, redistribute, or self-host the server side — ISC gives you options GPL does not.

Platform support is narrower: the app targets macOS, with an iOS beta. The TUI runs on macOS and Linux, with Windows planned, and it is the part you can actually run on a server today. The app adds cloud VMs and a skills collection, but those are ecosystem breadth rather than multiplexer features.

Both are fast native programs. tmux renders text and SIXEL graphics; the app renders through libghostty and puts a real browser next to your terminal. That browser, not the rendering engine, is the feature tmux has no answer for.

Migration map

Tasktmux 3.8cmux appcmux-tui
StarttmuxLaunch the app, ⌘+Ncmux
Named sessiontmux new -s workWorkspaces, not sessionscmux --session work
DetachCtrl+b dNo equivalent; use cmux local-tmuxCtrl+b d
New window or tabCtrl+b c⌘+TCtrl+b t (tab), c (screen)
Split right / downCtrl+b % / "⌘+D / ⌘+⇧+DCtrl+b % / "
Close paneCtrl+b x⌘+W closes the tabCtrl+b X (pane), x (tab)
ZoomCtrl+b z⌘+⇧+↩ (toggle pane zoom)Ctrl+b z
Send inputtmux send-keys -t …cmux send …cmux workspace current run -- …
Read outputtmux capture-pane -pcmux read-screencmux terminal <id> screen read
Config~/.tmux.conf~/.config/cmux/cmux.json~/.config/cmux/cmux-tui.json

Which should you use?