The Conmux Manual
Conmux is a tmux-like middleware built for Windows: it hooks up any CLI — native programs, PowerShell, even tools inside WSL — into one set of supervised sessions. Its goal is modest: let agent CLIs on Windows run and stay watchable, without dragging in a whole IDE.
This manual is split into paths by "who you are" — just pick the one closest to you:
| You are… | Start here |
|---|---|
| New to terminal multiplexers (never heard of tmux? that's fine) | What It Is & Why It's Worth Using |
| A tmux veteran who just wants to know what's different on Windows | Differences at a Glance |
| Looking to drive it from code / automate things | Advanced · Driving conmux from Code |
| Building your own agent framework and want to hook it up | Hooking Your CLI Up to conmux |
Honest note (where the honest boundary is): Conmux is still young (v0.1.x) and Windows-only. The core — sessions, split panes, whole-process-tree supervision, detach/attach reconnection — is already running in real use; persistence across daemon restarts and remote attach are still on the way. Throughout this manual, "works today" and "not built yet" are labeled honestly, point by point — the roadmap is never presented as the present.
If anything reads awkwardly, won't install, or you have a better idea, open an issue or discussion and tell me. This is an open-source project by a South China Normal University student, and I'd love to work with you to make the agent ecosystem on Windows better.
What It Is & Why It's Worth Using
(This page assumes you've never used tmux or any "terminal multiplexer". If you have, skip straight to Differences at a Glance.)
The problem first
You're running things on the command line — say, an AI coding agent (Claude Code, Codex…), or a build, or a local server. One terminal window per thing. Want to run three or four at once? Then it's three or four windows, and you're clicking back and forth between them: checking one by one who's stuck, who's waiting for your input, who's finished.
Once the windows pile up, you become the "human scheduler" — constantly switching around, terrified of missing the one that's waiting on you.
What Conmux does
Conmux puts multiple command-line sessions inside one window, each in its own patch (called a "pane"). In that window you can:
- Split panes — carve one window into a grid and watch several sessions side by side.
- Switch — a row of session dots represents each session; click one to jump over.
- Leave and come back — close the Conmux window and the sessions inside don't die: they keep running in the background; open it next time and the screen picks up right where it was (this is called detach / attach).
- Keep watch — whichever session needs you (say, an agent pops a permission request), Conmux flags it for you, instead of making you flip through them one by one.
In one sentence: it watches those command lines for you, so you only step in when you're actually needed.
Why Conmux, not tmux
If you've ever searched for "terminal multiplexer", you've most likely seen tmux — the classic tool on Linux / macOS. The problem: tmux was not built for Windows. To use it on Windows, you first have to install a layer of WSL (a Linux subsystem running inside Windows).
Conmux runs directly on Windows' own terminal substrate (called ConPTY), no WSL required. And if you do want to bring in tools from WSL, it can do that too — there's a dedicated section on it.
It's also lighter than "stuffing your agent into a full IDE (say, VS Code plus a pile of extensions)": Conmux revolves around the command line itself, without the whole editor apparatus.
Honest note: the GUI version of Conmux uses the system's built-in WebView2, which is not the most extreme lightweight option (the memory floor is a few hundred MB). It's far leaner than a full IDE, but if you're chasing "absolute minimum memory", the pure command-line
conmuxtool (no GUI) is the lightest tier.
Next up
- Haven't installed it yet? → Install It
- Installed and ready to try? → Your First Session & Split Panes
Install It
This page walks you through getting Conmux onto your own Windows machine. Up front: there is no prebuilt installer to download yet, so you either install the CLI version with cargo or build the GUI version from source. Sounds a bit manual, but every step is written out — just follow along.
Check Your Machine First
- OS: Windows 10 (version 1809 or later) or Windows 11. Conmux runs on Windows only — its entire reason for existing is going the Windows-native route, so there is no macOS / Linux build.
- For the CLI version (crate): you need the Rust toolchain, with the MSVC component (picking the default
x86_64-pc-windows-msvcwhen installing Rust is exactly right). - For building the GUI version from source: on top of Rust above, add Node.js 18 or later (which ships with
npm), plus Git.
Tip: if you just want a quick taste of what Conmux can do, installing the CLI version under option ① below is the least hassle — no Node required, done in minutes. Save the GUI build for when you're sure you'll use it.
Three Paths — Pick One
① The CLI version (the conmux crate) — lightest, fastest
It's one command:
cargo install --locked conmux
Once it's installed you have the conmux command and can start sessions, split panes, and detach / attach right away (see Your First Session & Split Panes for how). This tier has no GUI and is the smallest-memory-footprint one — the same one What It Is & Why It's Worth Using means by "if you're chasing minimal memory, pick this."
If you want to use Conmux as a library inside your own Rust project (rather than running it as a CLI tool), skip cargo install and add one line to your project's Cargo.toml instead:
[dependencies]
conmux = "0.1"
② Build the GUI version from source (conmux-app)
For now, the GUI version can only be built yourself. Four steps:
git clone https://github.com/Verson1daddy/Conmux.git
cd Conmux\conmux-app
npm install
npm run tauri:build
After a successful build, the Windows installer (NSIS format) is in this directory:
conmux-app\src-tauri\target\release\bundle\nsis\
Inside you'll find a .exe installer — double-click it to install Conmux onto your system.
Pitfalls you might hit:
- The first run of
npm run tauri:buildtakes a while — it has to compile the entire Rust backend, and anywhere from a few minutes to over ten is normal the first time. Don't assume it's hung.- If Rust or Node isn't set up properly, this step errors out partway through; go back to "Check Your Machine First" above, fill the gaps, and rerun.
- The installer is unsigned (see below for why), so Windows pops a SmartScreen prompt when you double-click it — how to handle that is also covered below.
③ Download an installer straight from Releases · 🚧 Roadmap (not built yet)
Eventually there will be prebuilt installers on the GitHub Releases page, and you won't have to build anything yourself. They're not there yet, so for now the GUI version is source-build-only via option ② above. Come back to this path once installers ship.
About "Unsigned" and That Scary Blue Box
Whether it's an installer you built yourself or one from Releases later, it will be unsigned — this is a student's open-source project, and there's no budget for a code-signing certificate.
The consequence: when you double-click the installer, Windows SmartScreen pops a blue box saying "Windows protected your PC" and "Publisher: Unknown." This does not mean something is wrong with the software — it's just how Windows treats every program that didn't pay for a certificate. To keep installing, click:
More info → Run anyway
The box goes away and installation proceeds normally. If that prompt genuinely makes you uneasy, take option ① the CLI route instead — cargo install goes through Rust's official package distribution and never touches the whole SmartScreen business.
Installed — Now What?
- Want to dive in right away → Your First Session & Split Panes
- Want to first understand what problem it actually solves → What It Is & Why It's Worth Using
Your First Session & Split Panes
Installed? Good — let's get it running and cover the handful of moves you'll use most, in five minutes.
(This page is about the Conmux GUI. The command-line-only conmux tool goes a different route — see Driving conmux from Code.)
What it looks like on launch
Open the Conmux GUI and you'll see a window running a command-line session — just like opening a terminal normally. Type whatever you want to run into it (an agent CLI, a build, a local server, your call).
The window has a strip of session dots along the bottom: every session you open adds one dot. Click a dot and you jump to that session. Once you have several sessions going, this row of dots is your at-a-glance view of "where am I, and what else is running."
These are the session dots from the previous page — "a row of dots, click one to jump over." Sessions never interfere with each other; each runs on its own.
The leader prefix: Conmux's "secret handshake"
Splitting panes, moving focus — Conmux triggers these actions with a leader key, Ctrl+B by default (tmux veterans will find this familiar).
It's a two-step gesture, not four keys pressed at once:
- Press
Ctrl+Bonce and release — this keystroke doesn't reach your program; Conmux enters a "standby" state (a small on-screen hint tells you it's waiting for the next key). - Then press a single command key — say
\,-, orz— and Conmux performs the corresponding action.
Why two steps? This way Conmux never touches your keyboard in normal use: apart from that one
Ctrl+Bkey, every other keystroke passes straight through to your program. Only the keystroke right afterCtrl+Bgets treated as a Conmux command. This is a hard guarantee from the kernel — it will never break your CLI.Two thoughtful details while we're at it: after pressing
Ctrl+B, if you don't press a command key within 1.5 seconds, it automatically falls back to pass-through (you didn't fat-finger anything; pretend nothing happened). And if you actually want to send theCtrl+Bkey itself to your program, pressCtrl+BthenCtrl+Bagain — the literal prefix gets delivered to the current session.
Split panes: one window, several cells
Press Ctrl+B and release, then press:
| Then press | Effect |
|---|---|
\ (backslash) | Vertical split — the current pane splits left/right, with a new session opening on the right |
- (minus) | Horizontal split — the current pane splits top/bottom, with a new session opening below |
Want a 2×2 grid? Just split a few more times — one vertical split, then one horizontal, arrange it however you like. Each cell is a "pane," each running its own session.
Moving focus between panes
Once you've split, keyboard input needs a notion of "which cell am I in right now." Press Ctrl+B and release, then press an arrow key, and focus jumps to the pane in that direction:
Ctrl+Bthen←/→/↑/↓— move focus to the pane on the left / right / above / below.
Whichever pane has focus is where your keystrokes go.
Getting a closer look: zoom
After splitting, every cell gets smaller. Want to temporarily blow one cell up to fill the whole window?
Ctrl+Bthenz— the currently focused pane zooms to full screen; doCtrl+Bzagain to restore the original pane layout.
(z = zoom. The other panes aren't closed, just temporarily hidden — restore and they're back.)
Leave and come back: detach / attach
This is the thing Conmux most wants you to feel safe about:
Close the Conmux window outright, and the sessions inside don't die. They keep running in the background. Open Conmux again and the picture reconnects exactly as it was — wherever things had gotten to, whatever was on screen, it's all there (scrollback history, cursor, even the UI state of full-screen programs gets replayed as-is).
This is detach / attach. It's especially useful for long-running tasks: close the window, go do something else, come back and pick up watching — nothing lost.
Honest note: "close the window and sessions don't die" refers to closing the client window. The sessions' real host is the background daemon; if you explicitly stop the daemon (
conmux kill-server) or it crashes on its own, that's when all sessions end together — this is the other side of "never leave orphan processes," a deliberate design choice, not a bug.
Not there yet (roadmap)
Marking the boundaries honestly, so you don't press something, get no response, and assume it's broken:
- The GUI itself is still under construction — the split, focus, and zoom keybindings above have frozen specs and interactions, and the code has landed, but the full native GUI shell is the current mainline development effort, and details may still be getting polished.
- Persistence across daemon restarts — right now detach/attach means reconnecting while the daemon stays alive; "sessions survive a reboot" style cross-process persistence is out of scope for now (an explicit design trade-off).
- Remote attach / unified cross-WSL support — both still on the roadmap; see Differences at a Glance and the Roadmap in the conmux repo.
What's next
- tmux veteran wondering what's different? → Differences at a Glance
- Want to drive it from code for automation? → Driving conmux from Code
Differences at a Glance
(This page is written for people who already live in tmux. It assumes you know what a prefix, a pane, and a split are, and just want to know what's the same and what's different on Windows, in Conmux.)
The one-sentence version first
Conmux borrows tmux's feel — a leader key, then one command key to split panes, move focus, zoom — but it is not a drop-in replacement for tmux. It doesn't read your .tmux.conf, its command set is a small subset of tmux's, and it runs on a Windows-native foundation. So: your muscle memory mostly carries over, but don't expect to port your tmux config and have it just work.
Windows-native, no WSL required
This is Conmux's number-one reason to exist. tmux was never designed for Windows — to use it there, you first install a layer of WSL (the Linux subsystem), and then your tmux actually lives inside that Linux and can't manage processes on the Windows side.
Conmux runs directly on Windows' own pseudo-terminal (ConPTY), no WSL needed. PowerShell, native programs, AI agent CLIs — they're all panes supervised under one process tree. (Want to bring in tools from WSL too? You can — but that belongs to "cross-WSL unification" (M4) on the roadmap, which isn't built yet; there's a separate section on what's possible today.)
The leader key: Ctrl+B by default, and why not Ctrl+Space
Good news: Conmux's default leader is Ctrl+B, exactly the same as tmux's own default — zero onboarding cost for veterans.
We actually tried Ctrl+Space first (a tmux custom prefix a lot of people love). We hit a landmine: on Chinese Windows, Ctrl+Space is the IME's global "Chinese/English toggle" hotkey — the IME swallows it first and Conmux never receives it, so the leader is effectively dead in a Chinese environment. Hence the default went back to Ctrl+B to steer clear of it.
The leader key is configurable (in-app, persisted locally). The one hard constraint: the leader must include Ctrl or Alt — a bare key as leader would swallow every ordinary keystroke as a command and outright break your CLI, so that's not allowed.
Same small detail as tmux: pressing the leader twice (
Ctrl+BthenCtrl+B) sends a literal prefix character into the current terminal — the equivalent of tmux'ssend-prefix.
Keybinding cheat sheet
Press the leader (default Ctrl+B), then the command key. Here's tmux vs. Conmux side by side:
| What you want | tmux (default) | Conmux | Notes |
|---|---|---|---|
| Vertical split (side by side) | prefix % | prefix \ | Conmux uses \, not % |
| Horizontal split (stacked) | prefix " | prefix - | Conmux uses -, not " |
| Move focus between panes | prefix ←↑↓→ | prefix ←↑↓→ | Same |
| Resize a pane | prefix Ctrl+←↑↓→ | prefix Shift+←↑↓→ | Conmux uses Shift+arrows |
| Zoom a pane to full screen (toggle) | prefix z | prefix z | Same |
| Jump to session N | prefix 0..9 (switches windows) | prefix 1..9 | Conmux jumps between sessions, starting at 1 |
| Next / previous session | prefix n / prefix p | prefix n / prefix p | Same |
| Open the command palette | prefix : (command line) | prefix : | Conmux opens the in-app command palette |
Honest note: this table covers exactly the leader commands Conmux has implemented — there is nothing more. tmux's session/window/pane three-level model, copy-mode,
prefix [paging and selection, theprefix ddetach shortcut (Conmux's detach is closing the client window, not a keybinding), command aliases, custombind-keybindings — none of these exist yet. Don't go down a tmux cheatsheet trying them one by one.
Leader-free direct shortcuts (opt-in, off by default)
Find the two-step leader tedious? Conmux offers a tier of leader-free direct shortcuts, one step and done:
Ctrl+Alt+H/J/K/L→ move pane focus in vim directionsCtrl+Alt+\→ vertical split ·Ctrl+Alt+-→ horizontal split ·Ctrl+Alt+Z→ zoom
But it's off by default — you have to explicitly enable it in settings. Why off by default: Conmux's core promise is "never break your CLI" — in the default state it intercepts exactly one key, the leader, and passes everything else through to the terminal untouched. Turning on direct shortcuts means letting it intercept a small handful of Ctrl+Alt combos too; that trades a bit of convenience against the purity of pass-through, so the choice is yours. (Also: genuine AltGr character composition is not intercepted, and non-US keyboard layouts won't have their input broken.)
Differences spelled out (don't step on these)
- It doesn't read
.tmux.conf. Conmux has no tmux config file parsing whatsoever. The leader key and direct shortcuts are configured in-app and stored locally — completely separate from tmux's config files. - The command set is a subset, not the full set. Most tmux features outside the table above don't exist; think of it as "a Windows-native multiplexer that borrows tmux's feel", not "tmux for Windows".
- detach / attach semantics differ. In tmux, sessions live on the server and
prefix ddetaches; in Conmux you close the client window and the panes keep running in the background — the nextattachreplays the screen exactly as it was (scrollback plus terminal mode state included). But note: panes outlive the client, not the daemon —conmux kill-serveror a daemon crash takes every pane down with it (that's the flip side of the "zero orphan processes" guarantee; the control plane chapter covers this in detail). - It's still young. Conmux is v0.1.x. The keys marked "Same" above are what's usable in the current implementation; the things marked "doesn't exist yet" genuinely don't. The manual flags these honestly, page by page — no selling the roadmap as the present.
Next up
- Want to bring in tools from WSL? → Bringing In Tools from WSL
- Want to drive it from code and automate? → Driving conmux from Code
Bringing In Tools from WSL
You already know Conmux runs directly on Windows' own terminal substrate (ConPTY) and doesn't need WSL. But plenty of people actually have WSL at hand — with the whole Linux toolchain installed inside, and some CLI that only runs smoothly in Ubuntu. This page covers: how to bring up a command inside WSL as a supervised Conmux pane.
Already works: launch a WSL pane in one click
In the GUI's Home add-item flow, there's a WSL picker. Open it, and Conmux asks your system which distributions are installed, then lays them out in a row for you to pick from:
- Under the hood it runs
wsl.exe --list --quiet— it just probes what's installed and doesn't start anything. - Got
Ubuntu,Debian,Ubuntu-22.04... installed? They all show up; - No WSL, or not a single distribution installed? The picker just stays empty, and you fall back to the plain "text add-item" and type a command by hand — no errors, no hangs.
Pick a distribution, fill in the CLI to run (say bash, htop, or some Linux-side agent command), and Conmux assembles this command for you and brings it up as a new pane:
wsl -d <distro> -- <your CLI>
For example, pick Ubuntu and fill in htop as the CLI, and what launches is wsl -d Ubuntu -- htop. This pane is treated exactly the same as a PowerShell pane: it can split, switch, keep running in the background after you detach and close the client, and the screen picks up right where it left off on the next attach. Whole-tree supervision (Job Object) applies too — close this pane, and the processes it spawned on the outside get cleaned up with it.
Tip: the picker just fills in the
wsl -d ... -- ...line for you. If you'd rather type it yourself, write that whole line in the plain add-item flow and launch it as a command with arguments — the effect is exactly the same. The picker saves effort; it's not the only entry point.
Not done yet (M4 roadmap): a unified Win/WSL "session"
Let's be clear here, so you don't overestimate what Conmux can do today.
What works now: bringing up a CLI inside WSL as an independent supervised pane — watchable, reconnectable after a disconnect. That's it — under the hood it's "Conmux launches wsl.exe, and wsl.exe itself enters WSL to run your command."
What doesn't work yet (all on the M4 roadmap, unimplemented):
- Graceful termination across the boundary (signal proxying) — letting the Windows-side daemon cleanly signal processes inside WSL to wind down, instead of just yanking the outer
wsl.exe; - Path translation — automatic conversion between Windows paths (
D:\foo) and WSL paths (/mnt/d/foo); - One unified Win/WSL session tree — letting a PowerShell pane and a WSL pane be addressed, terminated, and managed as equals under the same supervision semantics.
These three things together are the "owning the Win/WSL boundary" goal Conmux is actually after. Today it's not implemented yet — it's an M4 target. So for now, use it like this: treat a WSL CLI as a pane that's convenient to bring up, not as deep cross-boundary unification — that's still on the way.
Next up
- Want to see exactly how things differ from tmux on Windows? → Differences at a Glance
- Want to drive these panes from code? → Driving conmux from Code
Driving conmux from Code
(This page is for people who want to build automation, write tools, or hook Conmux up to their own scripts/programs. It assumes you know a little Rust; if you don't write code and just want to drive things with keystrokes, feel free to skip this page.)
One sentence up front
With most terminal multiplexers, the only way to poke them is the keyboard. Conmux is different: behind it sits a control plane you can drive directly from code — opening sessions, sending keystrokes, capturing screens, following a pane's output as it streams by — all of it can be done programmatically, not just via shortcuts.
The skeleton: one daemon + a bunch of thin clients
Conmux uses the tmux-style "server" model:
- The daemon holds all the real ConPTYs. Every pane you open — the process, its entire child-process tree, the scrollback history, the terminal mode state — lives inside this one daemon.
- Clients are "thin" — whether it's the
conmuxcommand line, the GUI shell, or a program you wrote yourself, each one just connects to the daemon over a named pipe and talks to it. Clients themselves hold no panes.
This is why "closing a client window doesn't kill what's inside": the only thing that dies is that connection — the pane keeps running in the daemon. (The flip side: only when the daemon itself goes away — kill-server or a crash — do all panes go with it. That's the other half of the "zero orphan processes" guarantee; see Mechanism vs. Semantics: Where the Boundary Is.)
Trust boundary: the pipe is open to the current user only (the DACL grants access solely to your own SID, and remote clients are rejected). The control plane is local — no accounts, no telemetry, nothing leaves this machine.
The control plane gives you three things
Verified against the source: these three things the conmux crate exposes are the core of the control plane:
- Request / reply — a framed, one-question-one-answer protocol. You send an operation (
MuxOp), the daemon sends back a reply (MuxReply::Ok { payload }orErr), with acorrelation_idso the two can be matched up. - A per-pane event stream — subscribe to a pane and you'll receive its
PaneOutput(with aseqnumber that is strictly monotonic per pane, starting from 1) andPaneExited(with the exact exit code). - Stable pane ids — every pane has a stable
PaneId, and that's how you address it: send, capture, resize, attach all go by this id.
With these three, you can drive it from code instead of just keystrokes.
Driving it from Rust
The client entry point is conmux::client::Client (all the method names below have been verified against client.rs):
#![allow(unused)] fn main() { use conmux::client::Client; use conmux::protocol::MuxOp; // Connect to the current user's daemon; if there is no daemon, one is spawned for you automatically (the tmux mindset). let mut client = Client::connect_or_spawn()?; // Request/reply: list all current panes. let panes = client.request(MuxOp::ListPanes)?; // Inject keystrokes into a pane (raw bytes, going through the single audited write path inside the daemon). client.request(MuxOp::Send { pane_id: id.clone(), data: b"ls\r".to_vec() })?; // Capture a pane's current screen (optionally with/without ANSI). let snap = client.request(MuxOp::Capture(cap_req))?; }
To follow a pane's output continuously (rather than just grabbing a snapshot), use attach — it first hands you an atomic snapshot (terminal-mode preamble + scrollback history + sequence high-water mark), then turns into a streaming session where you loop on live output and can also inject stdin back:
#![allow(unused)] fn main() { let attached = client.attach(&id)?; // First feed the renderer in mode_preamble → history → buffered order to rebuild the screen let mut session = attached.session; while let Some(ev) = session.recv_output() { // AttachEvent::Output { seq, data } / Exited { exit_code } } }
Other common operations (all variants of MuxOp, verified against protocol.rs): Spawn / Respawn (atomic restart under the same id) / Resize / KillTree / Subscribe · Unsubscribe / ListThemes · SetTheme / KillServer. For the full list and the shape of every payload, see the next page: Wire Protocol Reference.
Honest boundaries: what's stable, what will change
Conmux's stability promise comes in two tiers, and this matters — verified against the crate::lib docs:
- ✅ The protocol layer is a frozen contract (committed). The wire protocol types (
MuxRequest/MuxOp/MuxPayload/MuxReply/MuxNotify, plus the closure of types they carry, such asPaneId/PaneSize/PaneState), thePaneHostfacade, the event surface, the injection extension points, the theme surface — during 0.x, any change to these must go through a minor version bump + CHANGELOG; patch releases will not break you. The sequence semantics (PaneOutput.seqstrictly monotonic per pane, starting from 1) are frozen alongside them. Building automation against the protocol layer is safe. - ⚠️ APIs outside the protocol layer are unstable and may change without warning before 1.0. Concretely: the
daemon,client,pipe, andwiremodules are all explicitly marked "Stability: unstable — may change without notice" in the source. In other words, the specific Rust method shapes above —Client::connect_or_spawn()/attach()— belong to the will-change tier: they work, Conflux itself uses them for real, but don't treat them as a frozen contract. If you want a long-term anchor, anchor on the protocol itself (the wire types), not on any particular client method signature.
The one-line rule of thumb: the protocol is the contract; the client API is the current implementation. When building automation, the closer you stay to the wire protocol, the harder it is for a future release to knock you over.
What's next
- Want the full list of operations, the fields on every request/reply, and what event frames look like? → Wire Protocol Reference
- Want to bring in your own agent framework? → Hooking Your CLI Up to conmux
Wire Protocol Reference
(This page is for people who want to hook up to the daemon from code, or who want to know exactly what bytes run over the pipe. If you only use the GUI or the CLI, you can skip it.)
The Conmux daemon and its clients (the CLI, the GUI, your own programs) exchange a stream of JSON frames over a named pipe. This page spells out the shape of those frames — every one maps to a real #[derive(Serialize, Deserialize)] type in crates/conmux/src/protocol.rs; this is not documentation invented from scratch.
Stability note: the protocol layer is the only public surface conmux promises to keep stable (the serde shape is the contract; breaking changes go through a minor bump + CHANGELOG). Every API outside the protocol layer is still unstable. Every type name below matches one-to-one against
protocol.rs/event.rs.
What Runs on a Connection: the WireFrame Envelope
Every frame is wrapped in an envelope enum, WireFrame, externally tagged (in JSON that's {"VariantName": {...}}). There are five kinds:
| Frame | Direction | Payload |
|---|---|---|
Hello | client → daemon | protocol_version: u32, client_kind: String |
HelloAck | daemon → client | protocol_version: u32, daemon_version: String |
Request | client → daemon | a MuxRequest |
Reply | daemon → client | a MuxReply |
Notify | daemon → client | a MuxNotify (async event, no correlation id) |
Directions are enforced, not a free-for-all: the daemon only accepts Hello (and only during the handshake) and Request; clients only accept HelloAck / Reply / Notify. Sending a frame the wrong way = protocol error, and the connection is dropped on the spot.
Handshake first: after connecting, a client's first frame must be Hello. client_kind is just a free-form label that goes into the audit log — it plays no part in any authorization decision, so don't expect to escalate privileges with it.
Versions Must Match Exactly
PROTOCOL_VERSION is currently 1, and it is independent of the crate version. During the handshake the daemon checks its own version against the protocol_version in Hello for strict equality — not "greater than or equal", but "must be identical". If they don't match, you don't get a HelloAck. Any breaking change to the wire shape must bump this constant.
Unknown Fields Are Rejected (deny_unknown_fields)
The WireFrame envelope, Hello / HelloAck, and MuxOp all enable deny_unknown_fields: one extra undefined key in a message and deserialization fails outright, instead of being silently ignored. The most important job this rule does is keeping injection sources out at the door — see Send below.
Requests: MuxRequest and the MuxOp Operation Enum
A request = a correlation id + one operation:
MuxRequest { correlation_id: u64, op: MuxOp }
correlation_id pairs replies with requests (the reply carries the same number back). MuxOp is the full set of operations you can issue, listed one by one below. The right-hand column is the MuxReply::Ok payload (a MuxPayload variant) that operation produces on success.
MuxOp variant | Fields | Success payload (MuxPayload) | Notes |
|---|---|---|---|
Spawn | SpawnRequest | Spawned(PaneId) | Spawn a new pane |
Respawn | SpawnRequest | Spawned(PaneId) | Atomic same-ID restart (closes the ID-reuse window between KillTree+Spawn) |
Send | pane_id, data (see below) | Sent | Inject input into a pane |
Capture | CaptureRequest | Captured(CaptureResult) | Grab scrollback |
Resize | pane_id, size: PaneSize | Resized | Change a pane's rows/columns |
KillTree | pane_id | Killed | Kill the entire process tree |
ListPanes | none | Panes(Vec<PaneState>) | List all panes |
Subscribe | pane_id | Subscribed | Subscribe to that pane's event stream |
Unsubscribe | pane_id | Unsubscribed | Cancel the subscription |
Attach | pane_id | AttachSnapshot { ... } | Atomic "subscribe + snapshot" |
ListThemes | none | Themes(Vec<TerminalTheme>) | List theme presets |
SetTheme | id: String | ThemeSet | Hot-switch the theme; also broadcasts ThemeChanged |
KillServer | none | ServerKillScheduled | Terminate the daemon and all sessions |
PinExecutable | path: String | Pinned | Pin an executable into the trust store |
UnpinExecutable | path: String | Unpinned | Remove a pin |
That's all 15 variants — no more, no fewer. MuxOp deliberately omits #[non_exhaustive]: adding a variant is an explicit minor-version decision, the daemon's dispatcher matches on it exhaustively, and a future new variant will fail to compile on the daemon side — forcing you to handle it explicitly instead of silently missing it.
About Send: the Injection Source Never Crosses the Wire
Send's data is raw bytes, base64-encoded on the wire (arrow keys, Alt combinations, binary pastes — non-UTF-8 content like that can't be carried losslessly in a plain string).
Note that Send has no source field — that's deliberate: the injection source is assigned at the receiving boundary based on channel identity, and clients are not allowed to self-report it on the wire. Combined with deny_unknown_fields, a Send message carrying a source key fails at deserialization — rejected, not accepted and then discarded.
About the Subscription Operations
Subscribe / Unsubscribe maintain the set of "which panes this connection cares about"; the daemon's fan-out (FanoutSink) uses it to deliver PaneOutput / PaneExited events only to connections that subscribed. Attach is the one-step "subscribe + atomic snapshot": it registers the subscription first, then takes a snapshot of the current scrollback, and from there feeds the live stream continuously with seq > last_seq — no dropped frames, no duplicated frames in between (rate limiting + per-pane concurrency=1 guard against snapshot amplification). This "fan-out delivery + seamless snapshot stitching" is implemented and tested in the current daemon (the M2 milestone has landed; see daemon.rs::attach_with_limits / FanoutSink). (SetTheme explicitly does not persist the theme preference — persistence belongs to higher-level consumers / the GUI shell, not the daemon.)
Replies: MuxReply and MuxPayload
Replies take one of two paths, both carrying a correlation_id for pairing:
MuxReply::Ok { correlation_id: u64, payload: MuxPayload }
MuxReply::Err { correlation_id: u64, error: ConmuxError }
Errors go through Err, carrying a ConmuxError (a mechanism-layer error, e.g. PaneNotFound) — conmux only reports its own mechanism-layer errors; it doesn't recognize conflux's semantic errors.
The success payload MuxPayload's variants are already mapped one-to-one in the right-hand column of the table above. Most are field-less acknowledgements (Sent / Resized / Killed / Subscribed / …). Two carry real data and are worth a closer look:
AttachSnapshot— the atomic snapshot forAttach. Fields:mode_preamble_b64(terminal mode preamble, e.g. alt-screen),history_b64(scrollback history),last_seq(last sequence number),pane_state. Client-side reconstruction = feed the preamble → feed the history → then feed the live stream continuously withseq > last_seq, nothing dropped, nothing duplicated.Captured(CaptureResult)— the result ofCapture, containing the base64 data, first/last absolute line numbers, whether it was truncated, and whether the buffer is "actually full".
MuxPayload does carry #[non_exhaustive] (the opposite of MuxOp), so consumers must keep a _ => arm when matching.
Async Events: MuxNotify
Events the daemon pushes to clients on its own initiative, wrapped in WireFrame::Notify, with no correlation id (they don't answer any request). Defined in event.rs; there are three:
MuxNotify variant | Fields | Meaning |
|---|---|---|
PaneOutput | pane_id, seq: u64, data (base64) | The pane's raw output; seq is a per-pane monotonic sequence number, for replay reconciliation |
PaneExited | pane_id, exit_code: Option<i32> | The process exited; when the exit code can't be obtained it's None, never faked as 0 |
ThemeChanged | id: String | Broadcast after the theme is switched via SetTheme, for live reskinning |
Same as Send, PaneOutput.data also travels as base64 on the wire (raw bytes may contain unprintable / non-UTF-8 content). In-proc directly-connected consumers (sink implementations) still receive the raw Vec<u8>; base64 only applies at the pipe boundary.
The monotonicity of seq comes with a hard rule: if a consumer or conmux coalesces PaneOutput frames, it may only concatenate — never drop bytes, and seq must stay contiguous — dropped frames leave the consumer making wrong decisions on incomplete output.
Honest boundary:
MuxNotifyonly emits events conmux knows for certain at the mechanism layer — bytes, exits, theme changes. It does not emit semantic states like "this agent is thinking / waiting on a permission request": that's an upper layer's (e.g. Conflux's) interpretation of the PTY content, and it doesn't belong to the protocol layer. Don't expect to read agent state directly off the wire.
Cross-Check at a Glance
To verify all of the above quickly, the #[cfg(test)] module at the bottom of protocol.rs is living documentation: all_ops_round_trip lists all 15 ops, wire_frame_all_directions_round_trip exercises the five envelope frames, and send_with_source_field_is_rejected_on_wire and hello_rejects_unknown_fields demonstrate both places where unknown fields get rejected. Before changing the protocol, make these tests go red first — it's the most reliable way to reconcile.
Hooking Your CLI Up to conmux
(This page is for people building agent frameworks / CLI tools. You have a command-line program of your own and want it to run inside conmux — and be watched over.)
First, a Straight Answer
The "one-click onboarding for any framework, auto-detection, auto-adaptation" scaffolding — conmux doesn't have that yet. Don't let anyone's pitch tell you otherwise. What conmux can give you today comes in two tiers, with a clear line between them:
- Any CLI can be launched as a supervised pane — this is the foundation that already works. Your program, someone else's program, PowerShell, tools inside WSL — all the same.
- A layer of deep observation for Claude Code sessions — this is currently the only framework with a dedicated adapter.
As for "you toss in any agent framework and conmux automatically recognizes what it is, automatically parses out its model / tokens / subtasks" — that's the direction, not the current state. To get that layer, someone has to write a dedicated adapter per framework, the way it was done for Claude. So this page comes in two parts: first the tier that holds for everyone, then the Claude-only tier.
Tier 1: Launch Any CLI as a Supervised Pane (Works for Everyone)
conmux doesn't care whether your program "is an agent" — it only knows processes, bytes, panes. To hook up your CLI, all the information you need fits in one command:
- program — the executable to launch (e.g.
my-agent.exe,node,python). - args — launch arguments (e.g.
--serve,run main.py). - cwd — working directory (optional; if omitted, the current directory is inherited).
In the GUI, these three together form a launch entry: a display name + one line of raw command text (including arguments) + an optional working directory. Two runnable entries come built in — Shell (powershell.exe) and WSL (wsl) — and you can add your own alongside them, edit them, delete them. The raw command text is split into program and args by "whitespace tokenization + double quotes to contain segments with spaces" (pipes, redirection, and variable expansion are not supported — those are beyond what a launch entry covers).
Here's what happens when you press launch: conmux starts your command directly as a real ConPTY process (not by pouring characters into some shell's stdin), so your CLI becomes a supervised pane. From that moment on, everything the earlier chapters described holds for it in full:
- Split panes side by side with other panes, and switch between a row of session dots;
- Close the client window and your process doesn't die; re-attach and the screen picks up exactly where it was (detach / attach);
- It — together with all the child and grandchild processes it forks — is held by a Job Object: when it's time to kill, the whole tree goes down clean, no orphans left behind;
- Every byte of input written to it goes through the same audited channel.
This tier doesn't discriminate by framework. As long as your agent CLI can run in a Windows command line, it can become a conmux pane and get the entire package above. This is the most solid part of what conmux can offer "any framework" today.
Want a more precise onboarding contract? How program / args / cwd flow into spawn, how launch entries are persisted, how commands are parsed — the GUI-side source of truth is
conmux-app/src/lib/launch-registry.tsandsessions.ts. For driving the kernel from code (framed protocol, stable pane ids, event stream), see Driving conmux from Code.
Tier 2: Deep Observation for Claude Code (Currently the Only Dedicated Adapter)
Beyond "launching as a pane", conmux does one more layer for Claude Code sessions — it can tell which model the session is running, how much context has been used, which subtasks were dispatched, and when it's waiting on you. This layer was written specifically for Claude; it's not a generic capability.
When you launch with a bare claude command (literally just claude, no arguments of your own) through conmux, conmux automatically does three things:
- Injects
--session-id <uuid>— pins a unique id on the session, which lets the observation side anchor precisely onto its log file, with no cross-talk from other historical sessions in the same directory. - Reads along the JSONL — Claude Code writes session content as JSONL logs; conmux reads them incrementally, parsing out structured information like model name, tokens, context usage percentage, and the sub-agent tree.
- Attaches a Notification hook — by temporarily writing a
--settingsfile (effective only for this session, and merged with, not overriding, your own global hooks), it picks up structured "needs you" signals like "waiting for you to approve a permission request / idle waiting for your input", covering the TUI permission-dialog cases that a terminal bell (BEL) can't catch.
A few boundaries, stated up front:
- Only applies to a bare
claude. The moment you pass any explicit argument (even your own--session-id,-c,-p), conmux won't touch a single character of your command — no injection, no hook. This is deliberate discipline: don't overstep and rewrite someone's launch. - Depends on the
claudeCLI supporting--session-id(verified supported on versions as of 2026-07); versions too old to recognize the flag will error out visibly in the pane, not fail silently. - Any step that fails degrades honestly. Can't write the settings file / can't get the directory? It falls back to "inject session-id only", or even just BEL + exit signals — it never pretends observation is working.
- Observation only reads what was actually printed / actually logged; if it can't get something, it leaves it blank (the UI shows
—). It never guesses, never fabricates a model name or activity state.
Which fields deep observation actually parses out, how active / stale is judged, where the sub-agent tree comes from — see What conmux Observes. The onboarding source of truth in code is
conmux-app/src/observe/(session-observer andparsers/claude.ts); hook construction lives insrc/lib/claude-hooks.ts.
So What About a "Generic Adapter Layer" — What's Missing, and Where It's Headed
To be honest about the gap:
- What exists today: any CLI → launched as a supervised pane (generic); Claude Code → deep observation (dedicated).
- What doesn't exist today: a generic scaffolding where "you toss it in and the framework type is auto-detected, with the matching observation / adapter applied automatically". The observation layer today is one hand-written adapter per framework — Claude has one, other frameworks don't yet.
- To make conmux recognize your framework, the path is the same one Claude took: write a parser / observation adapter for it. For the extension points involved — and the dividing line of "how far mechanism should reach, and what semantics should be left to whom" — see Mechanism vs. Semantics: Where the Boundary Is.
If you're building your own agent CLI and want conmux to watch over it properly on Windows — Tier 1 works right now, so feel free to hook up directly. If you want to push the generic adapter layer forward, or get your framework added to deep observation, you're especially welcome to open an issue / discussion, or just send a PR. Making "hook your agent CLI up to Windows with ease" actually true is exactly what this project most wants to build with you.
What conmux Observes
If you're building your own agent framework, the question you probably care about most is this: once I bring it into conmux, how much can conmux watch on my behalf? This page answers that honestly — which signals exist for any CLI, which ones only Claude Code gets, and one bottom line that never changes: conmux only observes what a program actually prints and actually writes to disk. When it can't get a value it shows "—"; it never makes up a number to look good.
One big premise first. conmux's observation runs inside the terminal window's graphical shell (conmux-app), where an "observer" hangs alongside the terminal renderer and subscribes to the same PTY output — it doesn't modify your program, doesn't inject code, doesn't guess what you're up to. It just reads.
What Any CLI Gets: PTY-Level Signals
Whether you hook up PowerShell, a build script, or someone else's agent CLI — as long as it's a normal command-line process, conmux gives you these:
- Activity (running / idle) — output within the last short window means
running; silence for longer than about 2.5 seconds flips it toidle. This isn't a judgment call like "it's thinking" — it's literally "has it printed anything to the terminal recently". - Process exit + exit code — when the process ends, conmux gets the exact exit code (the kernel-level
PaneExitedevent carries the real exit code, not an estimate). That session's status in the window changes toexitedaccordingly. - Terminal bell → attention — if the program prints a bell character to the terminal (BEL,
\x07), or the process exits, conmux marks that session as "worth a look". Both are real signals (the bell follows tmux'smonitor-bellsemantics), not heuristic guesswork. Switch to that session and take a look, and the mark clears — once you've seen it, it stops bothering you.
That's it. For non-Claude CLIs, conmux provides only this layer. It won't try to parse your program's model name, token counts, or internal state — it has no honest way of knowing them, so it simply doesn't make them up. Those fields in the observation state (model / tokens / activity) are always null for a plain CLI, and the UI shows "—".
Claude Code Only: Deep Observation
Besides printing to the terminal, Claude Code also writes the whole session to disk in structured form — a JSONL file — and can be configured with a notification hook. conmux understands both, so it can see much deeper into Claude sessions. This is Claude-exclusive — it relies on real data that Claude Code itself writes out. Other CLIs don't produce these on-disk artifacts, so they don't get this layer.
How does conmux recognize a Claude session? Either you tell it at launch (the launch command contains claude), or it sniffs Claude's stable markers from the terminal output (say, the terminal title being fixed at ✳ Claude Code, or that Using Opus 4.8 (1M context) line). Only once it's identified does conmux lazily start the sources below — non-Claude sessions never touch them.
From reading the JSONL, you get:
- Model name (model) — taken directly from
message.modelin the session transcript (e.g.claude-opus-4-8). It's the literal ground truth, not scraped from the terminal. - Context usage (contextPct) — computed from the
usageof the last real assistant message:input + cache_read + cache_creationdivided by that model's context window (1M for Opus / Sonnet, 200K for Haiku; if the model isn't recognized, no guessing — it shows "—"). - Cumulative session tokens (Σ input / output) — the usage of all real messages across the session, summed up.
- Subagent tree — when Claude dispatches subagents, it leaves
tool_useentries in the transcript (Agent, orTaskin older versions) with asubagent_typeand a description; when the matchingtool_resultcomes back, that subagent is marked done. conmux renders this as a single flat level of subagents (Claude's main agent → subagents is exactly one level; it doesn't fabricate deeper nesting). - Running workflow / recently invoked skill — read from the
tool_useentries and labeled as-is.
Honest boundaries (a few gotchas, all documented in source comments): ① These fields are only written once the session has seen a real assistant message (with real usage — not a
<synthetic>empty message produced by an interruption); otherwise they stay at "—" rather than fobbing you off with a fake 0. ② These fields come from the JSONL — most Claude versions do not print token counts to the terminal, so don't expect to scrape them from terminal text; the deep data's source is that on-disk file. ③ Reading the JSONL requires knowing the session's working directory (cwd) first; until the cwd is available, deep observation explicitly tells the UI "can't read this yet" instead of silently showing "—" forever and leaving you baffled.
The Notification hook, driving more accurate attention:
Claude Code's Notification hook can signal at two moments — a permission request prompt pops up (permission_prompt) and Claude asks you something while idle (idle_prompt). conmux takes these events in, and when the type is on the registered list, it marks the session as needing attention. This tracks reality better than a plain terminal bell — it lines up precisely with the real moment when "Claude is stuck waiting for your approval / your answer".
One more piece of honest labeling around the hook: conmux only lights up "deep awareness active" after it has actually received a hook event and confirmed that pipeline works. It does not make the reverse claim that "unlit means the hook is broken" — maybe this session just hasn't triggered a permission prompt or an idle question yet. Positive labeling only, no negative inference.
The One-Line Wrap-Up
- Any CLI: activity, exit codes, and bell- / exit-triggered attention. Enough for you to keep a whole row of sessions under watch.
- Claude Code: a layer of structured deep observation stacked on top — model, context, tokens, the subagent tree, plus hook-driven precise attention.
- The bottom line stands: observation = reading what it actually prints and actually writes to disk. When it can't read something, it honestly shows "—" — never speculating, never filling in fake numbers.
To bring your own agent CLI in, get it at least the PTY-level observation, and even leave hooks for conmux to recognize it, see Hooking Your CLI Up to conmux.
Mechanism vs. Semantics: Where the Boundary Is
If you're building your own agent framework and want to bring conmux in, this is the page to read first. It's not about any particular API — it's about a deliberately hard-drawn boundary. Understand it, and you'll understand why conmux is worth using as a foundation, and which jobs it deliberately refuses to do for you.
In one sentence
conmux only knows about panes, processes, and bytes. It has no idea what an "agent" is.
All those concepts you've heard elsewhere — which agent is waiting on you, who should move to the front of the attention queue, how multiple agents collaborate, which light should turn on in the dynamic island — conmux recognizes none of them. To conmux, whether a pane is running Claude Code, cargo build, or a plain PowerShell makes no difference whatsoever: each is a supervised process tree that emits bytes and occasionally needs you to feed it input.
This isn't "not built yet." It's deliberately not built.
Why draw this line on purpose
This is exactly what separates a "foundation" from a "framework."
A framework nails the semantics down for you: it assumes you're building agents, assumes how agents should queue, how they should talk to each other — going along with its assumptions is convenient, but the moment your ideas diverge from its, you're fighting it.
A foundation provides only mechanism and leaves the semantics blank: it gives you solid ground (processes don't run wild, input gets audited, dropped connections can be reattached), and as for what you build on top — an agent workbench, a CI dashboard, a multi-session ops tool — it doesn't decide for you, and therefore never gets in your way.
conmux chose the latter. It takes the dirty, hard job of "how a CLI session gets reliably hosted, supervised, and reconnected after a disconnect" and does it all the way down, then stops right where semantics begin. The sentence "this pane is an agent waiting on a permission request" is one conmux cannot say — because "agent" and "waiting on a permission" are upper-layer words, to be defined by the consumer.
This is also why, in conmux's public API, you can search all you want and never find a single type with
Agentin its name. What's exposed isPaneHost, protocol messages (MuxRequest/MuxReply/ …), event outlets (PaneOutput/PaneExited), injection hooks, and themes — all vocabulary from the "pane / process / bytes" layer. (For the concrete type names, see Driving conmux from Code.)
So who does the semantics — you
The other half of the boundary is the space left for you. The semantic layer — "what an agent is, how multiple agents collaborate" — conmux never touches; it's handed entirely to the layer above. Conflux is exactly such a layer: on top of the panes / events / injection that conmux provides, it defines its own concepts like "this pane is an agent," "this agent popped a permission request and the user should be alerted," and "broadcast this user message to these agents."
In your case, the two sides of the boundary divide roughly like this:
| conmux handles (mechanism) | You handle (semantics) |
|---|---|
| Spawn the process, supervise the whole process tree, hand you an accurate exit code when it dies | Decide "this pane represents an agent / a build / a service" |
Deliver the bytes a pane emits to you in order (PaneOutput) | Read from the bytes that "it's waiting for my input," "it errored," "it finished" |
| Provide the single, auditable input channel and deliver the bytes you feed into the PTY | Decide "when to feed, what to feed, whether it should pass a policy check first" |
| Reattach the screen exactly as it was on reconnect | Decide "what the UI looks like, who goes first in the attention queue" |
In one sentence: conmux guarantees the ground beneath is flat, stable, and watchable; what gets built on it is up to you.
The dependency is one-way (please keep it that way)
This boundary is a hard constraint in the code, not a verbal agreement: the dependency direction is always your framework → conmux, never the reverse. conmux depends on no upper layer — it doesn't depend on Tauri, isn't aware of any UI, and certainly doesn't recognize any specific framework's business concepts.
The benefit to you is direct:
- Swap out the entire upper layer (a different UI, a different interaction model, even a different language to drive it), and the conmux ground beneath doesn't have to move.
- When conmux ships a new version, what it promises to keep stable is only that small, well-defined public surface (the protocol types + a few core traits); everything else in the internals is explicitly marked "unstable, may change at any time." Write against the public surface, and upgrades are far less likely to be rattled by internal refactors. conmux calls this principle "the library is the product" — a small, stable public surface beats a big all-inclusive one.
The one thing to remember
conmux gives you flat, stable, watchable mechanism; the semantics — "is this an agent, how should they collaborate" — are your job, and your freedom.
This boundary isn't a missing feature; it's design restraint — precisely because it stops where semantics begin, you have room to build your own tower.
Next up
- Want to see how to drive it from code (real type names, what requests/events look like) → Driving conmux from Code
- Ready to hook your CLI up → Hooking Your CLI Up to conmux
FAQ
Here are the questions people ask most often, in Q&A form, in plain language. Anywhere something is "not done yet," we say so — no hedging.
Do I need WSL first?
No. Conmux runs directly on Windows' own terminal foundation (ConPTY), and every pane is a real Windows pseudoterminal. Install it and you can run PowerShell, cmd, and any native command-line program — no Linux subsystem layered underneath.
What about tools that live in WSL? You can bring them in — run wsl.exe <your tool> directly in a pane, and Conmux takes it over, supervises it, and reconnects it after a disconnect just like any ordinary command. This path works today.
Honest note: Supervising Windows-side and WSL-side processes unified in a single session tree (e.g., gracefully shutting down WSL processes across the boundary, automatic path translation) — that layer is not built yet; it's M4 on the roadmap. Today you can run WSL tools in a pane, but that "everything treated equally under one unified supervisor" story is still on the way.
How does it relate to tmux?
Conmux is a tmux-like thing — a terminal multiplexer (multiple sessions in one window, split panes, reconnect after disconnect). But it is not a port of tmux, and not a drop-in replacement:
- tmux was not built for Windows, and upstream has explicitly said it won't do native Windows; to use tmux on Windows, you have to crawl into WSL first.
- Conmux is Windows-native from the ground up — it runs on ConPTY, with no Unix emulation layer.
So tmux config files, .tmux.conf, and that whole set of shortcuts don't carry over. Conmux has its own (in the GUI the default prefix is Ctrl+B — deliberately matched to tmux for the sake of veterans, but the implementations underneath are two different things). If you're a tmux veteran, the fastest path is Differences at a Glance.
Is it a memory hog?
Depends on which layer you use — different answers, and here's the honest version:
- Conmux with the graphical interface (conmux-app): it uses Tauri plus the system's built-in WebView2. This is not the most extreme lightweight option; the memory floor is roughly a few hundred MB. It's far leaner than "stuffing your agents into a full IDE (VS Code plus a pile of extensions)," but if "extreme memory frugality" is the bar, it isn't that.
- The pure command-line
conmux(the GUI-less crate / CLI): very light. It's a pure-Rust mechanism-layer kernel with a deliberately tiny dependency tree — no editor, no browser-engine blob.
In one sentence: chasing lightweight and living entirely in the command line → use the pure CLI conmux; want visual split panes, session dots, and deep observation panels → use the GUI shell and accept the few-hundred-MB floor.
Does it support macOS / Linux?
No, and that's by design, not for lack of time. Conmux is Windows only: its entire reason to exist is "bring tmux-class capabilities to native Windows" — ConPTY pseudoterminals, Job Object whole-tree supervision, named-pipe IPC — all of it Windows platform machinery. On other systems, you already have tmux / Zellij.
Technically: the GUI shell compiles and runs only on Windows (Win10 1809+ / Win11). The conmux crate's pure logic layer compiles and tests cross-platform (for development convenience), but the ConPTY / Job Object backends that do the real work compile only on Windows.
How does it relate to Conflux?
In short: Conmux is the foundation, Conflux is the building on top of it.
- Conmux understands exactly three things — panes, processes, and bytes. It has no idea what an "agent" is, and doesn't want to. That boundary — "mechanism only, never semantics" — is precisely what qualifies it to be a foundation. It's a standalone product: download Conmux on its own and what you get is a terminal multiplexer, nothing more.
- Conflux is built on top of Conmux — a multi-agent CLI supervision console (GUI) that adds the "agent" semantics back in: who's waiting on you, who popped a permission request, and how to arrange multiple agents.
So: want a Windows-native terminal-multiplexing foundation → Conmux; want visual multi-agent supervision on top of it → Conflux.
It's unsigned — is it safe to install?
First, the current state of things: the GUI installer will be unsigned — this is a student open-source project with no budget for a code-signing certificate. Windows SmartScreen will warn "Unknown publisher." You can click "More info → Run anyway," or just build from source yourself. (The pure CLI conmux installs via cargo install --locked conmux, which never triggers that warning at all.)
Why you can be at ease:
- The source is fully open — dual-licensed MIT / Apache-2.0; you can read all of it, audit it, and
cargo build/npm run tauri:buildyour own hand-compiled copy. - No local telemetry — no accounts, no analytics reporting, no data ever leaves your machine. Connection-level auditing (who connected to the daemon) is written only to one local rolling log,
%LOCALAPPDATA%\conmux\daemon.log, and never sent anywhere. - The trust boundary is "the current user" — the daemon runs over named pipes, with a DACL that admits only your own account (SID) and rejects remote clients.
Honest note: The identity checks at the named-pipe layer (client fail-closed; client verifying the daemon's process image via Authenticode) exist to "raise the bar and stay auditable" — they are not there to defend against malicious code already running under your own account. Any program running as you can already read your memory and kill your processes. That's the operating system's boundary, not something Conmux can patch over for you. We lay this out plainly rather than pretend otherwise.
Still have an unanswered question? Feel free to open an issue or discussion and help us make this FAQ more complete.
Troubleshooting
Won't install, clicks do nothing, the command line spits out a wall of errors you can't parse — this page lists the pitfalls people hit most often with Conmux, laid out as "Symptom → Why → What to do". Just jump to the one you're hitting.
If your problem isn't here, or the fix doesn't work, open an issue and tell me — include your Windows version and the exact error text; that makes it much easier to track down.
"Windows protected your PC" when installing the GUI (SmartScreen · unknown publisher)
Symptom: You double-click the Conmux GUI installer and Windows pops up a blue dialog saying "Windows protected your PC" and "Unknown publisher", with only a "Don't run" button.
Why: This is the standard treatment for an unsigned program — not a virus, not a broken install. Digitally signing a program means buying a code-signing certificate, and Conmux is a student's open-source project with no budget for one — so SmartScreen doesn't recognize the publisher and blocks it first. Details in the FAQ's unsigned-build entry.
What to do:
- Click "More info" in the blue dialog.
- A "Run anyway" button appears at the bottom — click it.
You only have to do this once; after that everything behaves normally. If the dialog still makes you uneasy, you can build from source yourself — see Install It — the conmux command-line tool installs via cargo install and never touches this flow at all.
GUI won't open / white screen (missing WebView2)
Symptom: The install finished, but Conmux's graphical interface won't start, or opens to a blank white screen.
Why: The Conmux GUI shell relies on the system's built-in WebView2 to render its interface (which is also why its memory floor sits at a few hundred MB and it isn't "ultra-lightweight" — see What It Is & Why It's Worth Using).
- Windows 11: WebView2 ships with the system; usually nothing to worry about.
- Windows 10: Some older machines don't have it preinstalled, which gets you the white screen or a failure to start.
What to do: On Win10, download the "Evergreen WebView2 Runtime" from Microsoft's official site, install it, then relaunch Conmux. Once it's installed the GUI picks it up on its own — no extra configuration.
The command-line-only
conmuxtool doesn't need WebView2 — if you only use the CLI, this one doesn't apply to you.
Pressing Ctrl+B does nothing (the leader key is a two-step gesture)
Symptom: You want to split panes or switch panes, you press Ctrl+B, and nothing happens on screen.
Why: This is most likely normal. Conmux keeps tmux's two-step leader gesture: Ctrl+B is just the knock on the door — it does nothing by itself. You have to release it first, then press a second key to trigger an action.
Ctrl+B then press \ # split vertically
Ctrl+B then press - # split horizontally
Ctrl+B then press ← ↑ ↓ → # move focus between panes
Ctrl+B then press z # zoom the current pane to full screen (press again to restore)
After you press Ctrl+B, the status bar lights up a ⌨ LEADER badge that means "waiting for your second key" — if you see it, the leader was received.
If even the badge doesn't light up:
- Make sure focus is in the Conmux window (and some other program isn't grabbing the key).
- Historical pitfall: the leader key used to default to
Ctrl+Space, but on Chinese Windows the IME's global Chinese/English toggle hotkey swallows it before Conmux ever sees it — so since v0.1 the default has been changed toCtrl+B. If you're on a very old build andCtrl+Spacedoes nothing, just upgrade to a newer version. - Want a different leader key? Reconfigure it via "Set leader prefix" in the command palette (the leader must include
CtrlorAlt; bare keys are rejected, so your everyday typing doesn't get swallowed as a leader).
Two steps feel like a chore? There's an off-by-default leader-free direct-shortcut mode (
Ctrl+Alt+\/-/Z, plus vim-styleCtrl+Alt+H/J/K/Lto switch panes). It ships off so it never steals a single keystroke until you turn it on yourself.
Tools from WSL won't start
Symptom: You try to hook up a command from WSL as a pane, but the pane exits as soon as it opens, or you get errors like "wsl not found" or "no such distribution".
Why: Today's Conmux directly invokes the wsl.exe on your system to start that pane — it doesn't bundle WSL, and it hasn't built "unified supervision across WSL" yet (that's on the M4 roadmap, see Differences at a Glance). So failures like these are almost always a problem with wsl itself, unrelated to Conmux's split panes or supervision:
- WSL isn't installed at all — run
wsl -l -von its own in a terminal; if that errors too, you need to install WSL first (Microsoft's officialwsl --install). - Distribution name mismatch — the distribution name you specified (say
Ubuntu-22.04) doesn't match what's actually installed.wsl -l -vlists what you really have; fill in the name exactly as listed.
What to do: First get that wsl command running in a plain terminal (install WSL if it needs installing, fix the distribution name if it needs fixing). Once it runs on its own, bring the same command into Conmux and it'll just work.
cargo build fails with schannel / CRL / certificate revocation errors
Symptom: When building from source, or running cargo install --locked conmux, cargo gets stuck fetching dependencies and reports errors mentioning schannel, CRL, or certificate revocation.
Why: This is an old Windows networking ailment, not a Conmux problem. Windows's schannel is strict about certificate revocation list (CRL) checks, and on corporate networks / behind a proxy it often can't fetch the CRL — so the whole TLS handshake fails.
What to do: Tell cargo to skip the revocation check — set an environment variable and retry:
$env:CARGO_HTTP_CHECK_REVOKE = "false"
cargo install --locked conmux
To make it permanent, add CARGO_HTTP_CHECK_REVOKE=false to your system environment variables. Switching to a Chinese mirror sometimes dodges it too, but that only masks the symptom — the switch above is aimed at the root cause.
Still stuck after reading all this? Send the exact error text + your Windows version (Win10/Win11) + whether it's the GUI or the command line to an issue, and I'll follow up as soon as I can.
About Conflux
Conmux is the foundation; Conflux is the building on top of it.
If conmux answers "how do multiple command-line sessions run on Windows, and stay watchable," Conflux tackles something higher-level and more concrete: you have three or four AI coding agents running at once — how do you keep from drowning in them?
In one sentence: conmux manages panes and processes; Conflux manages agents. conmux has no idea what an "agent" is — it only knows panes, processes, bytes. Semantics like "this is a Claude Code session and it's waiting for you to approve a permission" are Conflux's job.
The problem it wants to solve
You run agent CLIs like Claude Code, Codex, Aider, and OpenCode on Windows. One runs fine; three or four at once become unwatchable — you can't stare at four terminals simultaneously, flipping through each one to figure out who's stuck, who's waiting for you to click confirm, who's already finished.
Conflux's design goal is to make sure you don't have to babysit each one: surface the one that needs you right now, let you jump back with one click to the moment it asked its question, and record everything that happened while you weren't looking into an audit trail.
It is not yet another chat window. Agent work here is treated as real sessions, cards on a canvas, attention signals, permission requests, and a replayable event timeline.
What it looks like, what it can do
Conflux is a Tauri 2 + React Windows desktop workbench running on the conmux kernel. The core boils down to a few things:
- Multiple agents, one canvas — open multiple real CLI agents at once, each a live PTY session (ConPTY + xterm.js rendering, full ANSI/colors), laid out as cards on the same canvas — arrangeable, expandable, collapsible. Not fake cards, not read-only mockups.
- An attention surface that comes to you — dynamic island + sidebar + system tray, delivering each agent's status right to your eyes. For Claude Code sessions, "this one is waiting for you" comes from an authoritative hook signal, not screen-scraping guesses — so it's a real event, not an estimate.
- One-click jump back to the trigger point (jump-back) — click a notification and Conflux takes you straight to that split pane, straight to the spot that triggered it, no digging through a pile of terminals.
- Intervene without breaking flow — expand any card into a two-way terminal, type directly into that agent's session, then collapse it back into the grid when you're done.
- The gate stays in your hands (permission approval) — agents' permission requests surface as an approval UI; nothing proceeds without your sign-off.
- Everything leaves a trace — every event is written to local SQLite, and session timelines can be replayed after the fact.
Straight talk: the core that works, and the ideas still being shaped
This is the part of the page that most needs to be said clearly. What actually works and is stable in Conflux is this one chain:
Run multiple real CLI agents → observe every session → attention signals surface what needs handling → you approve / jump back with one click → everything written to SQLite audit, replayable.
That "supervision console" chain is real. But how agents collaborate with each other, how they get orchestrated — that set of ideas is still being shaped. Here's what is not built right now, stated plainly:
- The discussion panel is one-way broadcast, not collaboration. It does exactly one thing: take one prompt from you and broadcast it once to multiple agents.
- Agents do not talk to each other. They will not funnel their replies into a shared chatroom, will not converse with one another — after the broadcast goes out, each runs on its own.
- There is no automatic orchestration engine. There is no scheduling brain that "decides on its own who should do what." The decision-making stays with you — Conflux supervises agents, but it does not orchestrate them for you.
In other words: the supervision side is genuinely wired through; the collaboration / orchestration side is still a direction, not the current state. Don't mistake the roadmap for shipped features.
The attention surface consists of the two-state dynamic island + sidebar + system tray; the floating ball (Float Ball) from early designs has been removed and is not part of it.
Current status
- V1 · Windows only · early. It is a usable workbench, not a finished product.
- Runs directly on ConPTY, no WSL dependency, no Unix-like compatibility layer.
- No prebuilt / signed installer yet — for now, please build from source (
git clone→npm install+npm run tauri:buildunderconflux-app). The installer, once shipped, will be unsigned — there's no budget for a code-signing certificate, so SmartScreen will warn "unknown publisher"; click "More info → Run anyway".
Still moving forward on the roadmap (roadmap, not current state): prebuilt / signed installers, broader adapter coverage with simpler configuration, hardening of the attention and permission layers, timeline / audit experience polish.
How Conflux and Conmux relate
conmux— a standalone Rust crate: Windows terminal multiplexing + agent-isolated runtime (ConPTY, whole-process-tree supervision, a single audited input path…), no Tauri dependency. It's the star of this manual, and it works on its own.conflux-app— the Tauri 2 + React product layer built on top ofconmux(canvas, attention surface, permission UI, audit / timeline).
So the manual you're reading right now is about the foundation, conmux; Conflux is currently that foundation's largest consumer, and a living example of why the "mechanism vs. semantics" boundary deserves to exist. To dig into Conflux itself, head to its project repository.
Conflux and Conmux are both open-source projects built by a student at South China Normal University, developed in the open. Got ideas or hit a bug? Head to the corresponding repo to file an issue / open a discussion / send a PR — let's make the agent ecosystem on Windows better together.