This file is regenerated by the cmux app; `cmux vm --help` and the authored skill reference are authoritative when rollout versions differ. --- name: cmux-cloud-vm description: "Route work to cmux Cloud machines from the plain `cmux vm` CLI (alias `cmux cloud`): route/run/agent pick a machine, `vm tree` and `surface ls` catalog This Mac and cloud surfaces, and open, exec, transfer, workspace, terminal, port, checkpoint, and domain operations share the same app paths. Use when an agent should ...
Installs into .claude/skills of the current project.
Are you the author of Resources?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/manaflow-ai-resources)
# cmux Cloud skill
This file is regenerated by the cmux app; `cmux vm --help` and the authored skill reference are authoritative when rollout versions differ.
---
name: cmux-cloud-vm
description: "Route work to cmux Cloud machines from the plain `cmux vm` CLI (alias `cmux cloud`): route/run/agent pick a machine, `vm tree` and `surface ls` catalog This Mac and cloud surfaces, and open, exec, transfer, workspace, terminal, port, checkpoint, and domain operations share the same app paths. Use when an agent should run builds, tests, servers, desktop/browser tasks, or another agent on a cloud machine, or when the user says \"cloud machine\", \"cloud VM\", \"run it in the cloud\", \"cmux vm\", or \"cmux cloud\"."
---
# cmux Cloud Machines
Everything cmux Cloud exposes from the CLI, for any coding agent (Claude Code, Codex, OpenCode, Pi, or another harness): the agent-only primitives (`route`, `run`, `agent`, `exec`, `push`, `pull`, `wait`, `terminal send|read|wait`) plus every verb the Cloud sidebar has. Host-side operations require the cmux app and a signed-in account (`cmux auth status`, `cmux auth login`); a guest-safe auth/CodeRouter subset is available inside a machine. Bring up WireGuard (`cmux vpn up`) before private VM attach, exec, desktop, or port operations; a published domain is reached through its HTTPS edge and does not require the viewer's tunnel. `cmux vm --help` is the overview and `cmux vm <verb> --help` prints a verb's own options, both offline; [references/commands.md](references/commands.md) is the complete reference and CI keeps it in lockstep with the CLI (`tests/test_cloud_vm_skill_coverage.py`). An agent with no skill loaded can bootstrap itself with `cmux vm prompt`, which installs the app-bundled copy of this skill at `~/.config/cmux/skills/cmux-cloud.md` and prints a kickoff prompt.
**The mission is delegation.** A local agent (you, on the user's Mac) sends work to machines that outlive the laptop: every terminal and agent session lives in the machine's own cmux-tui daemon, so work keeps running with every pane closed and the lid shut, and any signed-in Mac reattaches later through the same addresses. Compose machine workspaces headlessly (as many as the task needs), watch them with `vm tree --json` and `vm terminal read` without opening anything, and surface panes or a `cmux notify` only when the user should look.
**The shortest reliable loop is:** authenticate → inspect limits and routing → run or stage work headlessly → observe with `tree`/`terminal read` → open a pane or share a URL only when there is something useful to show. Ask for confirmation before creating, forking, resetting, or destroying a machine, because those operations consume plan capacity or delete data.
**Treat the installed CLI as the contract.** Read `cmux vm --help` and the
specific `cmux vm <verb> --help` before using a newly added flag, and check the
`In flight` section of the reference only for planning. A tagged app, an older
machine image, and the app's bundled CLI can be on different rollout versions;
if help and this document disagree, follow help and report the discrepancy.
## What a machine is
| Term | Meaning |
|------|---------|
| **Machine** | A persistent cloud VM (`cmux vm ls`); its generated name (for example `brave-otter`) is its id everywhere, while `vm rename` changes only a display label. New machines run terminals, agents, exec, and the desktop as `cmux` (uid 1000, home `/home/cmux`, passwordless sudo). Older machines keep their root layout until recreated; resolve paths from the remote `$HOME`. A machine may sleep when idle and wakes on connect or exec. |
| **Kind** | Every new machine uses the devbox with TigerVNC on `:1`, openbox, the dock, and noVNC on 6901. `--desktop`, `--base`, and `--no-desktop` remain accepted for older scripts; they no longer select different new-machine kinds. Historical `base` machines can still lack a screen. Shells receive the desktop environment, including `DISPLAY=:1`, so `agent-browser`, `xdotool`, and `cua-driver` can drive the screen. |
| **Contents** | The Ubuntu 24.04 devbox supplies node, bun, uv, git, gh, ripgrep, fd, jq, tmux, xdotool, Chrome, and `cua-driver`; Claude Code, Codex, OpenCode, and Pi are installed on the session PATH. If a fresh machine is still bootstrapping, inspect `/tmp/cmux/provision.log`. |
| **Session** | Every machine runs a **cmux-tui remote daemon** with workspaces (`ws_…`) and terminals (`term_…`). A terminal keeps running when the Mac disconnects or its pane closes. |
| **Publication** | `cmux cloud domains publish` maps one VM port to one HTTPS hostname. `personal` allows the owner, `team` allows current members of a selected team, and `public` allows anyone with the URL. |
| **Workspaces** | One machine hosts **many** cmux-tui workspaces: the machine is the big box, workspaces are the desks in it. Make a workspace per task *inside* a machine (`cmux vm workspace new <id> --name <task>`, the machine's ⌘N) — not a machine per task. The Cloud sidebar shows them grouped under the machine's Workspaces group. |
| **Surface** | A terminal, VNC screen, or browser — on This Mac or on a machine — with a stable id `<machine>/<kind>/<key>` (`cmux surface ls --json`). Panes *project* surfaces: `cmux surface open <id>` reuses the pane already showing one or lands it at a pane edge; closing a pane never kills a machine's terminal. |
| **Base** | The one pinned persistent machine per user (`cmux vm base open`; `base reset` mints a new generation and keeps the old VM) — the user's ongoing work. |
| **Pool** | Machines the router provisioned for agent work (labeled `agent-pool` in `vm ls`, membership persisted in `~/.cmuxterm/vm-run-pool.json`). `vm run`/`vm agent` only draft these; any other machine needs `--machine <id>`. |
| **Plan meter** | `cmux vm ls` prints the meter; the cap is whatever the backend sends (`vm ls --json` → `limits.maxActiveVms`; absent = uncapped, printed as `no limit`). The same `limits` object advertises `memoryOptionsMb`; plan tiers change, so read those fields instead of relying on remembered caps or sizes. Where the deployment gates provisioning to paid plans, `vm new`, the first `vm base open`, `base reset`, `fork`, `restore`, and the router's own provisioning answer `vm_requires_pro` with the pricing link on free or unknown plans; a free plan that can provision gets its advertised machine inside a 7-day access window (`limits.freeAccessExpiresAt` — after it, access verbs need a paid plan while list/status/delete keep working). Never delete machines to make room without asking. |
| **Checkpoint / fork** | `vm snapshot` mints a restorable checkpoint, `vm fork` clones a machine for a parallel experiment, `vm restore` brings a snapshot back — where the provider supports it (`vm ls --json` → `capabilities`). |
## Decide: cloud or local?
| Run in the cloud when… | Stay local when… |
|------------------------|------------------|
| Builds/tests take minutes, need Linux, or would hog the user's Mac | The task is a quick edit or read |
| The task needs Linux isolation or a machine the user can watch through panes and port URLs | The user is editing the same files right now |
| You want isolation (a fork per experiment, a throwaway machine) | The repo has uncommitted local-only state you cannot sync |
| You want to fan out: several agents on several machines in parallel | |
| The user said "cloud", "machine", "VM", or `cmux vm route` shows a warm machine for this directory | |
## Fast start — let the router pick
```bash
cmux vm route # which machine this directory would get, and why (--json for scripts)
cmux vm run -- uname -a # routed, executed, exit code passed through
cmux vm run --sync -- bun test # push cwd to work/<dir> first, run there
cmux vm run --sync --pull work/app/dist -- bun run build
cmux vm agent --agent claude --sync -- "run the tests and fix failures" # a detached Claude Code session on the routed machine
cmux vm tree # the surface catalog: This Mac, then every machine → workspaces → ports → VNC displays → terminals
cmux vm open vivid-newt/main/term_2f9c # show the human one terminal (reuses its pane if open)
cmux surface open vivid-newt/terminal/term_2f9c --pane pane:2 --left # any surface, at a pane edge (same drop rules as the sidebar)
cmux cloud domains publish vivid-newt 3000 # public HTTPS hostname, personal access by default
cmux pane resize vivid-newt --disk 40G # grow persistent disk (4 GiB steps; never shrinks)
```
Repeat runs from the same directory hit the same machine (sticky binding, 14 days), so synced checkouts and dependencies stay warm. `--new` forces a fresh pool machine; `--machine <id>` pins one. For a machine the router creates, `--size` accepts `4g`, `8g`, `16g`, `24g`, `32g`, `64g`, or raw MB; read `vm ls --json` → `limits.memoryOptionsMb` first because the server advertises the current plan's allowed choices and resolves unsupported requests to its default.
## Picking a machine
1. `cmux vm route` — the router's answer for this directory. If it says it *would provision*, that costs a machine slot: check `cmux vm ls` first (`--provision` creates it now).
2. Ongoing user work → Base (`cmux vm base open`, or `--machine <base-id>`).
3. A new task on a machine you already use → a new **workspace**, not a new machine (`cmux vm workspace new <id> --name <task>`): one machine hosts many workspaces, and that is the intended unit of scale.
4. Hard isolation (a different environment, a risky experiment) → `cmux vm fork <id>` of a warm machine, or `cmux vm new --detach --json` for a new devbox; add `--name <label>`. Choose `--size` from `vm ls --json` → `limits.memoryOptionsMb` (named aliases: `4g`, `8g`, `16g`, `24g`, `32g`, `64g`; raw MB also parses). Never pass `--image` unless you have a specific image id. Then `--machine <id>`, and `cmux vm wait <id> --wake` before the first command.
5. Persistent disk growth → `cmux pane resize <id> --disk <GiB>` after confirming the target and requested capacity. Values are 4–256 GiB in 4 GiB steps; the operation is grow-only, keeps the machine data and identity intact, and can take a provider minute. Run `cmux vm stats <id>` afterward to verify `disk_total_mb`.
6. Never draft the user's own machines without `--machine`, and respect the plan meter.
## Publish a VM port safely
Use the domains namespace when a user needs a stable HTTPS URL. It is separate from `vm open <id> <port>`: the latter is a private, tunnel-only preview, while a publication creates a public edge with an explicit viewer policy.
```bash
# Generated cmux hostname; no customer DNS setup is needed.
cmux cloud domains publish <machine> <port> --json
# See the URL, policy, lifecycle state, and any verification instructions.
cmux cloud domains list
# Custom zone: print the complete DNS checklist, add the records, then retry.
cmux cloud domains verify example.com
cmux cloud domains publish <machine> <port> --domain app.example.com --access team --team <team-id>
cmux cloud domains zones
# Change or remove an existing publication (hostname or id is accepted).
cmux cloud domains access app.example.com public
cmux cloud domains rm app.example.com
```
The custom-domain flow is intentionally two-phase. The first `verify` call creates a pending ownership challenge and prints labelled records: an ownership TXT record, apex and wildcard routing records, and `_acme-challenge` NS delegation. Add those records at the DNS provider, run `verify` again, and wait for the certificate/routing state to become active. A verified zone can serve the apex or one-label children (`example.com` or `app.example.com`); deeper names need a covering zone. Generated cmux names are already reserved and certificate-covered, so they skip verification.
Choose the narrowest access mode: `personal` (the default) for the owner, `team` with a required `--team <id>` for current members of that team, or `public` for anyone holding the URL. `access` changes the policy immediately; it does not create viewer grants. Treat a `public` URL as a credential and do not put it in logs or prompts. Use `--json` when another tool will consume the result; it returns stable publication/domain objects rather than the human DNS table.
## Running work
| Need | Verb |
|------|------|
| One non-interactive command, ~30 s | `cmux vm exec <id> -- <cmd...>` (`--json` → `{stdout, stderr, exit_code}`; wrap shell constructs in `sh -c`) |
| A command with no machine id, minutes long, exit code through | `cmux vm run [--sync] [--pull <path>] [--timeout <s>] -- <cmd...>` |
| A coding agent, detached, reattachable from anywhere | `cmux vm agent --agent <claude\|codex\|opencode\|pi> [--sync] [--no-open] -- "<prompt>"` (flag/subcommand-led args pass through) |
| An interactive program (REPL, TUI, long test run) driven headlessly | `cmux surface new-terminal --machine <id> --no-open -- <cmd>`, then `cmux vm terminal send <id> <term> 'input' --keys enter` → `cmux vm terminal wait <id> <term> --pattern 'pass\|fail' --timeout 300` → `cmux vm terminal read <id> <term>` |
| Files in and out | `cmux vm push <id> ./repo work/repo` / `cmux vm pull <id> work/repo/out.tgz` (SHA-256 verified, 256 MB cap, `.git`/`node_modules` skipped by default) |
| A machine that is asleep or still booting | `cmux vm wait <id> --wake` |
Opening a machine (`cmux vm shell <id>`, `vm new`, `vm base open`, the sidebar) gives a **plain terminal** on it — one terminal in the machine's cmux-tui session attached in a pane like an ssh session; it keeps running if the pane closes and shows up in `cmux vm tree` (reattach with the `cmux vm open <m>/<ws>/<term>` address the `OK` line prints). Use `cmux vm open` or `cmux surface open` for the machine's individual surfaces. Long shell work under `exec` must be backgrounded (see recipes) — never hold a long `exec` open.
## Watching and reporting back
```bash
cmux vm tree <id> # live: terminals with title, cwd, agent state, (open: surface)
cmux vm terminal read <id> <term> # the screen of any machine terminal, without a pane
cmux vm open <id> # the machine's shell
cmux vm open <id>/<ws>/<term> # one terminal as a pane; reuses the pane already showing it
cmux vm workspace open <id> <ws> [--here|--tabs|--pane <p> --left] # a whole workspace: new local workspace, or into this one
cmux vm open <id>:desktop # the noVNC screen (new machines; historical shell-only machines have no screen)
cmux vm open <id>:port/3000 [--print] # URL for an HTTP port on the machine's private address (--print: URL only; needs `cmux vpn up`)
cmux vm workspace rename <id> <ws> <name> # rename it; `close` keeps its terminals (they detach into the pool), `rm` deletes it AND kills them
cmux vm tab rename <id> <tab> <name> # rename one exact tab placement; "" clears its custom label
cmux vm terminal rename <id> <term> <name> # rename every tab placement; "" clears every custom label
cmux surface ls --json # every surface (local + cloud) with ids, lifecycle, and which panes show it
cmux surface open <resource> [--new] [--pane <p> --left|--right|--up|--down|--tab] # one open path for all of them
cmux notify --title "Cloud build done" --body "…"
```
The user cannot see inside the machine: print URLs, pull artifacts, or open a pane when there is something to look at, and `cmux notify` for long work. Only share URLs minted by `cmux vm open`; never guess raw provider URLs. A pane showing a machine surface is an ordinary local pane: move, split, reorder, or close it with the local topology verbs (`../cmux/SKILL.md`); the surface catalog follows the pane.
## Workspaces and terminals on a machine
`cmux vm workspace new|open|rename|close|rm` and `cmux vm terminal close|send|read|wait` are the machine's cmux-tui session verbs; ids come from `cmux vm tree`. `workspace rm` is the sidebar's "Close Workspace…" (kills the workspace's terminals); `workspace close` is CLI-only and keeps them running in the Terminals pool. Every sidebar action has a CLI verb over the same socket method — [references/sidebar-parity.md](references/sidebar-parity.md).
## Credentials
Agents started with `vm agent` authenticate inside the machine the way they would locally: their own login under the remote `$HOME` (set up once with `vm exec`; it persists with the machine), or the team's subrouter through `cmux ai-accounts upload` (uploads local credentials so no token is copied onto a machine). Do not put the user's tokens on a machine unless they ask.
## Guest auth and CodeRouter
Start with `cmux self --json` to identify the current machine and `cmux vm ls`
to list the team's live machines. The guest reads those through its VM-bound
TLS edge without a Mac account token. Host lifecycle verbs still run on the Mac;
read the guest's `cmux --help` for the subset its image supports.
Inside a Cloud machine, the guest `cmux` adapter can report route health and run
an agent through the shared CodeRouter without exposing the Mac's Stack session:
```bash
cmux auth status --json
cmux coderouter status --json
cmux coderouter usage
cmux coderouter models
cmux coderouter agent claude "summarize the current checkout"
cmux agent codex "run the tests"
```
These commands describe the machine's daemon, TLS edge, and VM-bound route.
Host account login and upstream credential management remain on the Mac; do not
copy those tokens into a VM. `vm agent` still starts a detached terminal on the
selected machine, while the guest `cmux agent` form runs through CodeRouter.
## Agent policy
- **Prefer `vm route` / `vm run` / `vm agent` over naming machines.** They only draft pool machines; `--machine <id>` is the deliberate way to use another.
- **Reuse before create.** `vm ls`, then an idle machine or Base. Creating machines needs a paid plan and counts against its cap.
- **Stay headless while working** (`--detach`, `--no-open`, `--print`, `terminal send|read|wait`); open panes (`vm open`, `vm tree`'s addresses) to *show* results, and `--focus true` only when the user should be looking.
- **Checkpoint before risky operations** (`vm snapshot`); fork instead of experimenting on a machine the user relies on.
- **Only destroy what you created this session.** `vm rm` and `vm workspace rm` are permanent; `vm base reset` keeps the old VM but the user must ask for it.
- **Read plan limits and sizes from `cmux vm ls` and `--help`, not from memory.**
## Common issues and fixes
| Symptom | Fix |
|---------|-----|
| `vm exec` hangs or times out | Exec is capped (~30 s). Background it: `nohup … > /tmp/x.log 2>&1 &`, then poll — or use `vm run`, `vm agent`, or a session terminal driven with `terminal send|wait|read`. |
| `claude`/`codex` not found on a brand-new machine | Provisioning is still running: `cmux vm exec <id> -- tail /tmp/cmux/provision.log`; the agents land in the session PATH (use a login shell). |
| First command after idle is slow | The machine was asleep: `cmux vm wait <id> --wake`. |
| Attach/exec cannot reach any machine | The WireGuard tunnel is down: `cmux vpn up` (state: `cmux vpn status`; needs `brew install wireguard-tools`). Machines have no public ports. |
| `vm tree --json` times out while a link is connecting | Retry without `--refresh` or scope it to `cmux vm tree <id>`; inspect `cmux vm ls --json`/`vm status <id>`, then use `vm exec` or `vm terminal read` directly when you already know the target. |
| `vm route` says it would provision | The pool is empty/busy. Check the plan meter; `--provision` (or `vm run`) creates one. |
| Create fails with `vm_requires_pro` or an active-limit error | Provisioning needs a paid plan (`cmux.com/pricing`), or the plan's machine cap is reached. Report it; let the user upgrade or choose a machine to remove. |
| `vm open <m>/<ws>` says no such workspace | Names are the cmux-tui workspace names; copy the `ws_…` id from `cmux vm tree <m>` (`--refresh` right after a link attach). |
| `vm terminal wait` exits 1 | Timeout (default 30 s; raise `--timeout`) — the error carries the screen tail; `terminal read` shows the whole screen. |
| Pushed a repo but `.git` is missing | `push` skips `.git`, `node_modules`, `.venv`, `__pycache__`, `.DS_Store` by default; `--no-default-excludes`, or ship a `git bundle` (recipes). |
| Push/pull refuses a large payload | 256 MB cap. Clone/download inside the machine instead. |
| Command works in `vm shell` but not `vm exec` | Exec has no TTY/stdin; use non-interactive flags, or `surface new-terminal` + `terminal send|read|wait` for interactive programs. |
| `vm snapshot`/`vm fork` refused | Provider capability (`vm ls --json` → `capabilities`); providers without it hide the sidebar verbs too. |
| `vm ssh` errors | The default provider attaches through the cmux-tui daemon and mints no SSH endpoint; use `exec`, `agent`, or `open`. |
| `cloud domains verify` still says pending | DNS is eventually consistent. Compare the printed record name/type/value exactly, wait for propagation, and rerun `cmux cloud domains verify <domain>`; do not create a second zone for the same name. |
| A publication is not active | `cmux cloud domains list --json` shows `state` and `verification`; custom domains must be verified and certificate-ready before routing activates. Generated domains do not need DNS proof. |
| A viewer is denied | Check the publication's `accessMode`: `personal` requires the owner, `team` requires current membership in the selected team, and `public` is the only unauthenticated mode. Change it deliberately with `cmux cloud domains access`. |
## Deep-dive references
| Reference | When to use |
|-----------|-------------|
| [references/commands.md](references/commands.md) | Every verb, alias, flag, `--json` shape, exit code, socket method, and the sidebar action it mirrors — plus the "In flight" list of verbs that exist only in open PRs |
| [references/sidebar-parity.md](references/sidebar-parity.md) | Every Cloud-sidebar action and the CLI verb that does the same thing (1:1) |
| [references/agent-workflows.md](references/agent-workflows.md) | Recipes: cloud dev box, routed agents, headless terminal loops, parallel forks, desktop/browser tasks, showing the human |
| [../cmux/SKILL.md](../cmux/SKILL.md) | Windows/workspaces/panes when presenting machine panes |
| [../cmux-workspace/SKILL.md](../cmux-workspace/SKILL.md) | Non-disruptive automation rules (focus, caller workspace) |
# cmux Cloud skill
You are helping the user work with cmux Cloud machines through the `cmux` CLI. This file is regenerated by the cmux app; do not edit it. `cmux vm <subcommand> --help` is authoritative when this file disagrees.
## Mental model
- A machine is a persistent cloud VM owned by the signed-in cmux user. Its generated name (like `brave-otter`) is its address everywhere; `cmux vm rename` sets a display label only. The machine outlives panes, closed laptops, and reconnects.
- Every machine runs a cmux session daemon that owns terminal sessions and scrollback. Managed sessions use the private cmux-remote connection; the server's capability response determines available operations. SSH is a fallback some providers and images cannot mint, and its absence is not an error.
- The backend selects the image and supported machine options. Do not assume a desktop or a separate persistent-home volume; inspect the capabilities returned for that machine.
- Base is a separate single per-user persistent slot, pinned to the top of the sidebar. `cmux vm new` mints fresh machines; `cmux vm base` always reopens the same one.
- Terminals on a machine live in its cmux-tui session (workspaces `ws_…`, terminals `term_…`). They keep running detached. `cmux vm tree` catalogs every surface, and every line is an address `cmux vm open` (machine targets) or `cmux surface open` (any entry, including This Mac) accepts: `brave-otter/main/term_2f9c…`, `brave-otter:desktop`, `brave-otter:port/3000`.
- One machine hosts many workspaces. Make a workspace per task *inside* the machine rather than a machine per task. `cmux vm workspace new <id> --name <task>` is the machine's ⌘N action and intentionally creates its starter shell; for a layout-driven workspace use `cmux vm layout apply <id> <file> --name <task>` or `cmux vm dev <id>`. The Cloud sidebar groups workspaces under the machine, and `cmux vm open <machine>/<ws>` accepts the `ws_…` id or an unambiguous name.
- A workspace is a layout: its screen's panes, splits, divider ratios and tabs. `cmux vm layout export <id> <ws>` reads it as JSON and `cmux vm layout apply <id> <file>` builds one — the same document `cmux new-workspace --layout` and `cmux layout save|get` use on the Mac. Opening the workspace on the Mac reproduces that geometry.
- Pool machines (labeled `agent-pool` in `cmux vm ls`) are provisioned by the `vm run`/`vm agent` router and reused for routed work. The router never drafts machines a person made by hand.
- Plans cap active machine count and memory. `cmux vm ls` prints the meter and, on free plans, when free cloud access expires.
## Commands
List and inspect:
```bash
cmux vm ls # NAME LABEL STATE PROVIDER IMAGE + plan meter
cmux vm status <id> # provider, status, image
cmux vm stats <id> # live CPU/memory
cmux vm ports <id> # listening TCP ports inside the machine
cmux vm tools <id> # probe common tools inside the machine
cmux vm tree [<machine>|local] [--refresh]
cmux vm self <id> [<path>] [--json] # the machine's reflection (name, owner, team, peers, integrations) through your session
```
Create and name:
```bash
cmux vm new [--base] [--size <2g|4g|8g|16g|32g>] [--detach|-d]
cmux vm rename <id> <new-label> # label only; the id stays the address
```
`vm new` takes no positional arguments (rejected so a typo cannot provision a paid machine). A bare `vm new` creates a persistent machine with its own durable home, up to the plan limit. The backend picks the provider.
Base:
```bash
cmux vm base # open Base, reuses the same VM
cmux vm base reset [--reason <text>] # new generation; the old VM is retained
```
Attach and open:
```bash
cmux vm shell <id> # terminal workspace over the private connection
cmux vm tui <id> # the machine's full cmux-tui client in a pane
cmux vm desktop <id> # noVNC screen as a browser pane
cmux vm open <machine> # same as vm shell
cmux vm open <machine>/<ws>[/<term>] # a cmux-tui workspace or one terminal
cmux vm open <machine>:desktop
cmux vm open <machine> <port> [--print] # private-network URL for an HTTP port
cmux vm ssh <id> # SSH fallback; unavailable on some providers/images
```
Machine workspaces, terminals, and panes (everything the Cloud sidebar does):
```bash
cmux vm workspace new <id> [--name <n>] # create a workspace on the machine (its ⌘N) and open it here
cmux vm workspace open <id> <ws> [--here|--tabs|--pane <p> --left|--right|--up|--down] # an empty workspace opens nothing (opened=0); --tabs and a side are exclusive
cmux vm workspace rename <id> <ws> <name>
cmux vm workspace rm <id> <ws> # close the workspace AND kill every terminal in it (the sidebar's Close Workspace…)
cmux vm workspace close <id> <ws> # CLI-only: close the workspace but keep its terminals running in the Terminals pool
cmux vm terminal close <id> <term> # end one terminal (the process and its tab)
cmux vm terminal send <id> <term> 'bun test' --keys enter # type into a terminal headlessly, then press keys (no pane, no focus)
cmux vm terminal wait <id> <term> --pattern 'pass|fail' [--timeout 120] # block until the screen matches; exit 1 on timeout
cmux vm terminal wait-exit <id> <term> [--timeout <s>] # block until the process exits (exited code=<n> | pending)
cmux vm terminal output <id> <term> [--after <offset>] # the full output so far, resumable
cmux vm terminal read <id> <term> # the terminal's visible screen (what a person would see)
cmux surface ls [--json] # every surface (This Mac + machines) and which panes show it
cmux surface open <machine>/<kind>/<key> [--new] [--pane <p> --left|--right|--up|--down|--tab]
cmux surface new-terminal --machine <id> [--remote-workspace <ws>] [--cwd <dir>] [-- <cmd...>]
cmux vm layout export <id> [<ws>] [--raw] # the workspace's shape as JSON ({"name","cwd","layout"})
cmux vm layout apply <id> <file>|- [--name <n>] [--workspace <empty-ws>] [--cwd <dir>] [--from-saved <name>] [--open]
cmux vm env set <id> KEY=VALUE… [--from-file .env] ; cmux vm env ls <id> [--show] ; cmux vm env rm <id> KEY…
```
Layout document: `{"pane":{"surfaces":[{"type":"terminal"|"browser","name"?,"cwd"?,"command"?,"env"?,"url"?,"focus"?}]}}` leaves and `{"direction":"horizontal"|"vertical","split":0.1–0.9,"children":[a,b]}` splits (`horizontal` = side by side, first child left; `vertical` = stacked; `split` = the first child's share). Terminals are login shells in `cwd` (relative to the document `cwd`, default: the work user's home); `command` is typed into the shell so the pane survives it. `apply` builds a new workspace (`--name`) or fills an already-empty one (`--workspace`), never a non-empty one; `vm workspace new --no-open` intentionally creates the starter-shell workspace and is therefore not the recipe for an empty layout target. Prefer `--name` or `vm dev`; exit 2 with the JSON path on a malformed document. `vm env` values live 0600 at `~/.config/cmux/env` in the work user's home and are sourced by every shell cmux starts on that machine (terminals, `vm exec`, `vm agent`, layout panes). They travel over the machine's end-to-end encrypted link into the machine's `cmux env receive` — never through `vm exec`, a command line, or a terminal screen — so put secrets there and only names in a layout; prefer `--from-file .env` or `-` (stdin) so values skip your own shell history; `vm env ls` shows names unless `--show`; forks and snapshots inherit the file.
A pane showing a machine surface is an ordinary local cmux pane: move, split, reorder, or close it with the local workspace/pane commands (`cmux --help`), and closing a pane never kills the machine's terminal. Workspace (`ws_…`) and terminal (`term_…`) ids come from `cmux vm tree`; `cmux vm tree --refresh` re-reads the fleet and every machine (a machine you just created shows up at once). A `--workspace`/`--pane` that names nothing is an error, never a silent open somewhere else.
`terminal send/wait/read` is how you drive an interactive program on a machine (a REPL, a TUI, a long test run, another agent's session) without attaching a pane or taking the user's focus: start it with `surface new-terminal --machine <id> --no-open -- <cmd>`, then send input, wait for the prompt or result pattern, and read the screen. Open a pane for the person only when there is something to show.
Run commands:
```bash
cmux vm exec [--timeout <s>] <id> -- <command...> # one command, 30 s default up to 900 s, exit code passes through
cmux vm run [--sync] [--pull <remote-path>] [--machine <id>] [--new] [--size <s>] [--timeout <seconds>] [--wait [--output]] -- <command...>
cmux vm route [--cwd <dir>] # print which machine vm run/agent would pick, and why
cmux vm wait <id> [--timeout <seconds>] [--wake]
cmux vm dev <id> [<folder>] [--name <ws>] [--layout <file>] [--command "<cmd>"] [--port <n>] [--remote <path>] [--sync|--no-sync] [--no-open] [--dry-run] [--json]
```
`vm run` needs no machine name: it reuses an idle pool machine, wakes a sleeping one, or provisions a fresh one (default timeout 600s, max 15 minutes). `--sync` pushes the current directory to `work/<basename>` first; `--pull` fetches a path back afterward. For longer work start a detached terminal via `vm agent`. `vm dev` is the one-verb dev box: it checks the machine, optionally pushes the folder, detects the dev command and port, then creates or reuses the named workspace through `vm layout apply --name` and opens the resulting geometry on the Mac. `--command`/`--port` override detection, `--layout` replaces the built-in document, `--dry-run` makes no socket calls, `--no-open` stages without a local pane, and `--json` returns machine/workspace/terminal/layout/url fields. When the workspace already has live terminals, a rerun preserves its layout instead of starting another dev server.
Coding agents on machines:
```bash
cmux vm agent --agent <claude|codex|opencode|pi> [--machine <id>] [--sync] [--cwd <dir>] [--name <name>] [--no-open] [--new] [--size <s>] [--wait [--output] [--timeout <s>]] -- <prompt or args...>
```
The agent starts as a detached terminal in the machine's cmux-tui session: it keeps running when the pane closes, and `cmux vm open <machine>/<ws>/<term>` reattaches from any device. A bare prompt runs the agent's one-shot form; leading flags or known subcommands pass through verbatim. Credentials for cloud agents come from `cmux ai-accounts upload`. `--wait` blocks until the agent's process exits (Ctrl-C stops the wait, not the agent), `--output` then prints everything it wrote, and the agent's exit code becomes yours (1 on timeout or signal). Without `--wait`, `cmux vm terminal wait-exit` + `terminal output` on the printed terminal id do the same later.
Files:
```bash
cmux vm push <id> <local-path> [remote-path] [--exclude <pattern>]... [--watch [--interval <s>]]
cmux vm push --secret <id> <local-file> <remote-path> [--mode <octal>] # one file over the machine's link, never through exec (0600, atomic, 256 KiB cap)
cmux vm pull <id> <remote-path> [local-path]
```
Directories travel as tarballs with default excludes (node_modules, .git, and similar); transfers are size-capped, so ship repos without build artifacts. `--watch` re-pushes on local change (one line per push, Ctrl-C to stop). `--secret` is for keys, tokens and config files with credentials: like `vm env set`, it rides the end-to-end link into the machine's `cmux file receive`, which turns echo off before it reads, so the bytes never touch a command line, the control plane, the provider API, a screen or scrollback; it refuses directories.
Push and watch uploads copy files over the destination. They preserve remote-only files, including files deleted locally; remove those explicitly on the machine when needed. This lets agents keep files they create remotely, but it does not produce an exact directory mirror.
Snapshot, fork, restore:
```bash
cmux vm snapshot <id> [--name <name>]
cmux vm snapshot ls <id> [--json] # this machine's snapshots, newest first
cmux vm snapshot rm <id> <snapshot-id> # delete one of this machine's snapshots
cmux vm fork <id> [--name <name>]
cmux vm restore <snapshot-id>
```
Destroy:
```bash
cmux vm pause <id> # park a machine when the work is done (compute stops; files and daemon state stay)
cmux vm resume <id> # wake it again
cmux vm rm <id> # irreversible and unprompted
```
## The grammar (one spelling per concept)
| Where you are | What you address | Spelling |
|---|---|---|
| Mac | a machine | `cmux vm <verb> <machine> …` — everything about machines lives here |
| Mac | this Mac's own session | the unprefixed local verbs (`cmux send-key`, `cmux new-workspace`, …) |
| inside a machine | **this** machine's session | the same unprefixed local verbs as on a Mac (`cmux send-key`, `cmux terminal send`, `cmux layout apply`, `cmux env set`, `cmux notify`) |
| inside a machine | **another** machine | `cmux vm <verb> <machine> …` — the same grammar as on the Mac |
| inside a machine | the owner's machines | `cmux vm ls` (this one marked `*`, with reachability) |
| inside a machine | myself | `cmux self [peers\|integrations\|owner\|machine] [--json]` (aliases: `cmux whoami`, `cmux reflect [<path>]`) |
## Inside a machine
Every machine has its own in-VM `cmux` CLI (a shim over the machine's cmux-tui daemon). Local verbs use cmux-tui's grammar (`cmux <resource> <action>`) against the machine's own session — workspaces, terminals, panes:
`cmux self [--json]` identifies this machine and `cmux vm ls [--json]` lists the owner's live machines, this one marked `*`, with reachable/linked state. Both read reflection (`GET /api/vm/reflection`, falling back to `/api/vm/self` on an older server), work without a local daemon, and use the edge-injected machine identity, never an account token copied into the VM.
```bash
cmux workspace current run -- bun test # run a command in a durable terminal here
cmux session current snapshot --json # this machine's workspace/terminal tree
```
A machine knows who it is: `cmux self` (name, id, status, team, owner, plan) and `cmux self peers|integrations|owner|machine` read the reflection service through the edge, which asserts the identity — nothing in the guest is a credential. `cmux self integrations` lists what the machine can use with a help command each; `cmux whoami` and `cmux reflect <path>` are aliases.
The Mac spellings work there too, against the machine's own session (default target: the terminal you run them from, `$CMUX_TUI_TERMINAL_ID`):
```
cmux tree --json ; cmux new-workspace --name tests ; cmux new-split right
cmux send-key --terminal <term> ctrl+c ; cmux send --terminal <term> 'bun test' ; cmux read-screen --terminal <term>
cmux terminal send <term> 'bun test' --keys enter ; cmux terminal wait <term> --pattern 'pass|fail' ; cmux terminal read <term>
cmux terminal wait-exit <term> --timeout 600 ; cmux terminal output <term> # wait for the process to end, then read everything it printed
cmux layout export [--workspace <ws>] ; cmux layout apply --name app app.json
cmux env set KEY=VALUE ; cmux env ls ; cmux env rm KEY
cmux agent claude [--timeout <s>] "fix the tests" # runs in this terminal until it exits (it is the wait; exit code passes through)
cmux file receive <path> [--mode <octal>] # the receiver that `cmux vm push --secret` (Mac) and peer `cmux vm push` type into
```
`cmux vm …` inside a machine talks to OTHER machines of the same owner, discovered through `cmux self peers` / `cmux vm ls` (the private network is the trust boundary; no Mac step is needed, and older Mac-written route files still work):
```bash
cmux vm ls # the owner's machines: this one marked *, reachable/linked/connected
cmux vm exec <peer> -- <command> # run on the peer (durable terminal there)
cmux vm tree <peer> # the peer's workspace/terminal snapshot
cmux vm terminal send|read|wait|close <peer> <term> … ; cmux vm terminal send <peer> <term> <keys…>
cmux vm workspace new|rename|close|rm <peer> … ; cmux vm layout export|apply <peer> … ; cmux vm env set|ls|rm <peer> …
cmux vm agent <peer> --agent <claude|codex|opencode|pi> [--name <n>] [--cwd <dir>] [--wait [--output]] -- <prompt> # a durable agent terminal on the peer; --wait blocks until it exits
cmux vm push <peer> <local-file> <remote-path> [--mode <octal>] # one file to the peer, always over the link (secret-safe by construction)
```
Peer commands require an existing grant and a compatible daemon. No control-plane credential lives in a VM. Connection readiness comes from daemon events, with a 30-second deadline and cancellation cleanup, not polling. Guest messages follow `LC_ALL`, `LC_MESSAGES`, then `LANG` (English or Japanese).
## Rules
- Never run `cmux vm rm` on a machine you did not create in this session unless the user names it this turn. Use `cmux vm base reset` for a fresh Base; it retains the old VM.
- Prefer `vm run`/`vm agent` routing over creating machines; creation bills the user and counts against the plan. Check `cmux vm ls` before `vm new`.
- Use `cmux vm wait` instead of polling `vm status`.
- Use `--json` only on commands whose `--help` documents it; interactive verbs (`shell`, `tui`, `desktop`) have none. Do not parse the human table output either way.
- `vm exec` quoting is faithful per argv element; wrap shell constructs as `-- sh -c '<script>'`.
- Read plan limits and sizes from `cmux vm ls` and `--help`, not from memory.
### Arrange the view from inside the machine
Use these commands in a daemon terminal (`cmux tree --json` supplies workspace,
screen, pane, split and tab IDs):
```bash
cmux workspace rename <ws> "Review ready"
cmux terminal rename current "Builder" --json
cmux tab rename <tab> "Test results" --json
cmux pane split <pane> right --ratio 0.6 --json
cmux tab move <tab> --workspace <ws> --screen <screen> --pane <destination-pane> --index 0 --json
cmux pane swap <pane> --other-workspace <ws> --other-screen <screen> --other-pane <other-pane> --json
cmux pane resize <pane> --split <split> --ratio 0.65 --json
cmux workspace move <ws> --index 0
cmux tab focus <tab>
cmux notify --title "Review ready" --body "The workspace has the app, logs, and test results."
```
`tab rename` labels one placement; `terminal rename` labels every current placement
of that terminal. Names, including spaces and an empty string, are passed as exact
arguments. A terminal with several views should be moved by its tab ID. Moving,
renaming, swapping, and changing split ratios preserve running terminal processes.
`pane resize` changes layout geometry; machine disk resizing remains unavailable.
Local commands use `cmux <resource> <verb> …`; a peer uses
`cmux vm <resource> <verb> <machine> …` (for example,
`cmux vm tab rename <machine> <tab> "Logs"`). Existing ID-first daemon syntax is
also supported. `cmux workspace help` lists the full topology grammar.
Arrange the daemon workspace before presenting it. The Mac's
`cmux vm workspace open <machine> <ws>` reads that layout when creating its local
view. These guest commands do not force focus or rearrange an already-open Mac
projection. `layout apply` is for new/empty workspaces; use the commands above to
change an occupied workspace without restarting its agents.