# cmux Cloud CLI reference This reference covers the cmux Cloud CLI in this source revision. `cmux cloud` is an alias for `cmux vm` (`cmux cloud ls` == `cmux vm ls`). `tests/test_cloud_vm_skill_coverage.py` checks command coverage against the CLI source. Find the needed section: [machines](#machines), [execution](#execution), [files](#files), [routing](#routing), [workspaces/terminals](#workspaces-and-terminals-the-machines-cmux-tui-session), [layouts](#layouts-the-shape-of-a-machine-workspace), [environment](#environment-project-secrets-and-settings-on-a-machine), [domains](#public-https-domains), [limits](#plan-meter-and-limits). Guest auth, peer access, notifications and browser opening are in [guest operations](guest.md). Installed `cmux vm --help` owns rollout availability; this reference documents current source. ## Conventions - **Requires** the cmux app running on the Mac, a signed-in account (`cmux auth status`), and the WireGuard tunnel up (`cmux vpn up`) — machines live on a private per-user network with no public ports, so attach/exec/port verbs need it. Every verb talks to the app over its Unix socket (`CMUX_SOCKET_PATH` when set; the app's default socket otherwise) — the app, not the CLI, holds the cloud credentials. The guest `cmux self` and `cmux vm ls` commands are the exception: they run inside a machine through its edge-injected credential. - **`--json`** is a global flag: it may appear before or after the subcommand and prints the socket payload (or the CLI's own summary object, noted per verb) instead of text. Parse JSON, never the human tables. - **`--help` / `-h`** works offline (no app needed). `cmux vm --help` is the overview; `cmux vm run --help`, `route`, `agent`, `push`, `pull`, `wait`, `open`, `tree`, `workspace`, `terminal`, `tui`, `prompt`, `base`, and `domains` print that verb's own options (`cmux vm terminal --help` covers close, send, read, and wait). `resize` is documented in the overview because its compact validation usage is emitted inline. Anything after `--` is never treated as a help flag (`cmux vm exec -- --help` runs `--help` on the machine). - **Exit codes:** `0` success; `1` any error (socket missing, backend error, usage error, unknown `vm` verb); `2` missing or unknown top-level command. `cmux vm run` exits with the **remote command's exit code**; `cmux vm exec` prints `exit ` to stderr and exits `1` when the remote command fails; `cmux vm wait` and `cmux vm terminal wait` exit `1` on timeout (or a failed machine). - **Ids:** a machine's generated name (`brave-otter`) is its id everywhere; the display label from `vm rename` is cosmetic. Workspace ids are `ws_…`, terminal ids `term_…` (both from `cmux vm tree`). `--window ` on the opening verbs picks the local window; `--workspace ` the local workspace. - **Env:** `CMUX_VM_API_BASE_URL` overrides the backend origin (dev stacks). `HOME` is honored for the router's state files (`~/.cmuxterm/vm-run-pool.json`, `~/.cmuxterm/vm-run-bindings.json`). For a first run, use this read-only preflight before provisioning anything: ```bash cmux auth status cmux vm ls --json cmux vm route --json cmux vm tree --json ``` ### Command catalog These are individual examples, not a sequence to run. VPN and workspace commands below can change state; choose only the operation needed for the task. ```bash cmux auth status # signed in? cmux vm ls # NAME / LABEL / STATE / PROVIDER / IMAGE + plan meter (+ free-window countdown) cmux vm ls --json # {vms: [{id, status, image, createdAt, freeAccessExpiresAt, capabilities: {ports, …}}], limits: {maxActiveVms, planId, memoryOptionsMb, freeAccessWindowDays, freeAccessExpiresAt}} cmux vpn status # this build's WireGuard tunnel to its private machine network (machines open no public port): up, down, or up for another enrollment (stale) cmux vpn up # enroll this Mac and bring the tunnel up (sudo); a stale tunnel (rotated keys) is replaced. One tunnel per deployment (`cmux` for production, `cmux-staging`/`cmux-dev` for dev builds), so a dev build and the production app can both be up cmux vpn down # take this build's tunnel down (sudo) cmux self # INSIDE a machine: this machine's name, id, status, team (--json: {schema, machine, team, machines}); guest `cmux vm ls` lists the team's machines with this one marked * cmux vm tree # the surface catalog: This Mac (terminals by workspace, browsers), then every machine → Workspaces, Ports, VNC Displays, Terminals cmux vm tree --refresh # one machine (`local` for This Mac), re-synced first cmux vm workspace new [--name n] # a new cmux-tui workspace on the machine (⌘N there), opened as a new local workspace cmux vm workspace open # open a machine workspace as a NEW local workspace: one pane per terminal/browser (clicking its row) cmux vm workspace open --here [--workspace ] # into the current local workspace: one pane + the rest as tabs (drop a workspace row onto a pane) cmux vm workspace open --tabs [--pane

] # all as tabs of the focused/--pane pane (CLI placement) cmux vm workspace open --pane

--left|--right|--up|--down # what dropping the row on that pane edge does cmux vm workspace rename # rename that workspace (the row's "Rename…") cmux vm workspace close # CLI-only: close that workspace but keep its terminals running in the Terminals pool cmux vm workspace rm # close that workspace AND kill every terminal in it (the row's "Close Workspace…" / hover ×). Permanent. cmux vm terminal close # end one terminal on the machine (the sidebar's ×); its local panes close too cmux vm terminal send [text] [--keys enter,ctrl+c,…] # type into the terminal headlessly (as-is, no newline), then press named keys (chords join with +); no pane, no focus cmux vm terminal read # the visible screen as text (--json: + rows, cols, cursor) cmux vm terminal wait --pattern [--timeout ] # block until the screen matches (default 30 s); exit 1 on timeout cmux vm tree --json # {machines: [{id, local, name, status, link_state, …}], resources: [{id, machine, kind, key, title, detail, lifecycle, agent, remote_workspace, port, url, open, open_surface_ids}], projections: […]} cmux surface ls [--json] # same catalog; `surface open ` / `surface new-terminal --machine ` / `surface new-display --machine ` are the generic verbs cmux vm status # provider, status, image cmux vm stats # CPU/mem/disk now; sleeping machines stay asleep cmux vm clean # dry-run: what disk cleanup would free (removes nothing); --apply removes it cmux vm resize --disk 40G # grow persistent disk in 4 GiB steps (never shrinks) cmux vm network # outbound policy: mode, presets, domains, ranges, applied state cmux vm agent-updates # latest (updated on attach) or image (baked pins) cmux vm tools # which tools are installed cmux vm ports # listening TCP ports inside the machine cmux vm handoff # short attach block to paste to a human or another agent ``` Only then choose `vm run`/`vm agent` (router-managed work), `vm base open` (the persistent personal machine), or an explicit `vm new`. Use `--json` for automation and the human output when teaching a person what to click or copy. ## Machines ### `cmux vm ls` ```bash cmux vm ls [--json] # alias: cmux vm list ``` Socket `vm.list`. Text: a `NAME LABEL STATE PROVIDER IMAGE` table, then the plan meter (`N of M machines on the plan` when `limits.maxActiveVms` is set, `N machines on the plan, no limit` when it is absent) and, on free plans, when free cloud access expires. Empty: `No cloud VMs. Try: cmux vm new`. `--json`: `{vms: [{id, displayName?, status, provider, image, kind?, capabilities?: {snapshot, fork}, resources?: {vcpus, memoryMb, diskMb?}, createdAt?, freeAccessExpiresAt?}], limits: {maxActiveVms, planId, memoryOptionsMb?, maxDiskMb?, maxMemoryMb?, maxVcpus?, poolVcpus?, poolMemoryMb?, usedVcpus?, usedMemoryMb?, freeAccessWindowDays?, freeAccessExpiresAt?}, imageKinds?}`. Sidebar: the Machines panel list. ### Lifecycle and safety Create a machine only after `vm ls` shows the available plan capacity. A bare `vm new` provisions the devbox with displays. The legacy `--base`, `--no-desktop`, and `--desktop` flags remain accepted but all select that same devbox. Use `--detach` for a headless create, then wait for readiness before the first command: ```bash cmux vm ls --json cmux vm new --detach --name "build box" --size 8g --json cmux vm wait --wake cmux vm status ``` Named sizes are `4g`, `8g`, `16g`, `24g`, `32g`, and `64g`; a raw memory value in MB is also parsed. Pro and Team support 4g, 8g, and 16g (2, 4, and 8 vCPU). Max additionally unlocks 24g and 32g (12 and 16 vCPU), plus 64g (32 vCPU). Read `vm ls --json` → `limits.memoryOptionsMb` for the current plan's choices. The server uses its plan default when a parsed request is not allowed; the chosen image supplies the matching CPU and initial disk. `--name` changes the display label, never the generated machine id. Prefer a workspace on an existing machine for another task; use `vm fork` for an isolated experiment. `vm rm` permanently deletes the machine and its volume, so only remove a machine created for the current task and ask before touching someone else's machine or Base. ### `cmux vm new` ```bash cmux vm new [--desktop|--base] [--size <4g|8g|16g|24g|32g|64g|MB>] [--name

] [--image ] [--workspace ] [--window ] [--focus|--no-focus] [--detach|-d] [--json] # alias: cmux vm create ``` Socket `vm.create` with `kind: desktop` for every new machine. The legacy `--base`/`--no-desktop` and `--desktop` flags all select the same devbox; contradictory flags are rejected. The backend selects the image from its manifest; `--image ` is the explicit override and the only way an image id leaves the client. If the requested kind is not offered, the server fails closed with an image-config error rather than silently returning the wrong shape. `--size` accepts `4g`, `8g`, `16g`, `24g`, `32g`, `64g`, or raw MB ≥ 512. Pro and Team can create through 16g (8 vCPU); 24g and 32g (12 and 16 vCPU) and 64g (32 vCPU) require Max. `vm ls --json` → `limits.memoryOptionsMb` is authoritative for the current plan; the backend selects its default when a parsed request is unavailable, and the chosen image supplies the matching CPU and initial disk. `--name` applies a display label through `vm.rename` after the create. Positional arguments are rejected (`cmux vm new myvm` errors instead of provisioning). Retries of a failed create reuse an idempotency key so a transient failure never mints two machines. Without `--detach`, opens a plain terminal on the machine (the same open path as `vm shell`); it switches to its workspace only when run interactively from a terminal (an agent or script gets it in the background, marked unread); `--focus` / `--no-focus` (also `--focus true|false`) override that, and `--no-focus` is what the New Machine sheet does — the app's Create returns control immediately and the pane appears in the background); desktop machines also get their screen in a split. Text output carries the stable `OK machine=` marker after the localized created line; `--detach` prints ` is ready` and the follow-up commands. `--json`: the `vm.create` payload (`{id, provider, image, kind?, …}`) and no pane. Sidebar: Machines panel + / "New Cloud Machine…" sheet (name, size, plan meter). On a free or unknown plan the backend returns `vm_requires_pro` (exit 1); paid-plan machine caps come from the backend (`vm ls --json` → `limits.maxActiveVms`; absent means uncapped). The current CLI accepts `--provider freestyle`; omit it to let the server choose the configured default. If a deployment adds another provider, read that tagged app's `vm new --help` before using it. ### `cmux vm rename` ```bash cmux vm rename