acpmux: the caller's side of the VM boundary (vmBoundary lease, saved peer, forwarding)

Merged
TE
9d ago
+349-22

The caller's side of the VM boundary. A caller keeps its own opt-out for a peer's remote chains, and forwards it only for the person's own clients. No machine's behaviour changes yet: the guest ignores the field until #26276 (the guest's side) lands.

The design, as agreed with the area owner: "the kind comes from outside the guest. The client side (the app or the daemon that starts the remote session) asks the Cloud API which kind the VM is, for the signed-in account, and passes that to the remote start; the guest never decides." And: "Agents keep sudo in Cloud VMs".

PR 2a of the series. The order:

  • Done: fail-closed #26227 (every Cloud VM sandboxes).
  • Done: web/ attach-endpoint vmKind #26222 (live).
  • This PR (2a): the caller's side.
  • #26276 (2b): the guest's side.
  • (3): the Cloud link passes --vm-kind owner into the lease, only when the Cloud API answered "owner" for the signed-in caller.

This was one PR. I split it to keep each part under about 800 lines.

What changes

  • Saved peer: vmBoundary: true on a saved peer in the caller's own config.json, for a VM its fleet registry calls a dedicated agent VM. peer_add never sets it, and a re-add of the same name and URL keeps it. A config reload applies the file's value to the running peer (same URL only) with no reconnect. A leased peer keeps its lease's value.

  • Cloud link lease: _acpmux/peer_lease {vmBoundary: true} (unix socket only). PR 3's link sends it only when the Cloud API said this caller owns the VM. It is held with the lease and never saved. Only the JSON value true counts.

  • Forwarding: each session/new the caller forwards to such a peer carries _meta.acpmux.vmBoundary: true, but only for the person's own clients (vm_boundary::owner):

    • the unix socket;
    • the app (LocalApp);
    • the caller's own peer daemon asking for itself (PeerToken and Control::Peer).

    It is never added for a Web client, the listener token, a paired machine (Device, ReadOnlyDevice) or a request held to the Web's rules. Any vmBoundary the client sent itself is dropped first.

  • via: device: a request this daemon forwards for a paired machine's client credential, which is served as a peer, now says via: device. The peer then never takes it for the owner's. An older peer ignores the mark, and its folder-trust gate treats it as before (no via).

Risk

Low. Nothing reads the field on the guest yet. The lease takes one more optional param, and the forwarded session/new carries one more _meta key that older daemons ignore (vmBoundary is not a mode field).

Tests

tests/peer_lease.rs a_vm_boundary_lease_forwards_the_boundary_for_the_persons_own_clients_only uses a fake peer that records each forwarded session/new:

  • With a vmBoundary lease, the unix socket's and the app's starts carry vmBoundary: true.
  • With a plain lease, they don't, even though each client sent vmBoundary: true itself.
  • A Web client's or a paired machine's start never carries it. If either is forwarded at all, it goes as via: web or via: device.

a_reload_takes_a_saved_peers_vm_boundary_back checks that a reload turns a saved peer's boundary off and on again with no reconnect. Unit tests cover forwards and owner.

Before and after

On base 370fc6137dc, with only the new forwarding test:

test a_vm_boundary_lease_forwards_the_boundary_for_the_persons_own_clients_only ... FAILED
panicked at crates/acpmux/tests/peer_lease.rs:377:13:
  left: Some(Bool(true))
 right: None
test result: FAILED. 6 passed; 1 failed

On base, a plain lease forwards the client's own vmBoundary.

At this head (cmux-ci, Linux):

cargo fmt -p acpmux --check: ok
cargo clippy -p acpmux --all-targets -- -D warnings: ok (with the base's collapsible_if red in cli/codex_auth_broker.rs patched locally, not in this PR)
lib vm_boundary peer_lease peers catalog_reload remote_guard: 35 passed
--test peer_lease 8, web_token_exposure 6, peer_integration 50, remote_new_hints 8, peer_origin 4, pairing 7, local_app_origin 7: all passed

The full cargo test -p acpmux fails on the tip the same way it fails here: agent_tools, codex_auth_broker, daemon_chat_open, ephemeral_run, hermetic_daemon_guard and session_pool.

Agent session: agent session
šŸ¤– Generated with Claude Code


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.


Summary by cubic

Implements the caller's side of the VM boundary: a caller marks a peer as a VM it owns, and its daemon forwards _meta.acpmux.vmBoundary: true on session/new for that peer. The mark comes from a saved peer's vmBoundary in config.json (a reload applies the file's value with no reconnect) or from _acpmux/peer_lease {vmBoundary: true}, which is held with the lease and never saved.

  • The boundary is forwarded only for the person's own clients: the unix socket, the app, and the caller's own peer daemon. Web clients, paired machines, and listener-token connections never get it, and any vmBoundary a client sends itself is dropped first.
  • Requests forwarded for a paired machine's client credential now carry via: device so the peer never treats them as the owner's.
  • The guest ignores the field until the next PR lands, so no behavior changes yet.