Cloud VM image: cmux-rd desktop host on the agent display behind a root-only socket (role desktop)
Why
Leo, 2026-11-05:
"Also go check in on the state of affairs of screen sharing remotely on these machines, like the cloud machines, because there was so much VNC work. But it's also like, I feel like it wasn't really linked up end to end here, and we have an opportunity quite soon to start doing that. [...] And then it became AI computer use!!"
Leo, on cmux#36080: "is that really necessary? can't you both do it at the same time?"
Leo: "don't stick it into dev. Put it into prod".
This is the image slice of the Cloud desktop: the person's view of a Cloud machine's agent display :99 is cmux.rd/1, carried over the owner's Native link as link service desktop. The machine's link receiver (the session daemon's __receive-vm, which runs as user cmux) admits the owner's link token for desktop and connects the host's socket; systemd then starts the cmux-rd host on :99 as its own user.
What
--agent-tools (the option the production image is baked with) now also bakes:
- System user
cmux-rd:useradd --system --user-group --no-create-home --home-dir /nonexistent --shell /usr/sbin/nologin; the bake asserts its only group iscmux-rd, it has no sudo, andcmuxis not in its group. cmux-agent-rd.socket:ListenStream=/run/cmux-rd/desktop.sock,SocketUser=root,SocketGroup=cmux,SocketMode=0660(the link receiver runs ascmux), in/run/cmux-rd(0711 root:root, tmpfiles andDirectoryMode),Service=cmux-agent-rd.service,WantedBy=sockets.target, enabled and listening at bake (so in the snapshot too) with the host not running.cmux-agent-rd.service(no[Install], only the socket starts it):User=cmux-rd,BindsTo=/After=cmux-agent-display@99.service,ExecStart=<store>/bin/cmux-rd host --listen-systemd --peer-uid 1000 --display :99 --xauthority /run/cmux-rd-host/Xauthority-99 --codec openh264. The binary path comes from the lock entry's store path, and the peer uid isDEVBOX_WORK_UID, the uid the link receiver runs as (the bake checks it againstid -u cmux).RuntimeDirectory=cmux-rd-host(cmux-rd 0700) holds the cookie copy, apart from the socket directory. Sandbox:RestrictAddressFamilies=AF_UNIX(the activated socket and the X display are both Unix sockets), an emptyCapabilityBoundingSet=,NoNewPrivileges,ProtectHome=yes,ProtectSystem=strict,PrivateTmp=yeswith/tmp/.X11-unixbound read-only,PrivateDevices,RestrictNamespaces,RestrictSUIDSGID, and more.- The cookie copy (root
ExecStartPre=+): root never opens the agent's cookie, because/home/cmux/.cache/cmux-display/Xauthority-99is a path the agent controls. It is read ascmux(setpriv --reuid=cmux --regid=cmux --clear-groups --inh-caps=-all --bounding-set=-all --no-new-privs). Only a regular file that is not a symlink is accepted, read within 5 s and capped at 4096 bytes, into a temp file in/run/cmux-rd-host. The temp file is created exclusively (set -C,umask 077, after a check that the directory is cmux-rd's own 0700 directory and not a link), handed over withchown -hand renamed toXauthority-99. The script has no systemd%specifier and no$expansion. A test runs the script against a regular file, a symlink, a FIFO, an empty file and an oversized file. cmux-agent-rd-openh264.service(oneshot,User=cmux-rd,StateDirectory=cmux-rd,ConditionPathExists=!its marker):cmux-rd openh264-installon the first start only (Cisco's binary over HTTPS, never bundled). It is the only cmux-rd unit allowedAF_INET/AF_INET6. The hostRequires=/After=it.- Lock program
cmux-rd(added toOPTIONAL_PROGRAMS, roledesktop, baked):cmuxRdReleaseShape(V)is the releasecmux-rd-vVon manaflow-ai/cmux,cmux-rd-V-x86_64-unknown-linux-gnu.tar.gzchecked against itschecksums.txt. The archive layout is the one #36433'sscripts/release.pypacks:bin/cmux-rd, plusLICENSEandTHIRD_PARTY_LICENSES.md(notices, which the install checks) at the root. A test packs an archive the same way and runs the real install command on it. The validator refuses another URL, a missing checksums.txt, a missingdesktoprole or a first-use entry. - Pinned:
cmux-rd0.1.0 from releasecmux-rd-v0.1.0(#36433, merge11edd026, release run 42028500138): sha2568118a53a809e9204e68bb692e88ec1f429c897380c558b98604046dca2d29e73, 6868976 bytes. These agree with the releasechecksums.txt, the asset digest and a download. The archive holdsLICENSE,THIRD_PARTY_LICENSES.mdandbin/cmux-rd. - Start limit:
StartLimitIntervalSec=0on the host. With a start limit, a run of failed starts would leavecmux-agent-rd.socketfailed (service-start-limit-hit) until areset-failed. Without it, each start is one admitted connection, a failing host fails only that connection, and the next one tries again. - Bake record:
result.desktop(role: desktop, program, version, socket) is written for each image that has the role, so the platform can derivedesktop: truefrom the image record and never from the provider. - agent-tools-probe desktop checks:
cmux-rdexists with nologin and only its own group;/run/cmux-rdis711 root:rootand the socket660 root:cmux; socket enabled and active, host inactive; the host's--peer-uidequalsid -u cmux, and itsRestrictAddressFamiliesisAF_UNIX; asnobody, connecting givesEACCES; a held connection ascmux(the receiver's uid) starts the host, which runs ascmux-rdwith--listen-systemd --peer-uid 1000and has no TCP or UDP socket; the cookie copy is600 cmux-rdin a700 cmux-rddirectory; and, while the connection is still held (the host running, its copy present),cmuxopening the copy fails withEACCES.ENOENT(a copy already removed with an idle host) fails the check.
Where the boundary is
The boundary is the link admission. Only the owner's app, through the owner's platform-minted, one-time link token for service desktop (verified by the receiver with its replay history and receiving policy), reaches this host from outside the machine.
On the machine the socket is not a boundary against the agent, and this PR does not claim it is. The link receiver runs as cmux, so the socket admits cmux (and the host's --peer-uid is cmux's uid). The agent is cmux too; it already reaches :99, its own display, and has NOPASSWD:ALL sudo (workUser.ts). The socket mode keeps every other local user out (nobody gets EACCES). The separate cmux-rd user and its private directories keep the host's files (cookie copy, OpenH264 state) out of the agent's reach, so the agent can't get at them without sudo. The host has no network family and no capabilities.
This differs from the shared brief's first table, which had a root receiver and a root-only socket. The receiver turned out to run as cmux.
The unit uses BindsTo= on the display, which includes Requires=, so that a restarted display (a new cookie) restarts the host.
Pending (not in this PR)
- The bake:
.github/workflows/cloud-vm-image-bake.ymlwithmode=bake,agent_tools=true,keep_snapshot=true; thenagent-tools-probe.ts --snapshot <sh-id>with everyrd-*check passing (the first host start downloads OpenH264); thenmode=prod-copywithdev_snapshot=<sh-id>and aVM_BOOT_IMAGESentry. - The host README from #36433 still describes
--peer-uid 0(a root receiver). The host takes any uid from that flag, so nothing breaks with--peer-uid 1000; the README wording is a follow-up there.
Verification
Fleet, at head 985e8b3e:
- every
tests/vm-image-*.test.tsplustests/vm-devbox-image.test.ts: 402 tests, 0 fail (run 42035021538).vm-image-restricted.test.tsruns on its own (7 pass), because itsmock.moduleof the manifest does not take effect once another file in the same bun process has loaded the resolver; in one combined process 3 of its tests fail. This PR does not touch that test or the manifest it mocks; I did not run the combined process on the base. tsgo --noEmitoverweb/: exit 0, with all six changed files in the program.tsgo --noEmitoverweb/exits 0 with all six changed files in the program.
Agent session: agent session
š¤ Generated with Claude Code