Cloud: Codex and opencode chats run on your own Cloud machine

Merged
TE
5d ago
+381-17

Leo, 2026-10-30, after showing ChatGPT's "Where should this chat run? On your computer / In the cloud" popover: "we should just have it" / "we have the roadmap in place to have it go more like chatgpt work right where you just pick the cloud button or whatever and then it just works?"

What

Today a Codex or opencode chat on a Cloud machine shows "Not available from here: this agent has no mode that asks before each change" and has no Send, so no Codex chat runs in Cloud. After this PR, on a Cloud machine the signed-in person owns, Codex and opencode chats start and send like Claude does. Nothing changes anywhere else.

  • cmux-tui (the machine's daemon), agent-session-start. Two conditions must both hold: the machine is a Cloud VM with no team evidence (OwnerCloud/UnknownCloud), and the caller sent vm_kind: "owner", the Cloud API's answer for this link, which the app already sends since #26323 (never the guest's own record). When they do, the start skips the refused-family check and the asking-mode settle. The agent starts in its own default mode, still with policy ask, so a permission question the agent asks still reaches the person. The new capability is agent-session-start-owner-cloud-v1. The daemon advertises it only where its own host read is a Cloud VM with no team evidence, so a Mac, an SSH host or a VM with team evidence never lets the app send to a Codex or opencode session there, whatever the Cloud API says.
  • The app's remote wire (RemoteAcpmuxWire). initialize sets ownerCloud: true only when two things are true at that moment: the daemon serves the capability, and the link's owner answer holds (AgentTabRemoteHost.ownerLink). origin stays "remote".
  • CloudLinkSession.ownerAnswer(appLink:link:). The link owner-answer closure moved here from the App's AgentTabRemoteHost.ownerLink(appLink:link:), unchanged. That way this PR adds no CmuxNextApp lines (arch ratchet app_lines -4). RemoteAcpmuxWire.ownerCloud(_:served:) builds the wire's closure from it.
  • Composer (remoteEditing.ts). remoteComposer lets a Codex or opencode chat send on origin: "remote" only when ownerCloud is true.
  • Doc. plans/cmux-next/acp-remote-guard.md gets an update section that records this decision next to D10.

Why it's safe, and what still refuses

A Cloud VM the person owns is a disposable sandbox of their own: its agents already have sudo there, and Claude already runs unsandboxed at the VM boundary (#26276/#26309/#26323). The refusal stays for:

  • Mac or SSH host: refused even if a caller says owner. A Mac is not a disposable sandbox.
  • Team VM: refused at team_vm_blocked, as before.
  • Cloud VM without the caller's owner word: refused at host_unverified, as before.
  • Remote browser and paired devices (acpmux Web/Device origins): REFUSED_FAMILIES in acpmux web_modes.rs is unchanged.

Prior art read first:

  • #26323, #26276, #26309: the attested vmKind and the VM boundary. This PR reuses their owner word and doesn't widen what it accepts.
  • #25310: the Codex bridge's narrow modes.
  • #26175: Codex access grants into owner VMs, the sign-in half that this needs.
  • D10 in acp-remote-guard.md: why the families are refused.

Takes back #27071: not a take-back. #27071 added AGENT_SESSION_START_MODEL_CAPABILITY to the pub use agent_session_start::{...} list in agent_session_attach.rs; this PR keeps it and adds AGENT_SESSION_START_OWNER_CLOUD_CAPABILITY, so rustfmt rewraps those lines.

Takes back #26825: not a take-back. #26825 added start_link_share_bridge to the pub use agent_session_attach::{...} list in server.rs; this PR keeps it and adds AGENT_SESSION_START_OWNER_CLOUD_CAPABILITY, so rustfmt rewraps those lines.

No capture: the Swift changes are wire and link logic (RemoteAcpmuxWire, CloudLinkSession) with no native UI of their own; the visible change is the webview composer, captured by the gallery advisory run under Tests.

Risk

risk:high (socket and relay policy). The relaxation applies only on a Cloud VM, only with the caller's exact owner word, and is read again at each start. An older daemon doesn't serve the capability, so the app never sets ownerCloud for it and the composer stays as it is today.

Tests

Gallery: agent-pane.composer/codex-cloud-team, agent-pane.composer/codex-cloud-own

Before/after: the composer for a Codex chat on a Cloud machine. Before (and still, on a team machine): codex-cloud-team, with the "Not available from here" note and no Send. That state renders exactly what base shows on every Cloud machine, the person's own included. After, on the person's own machine: codex-cloud-own, the normal composer with Send. The gallery advisory run captured both states, light and dark, 2x, synthetic data: https://github.com/manaflow-ai/cmux/actions/runs/41159050556 at be6e24442eb3 (artifact gallery-advisory-28613-<sha>; the head commit after it changes no webview file). I viewed the after images: codex-cloud-own shows the normal composer with Send, and codex-cloud-team shows the "Not available from here" note and no Send, in both themes.

Viewed after image: yes
Visual verdict: The composer is unchanged from the ordinary chat composer on the own machine and keeps today's note on a team machine, so it sits fine next to ChatGPT or Claude.

  • cmux-tui/tests/cli/agent_session_start.rs agent_session_start_starts_codex_on_the_callers_own_cloud_vm_only:
    • On an owner Cloud VM, a Codex-family and an opencode-family harness both start with vm_kind: "owner".
    • The same machine refuses them without the word.
    • An SSH host refuses them with the word (not_asking), and so does a team VM (team_vm_blocked).
    • The SSH and team daemons do not advertise agent-session-start-owner-cloud-v1.
  • agent_session_start.rs unit test only_the_callers_own_cloud_vm_starts_any_agent: the host-kind Ɨ owner-word table.
  • RemoteAcpmuxOwnerCloudTests (Swift): initialize names ownerCloud only when the owner answer holds, for an attached chat and for a new chat's wire alike. CloudVmKindTests checks the capability's raw value.
  • remoteEditing.test.ts: send is off on a remote Codex or opencode chat and on when ownerCloud is set. An unknown origin still refuses even with the flag.

RED/GREEN: see the comments below for the runs at the red commit (tests only) and at the head.

Observed / Not run

See the runs linked below. Not run: a live Codex chat on a real staging Cloud VM.

Changelog

  • Changed: Codex and opencode chats run on your own Cloud machines.

Agent session: agent session

šŸ¤– Generated with Claude Code