cmux NEXT: vm.agent_key.create mints a VM-only agent key (Labs)

TE
7d ago
+538-1

Leo (2026-10-29): "Use cmux Cloud VMs: start a lane to ship cmux-vm in the NEXT bundle and let agents (including ones outside cmux, with a scoped token) run cmux vm run --sync."
Leo (2026-10-19): "anybody logged in with an @cmux.com account ALREADY CAN JUST USE THE VMS"
Leo (2026-10-29), on landing #27604 as designed (Labs-gated, VM-only scopes, up to 24h, budget-capped): "Yes, ship it"

What changes

New socket method vm.agent_key.create, the app half of cmux vm token create (#27603). With the signed-in Stack session and the chosen team, the app calls POST https://vm.cmux.dev/v1/agent-keys (vm-staging for development builds; no override, so the Stack session goes only there) and returns the agent key, its id, expiry, budget, base URL and team over the socket.

  • Gates, in order: the Cloud feature policy (vm.agent_key.create joins .cloud in policyFeature), Labs > Show Preview Features (labs_off), signed in (not_signed_in), a team (no_team). The socket's same-process rule is unchanged, so only processes cmux started can mint.
  • Scopes are not the caller's to choose: the request always carries vm:read, vm:write, vm:exec; the caller sets only name, ttlSeconds, maxVms and maxUsdPerHour, each refused with invalid_params when present but not a number in range (TTL 60s–24h, 1–20 VMs, more than $0 and at most $50/h), never dropped for a larger server default; vm.cmux.dev bounds them again.
  • The Stack token never leaves the app. HTTP errors map to control errors (401/403 → not signed in or not allowed, 402 → the team's plan has no VMs, 429 → quota, others → unreachable).
  • Three new strings in Runs.xcstrings (all 21 locales).

Design and threat model are in #27603. Same trust line:

Testing

No capture: this adds a socket method and three refusal strings that cmux vm token create prints in the terminal; no app window, view or menu changes.

Compile-checked on the fleet for this PR. The end-to-end proof runs on a NEXT nightly once both PRs land.

🤖 Generated with Claude Code

Agent session: agent session