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.
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 serveputs every session — output, status, controls — on one web page. - Remote control. From a hub machine,
amux-remotedrives 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
| Command | Does |
|---|---|
amux register NAME --dir DIR [agent flags] | Define a session (no start). |
amux start NAME | Start it (or attach if already running). |
amux exec NAME --dir DIR [agent flags] | Register and start in one step. |
amux attach NAME | Attach to a running session. Detach: Ctrl-b then d. |
amux ls | List sessions; * marks running ones. |
amux ls -c | Compact 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 NAME | Stop / 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>].
| Provider | Binary | Resume semantics | Default model | Full-access flag |
|---|---|---|---|---|
claude | claude | Auto-resumes the newest conversation for the directory. | sonnet (or your defaults) | --dangerously-skip-permissions (--yolo) |
codex | codex | codex 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 |
gemini | gemini | --session-id minted at first start, --resume <id> after. | auto | --yolo |
opencode | opencode | -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
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:
| Slot | File | Meaning |
|---|---|---|
personal (P) | ~/.config/claude-code/env | The default. May be absent on machines that sign in through the browser instead. |
team (T) | ~/.config/claude-code/env.team | The 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.
| Command | Does |
|---|---|
amux register NAME --provider claude --account team | Pin the account at creation (exec too). P / T work as short spellings. |
amux account NAME | Show a session’s account. |
amux account NAME T | Change it (session must be stopped; the next start uses it). |
amux start NAME --account T | One 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
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
-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
- Hands-on from scratch: Run agents on one machine .
- Tie machines together: hcom and Connect machines .
Source: content/tools/amux.md · maintained in the nuilab-agenticai repository.