Docs / Tools & Frameworks / amux

Tools & Frameworks

amux

A session manager for coding agents: runs each agent in its own git worktree inside tmux, with a web dashboard to watch and steer them all.

Updated Aug 18, 2026

amux runs coding-agent sessions on a machine. Each session is a named, long-running agent in its own git worktree, living inside tmux, with a web dashboard that shows every session at once. It is the per-machine layer of the multi-machine stack : one amux per machine, optionally tied together by hcom .

It is deliberately small — a launcher script, a Python dashboard server, and a remote-control script — so there is little to break and nothing to learn beyond a handful of verbs.

What it gives you

  • Isolated sessions. Each agent works in its own git worktree, so several can edit the same repository in parallel and you review their branches separately.
  • Persistence. Sessions live in tmux, so they keep running after you detach, close the terminal, or drop the SSH connection. Reattach later, right where it was.
  • A dashboard. amux serve puts every session — output, status, controls — on one web page.
  • Remote control. From a hub machine, amux-remote drives any other machine’s amux over its HTTP API. See connecting machines .

Install

amux is three files that must sit in the same directory on your PATH (the launcher finds the server next to itself):

git clone --depth 1 https://github.com/mixpeek/amux ~/amux-src
install -m 755 ~/amux-src/amux ~/amux-src/amux-remote ~/amux-src/amux-server.py ~/.local/bin/
amux --version

Needs tmux, git, and python3 present, plus at least one coding agent (Claude Code , codex, gemini, or opencode ) installed and signed in.

Core commands

CommandDoes
amux register NAME --dir DIR [agent flags]Define a session (no start).
amux start NAMEStart it (or attach if already running).
amux exec NAME --dir DIR [agent flags]Register and start in one step.
amux attach NAMEAttach to a running session. Detach: Ctrl-b then d.
amux lsList sessions; * marks running ones.
amux ls -cCompact one-screen grid (NN ● name).
amux peek NAME [lines]Print recent output without attaching.
amux send NAME "text"Send a line of input to a session.
amux stop NAME / amux rm NAMEStop / remove a session.
amux serve [port] --bind H[,H]Start the web dashboard.

Anything amux doesn’t recognize on register/exec is passed straight to the coding agent, so you use the agent’s own flags (--model, --continue, --append-system-prompt, …). The two helper scripts — amux-run and amux-attach — wrap the two things you do most.

Long lists get noisy — amux ls -c prints a compact one-screen grid, and amux ls | less -R pages the full list while keeping colors.

Providers — which agent a session runs

Each session runs one coding agent, and amux supports four. Pick it with --provider on register / exec (persisted with the session), change it later with amux provider NAME <p> (with the session stopped), or override a single start with amux start NAME --provider <p> (not persisted). The remote mirror is identical: amux-remote BOX exec NAME … --provider <p> and amux-remote BOX provider NAME [<p>].

ProviderBinaryResume semanticsDefault modelFull-access flag
claudeclaudeAuto-resumes the newest conversation for the directory.sonnet (or your defaults)--dangerously-skip-permissions (--yolo)
codexcodexcodex resume <id>; the id is captured after the first start, else the newest rollout for the directory.gpt-5.5--dangerously-bypass-approvals-and-sandbox
geminigemini--session-id minted at first start, --resume <id> after.auto--yolo
opencodeopencode-s <id>; captured after the first start, else the newest session for the directory.first local Ollama model--auto

A missing provider binary fails loudly at amux start (a clear error naming the binary) instead of leaving a dead tmux pane, and amux --help shows which providers are installed on the current machine.

opencode is the local-model lane

When a session’s provider is opencode, amux resolves a provider/model from ~/.config/opencode/opencode.json (or AMUX_OPENCODE_MODEL), verifies the endpoint is local — loopback or a private-network address serving Ollama , not a hosted API — and refuses to start otherwise, in both the CLI and the dashboard server. Point opencode at a model you serve yourself: see opencode and Local and frontier models .

Accounts — which Claude subscription a session bills to

If you hold more than one Claude subscription (a personal plan and a team or organization plan is the usual pair), a claude session can be pinned to one of them. amux calls the two slots personal (P, the default) and team (T). Each slot is a small, mode-600 file that exports the long-lived token Claude Code reads (CLAUDE_CODE_OAUTH_TOKEN, minted with claude setup-token) plus a CLAUDE_ACCOUNT marker naming the slot:

SlotFileMeaning
personal (P)~/.config/claude-code/envThe default. May be absent on machines that sign in through the browser instead.
team (T)~/.config/claude-code/env.teamThe second subscription. Required on a machine before a T session may start there.

The session stores its slot and sources the matching file every time it starts, so the choice survives restarts and is independent of whatever the launching shell happens to hold. Only claude sessions (and the iTerm2 tab variant, which also runs claude) have an account; for codex, gemini, opencode and other providers --account is accepted and ignored.

CommandDoes
amux register NAME --provider claude --account teamPin the account at creation (exec too). P / T work as short spellings.
amux account NAMEShow a session’s account.
amux account NAME TChange it (session must be stopped; the next start uses it).
amux start NAME --account TOne launch on the other account, stored value untouched.
amux-remote BOX exec NAME … --account T / amux-remote BOX account NAME [P|T]The same on another machine.

When you create a session without --account, it inherits the launching terminal’s identity: if your shell has sourced one of the slot files, CLAUDE_ACCOUNT names it, and that becomes the session’s stored value; otherwise personal. A tiny shell function that sources one file or the other is all it takes to switch a terminal.

Everywhere sessions are listed, the account is visible. amux ls and amux ls -c prefix every session with its account and provider: P:cc:name or T:cc:name for claude (P:it: for an iTerm2 claude tab), and a bare provider code for the agents that have only one login, cx: codex, oc: opencode, ag: antigravity, gm: gemini. amux info and the dashboard show it too, and the status line inside the pane can carry a P / T badge. A session whose slot is not provisioned on that machine refuses to start rather than quietly billing the other subscription.

Full access, and the sandbox each agent starts in

Left alone, the agents ask before doing anything consequential, and codex additionally runs its shell inside a sandbox. Switching that off is the agent’s own flag — the last column of the table above — and since register / exec pass unknown flags straight through, it rides on the session:

amux exec build --provider codex --dir ~/src --dangerously-bypass-approvals-and-sandbox
amux exec ui    --provider claude --dir ~/src --dangerously-skip-permissions

The flag is persisted with the session, so every later amux start keeps it. Register the session without it to get the agent’s normal, guarded behavior back.

A middle ground for codex: permission profiles

codex (0.147) reads permission profiles from its config — [permissions.<name>] blocks with per-path read / write / deny rules, extends for a base, and network.enabled — and you select one with -p <name>. That is the setting between “approve every command” and “no sandbox at all”: broad filesystem access for the work, with credential files (~/.ssh, cloud tokens, .netrc, keyrings) explicitly denied.

Two things worth knowing before you write one: extends accepts only :read-only and :workspace — there is no full-access base to subtract from, so you extend :workspace and widen it explicitly — and a narrow read rule does not re-open a path under a denied parent, so a directory holding both secrets and files the agent needs (~/.ssh is the usual one) has to be spelled out file by file rather than denied wholesale.

Companion commands

A set of small wrappers rides alongside amux for working across many sessions and machines: amux-remote (drive a named box over its API), amux-all (one list across every machine), amux-up (start if needed, then attach), ccjump (fuzzy-jump to any active session, local or remote), amux-rename, amux-convos, amux-reauth, check-claude-auth, amux-tabs, plus amux-config (manage the per-machine configs) and amux-doctor (is this machine actually set up right?).

They ship in the public companion repo — download the package or clone amux-nui-public (opens in new tab) and run ./install.sh to get them all (including an enhanced amux-remote, symlinked into place). amux-tools has the full table and install details; Connect machines walks through pointing them at your machines.

amux ls only ever shows the box you’re on, so a remote listing should say which machine it’s from. amux-remote <box> ls leads with that, and -c gives the same compact grid as amux ls -c:

$ amux-remote myhub ls -c
myhub (remote) · 8 sessions · 3 ● running
https://100.x.y.z:8822  · ssh myhub

 1 ● webapp   4 ○ docs      7 ○ infra
 2 ● api      5 ○ scratch   8 ○ notes
 3 ● worker   6 ○ archive

amux-all stacks that view for every configured machine — local first, then each remote — so one command shows the whole fleet, compact by default.

Two behaviors that trip people up

Remote start attaches only when interactive

From a terminal, amux-remote BOX start NAME starts (or no-ops on) the session on that box and then attaches you over SSH. From a script, a pipe, or with --no-attach, it starts only and prints the attach command instead — so automation never grabs your terminal. amux-remote BOX attach NAME attaches on its own at any time.

codex has no --effort flag

Reasoning effort on codex is a config override, not a flag: -c model_reasoning_effort=medium. Plain codex --effort medium fails with unexpected argument '--effort' found (0.147), and so does codex resume. Since amux puts your flags on the agent’s command line verbatim, a codex session registered with --effort dies the moment it launches — with the agent’s error, not amux’s. Register it with -c model_reasoning_effort=<low|medium|high> instead.

--continue is dropped on an already-running session

amux start NAME --continue only takes effect when the session is not already running. If it is, start short-circuits to “attach” and silently drops the flag. Stop it first, or resume from inside the session with the agent’s own --resume.

Resuming after a restart

A session’s metadata records which conversation to resume, so a restart picks up where it left off instead of starting blank. If the recorded target is stale or fails to launch, start falls back automatically — first to the newest substantive conversation on disk for the working directory, then to a fresh start — so a plain start recovers context on its own. This also covers the case where the pane’s shell was exited manually (the tmux pane is left dead): start detects the dead pane, replaces it, and resumes normally. Nothing is ever deleted: a lost conversation can still be recovered with the agent’s own --resume ID run in that directory.

If a start truly fails, the error now includes the last lines the pane actually printed — the agent’s own error message — instead of a bare “failed to start”.

To see the conversations and pick the right one deliberately, use amux-convos NAME — Find and resume the right conversation covers it, along with the trap where a resumed session shows an old 401 / Please run /login from its transcript that looks like a live auth failure but isn’t.

The dashboard

amux serve 8822 --bind 127.0.0.1,<private-IP>

Never bind to 0.0.0.0

amux serve defaults to every interface, including any public one. Always pass --bind with loopback plus the machine’s private (tailnet ) address, so the dashboard is reachable only from your private network. The dashboard’s token (in ~/.amux/) is its password — keep it private.

It serves a self-signed certificate by default; point it at a real certificate (for example one from tailscale cert) for a clean green lock. Run it from a service manager (systemd / launchd) so it comes back after a reboot.

Where things live

~/.amux/ holds the session database, the dashboard token, TLS certificates, and per-session state. Back it up if the sessions matter; never commit the token anywhere public.

Next

Source: content/tools/amux.md · maintained in the nuilab-agenticai repository.