acpmux: Linux hosts keep their daemon on the newest feat-cmux-next build by themselves
Lands after #24304. The update lock and the read of the daemon's own home and socket moved to #24304, which is in review; this PR keeps the self-update and its timer and gets a base merge once #24304 lands. The CI job that publishes the channel is #24256, whose merge is held for Leo's OK on trust and security (it gets the R2 credentials). Until #24256 lands, host self-update finds no channel and changes nothing.
Summary
Leo: "OUR SHIT IS HARDLY EVER GOING TO GO IDLE. IT JUST WONT. YOU'LL JUST HAVE TO ACCEPT IT BREAKING AT SOME POINT BUT IT RESUMES WHATEVER IT INTERRUPTED. END OF STORY FOR THE DAEMON UPDATE."
Leo: "BRO CANT YOU JUST LIKE SUSPEND SHIT AFTER A TOOL CALL/TURN THEN JUST GO AND AUTOUPDATE OUR TEAMMATES NEED ACPMUX UPDATING TOO..."
#24070 updates an ssh peer when a Mac running cmux NEXT connects to it. A Linux host that no app connects to stayed on its old acpmux. With this PR, Linux hosts keep their own daemon on the newest feat-cmux-next build:
- Channel. #24256 points
cmux-tui/channels/feat-cmux-next.jsonat each daemon-tested feat-cmux-next tree, forward only. The file holds the tree key, the commit and the sha256 of eachcmux-tui-acpmux-<target>. acpmux host self-update [--channel feat-cmux-next](Linux). It reads the channel. When the installed~/.local/bin/acpmuxhas another sha256, it downloads that host's binary over https from files.cmux.com and checks the sha256. Then it runs the same update ashost update:- the daemon restarts with its agents kept (
KillMode=process); - it is rolled back when the new daemon does not come up, and that build is not retried.
- the daemon restarts with its agents kept (
- One update per host at a time. The self-update takes the same lease as
host update(#24304), so a timer run and an update from the app never overlap. A run that finds the lease held changes nothing and tries again at the next tick. - Timer.
host setupand everyhost updateinstallacpmux-update.serviceandacpmux-update.timer(every 15 minutes, first run 5 minutes after boot) next toacpmux.service. A hand-written unit with either name is left alone. - Opt-out.
host setup --no-auto-updatewrites~/.acpmux/no-auto-update, which keeps the host off it.
Leo-ask: ask-48bbaec1d4
Leo-ask: ask-061e3574a6
Leo-ask: ask-98502f19a4
Changelog
- Added:
acpmux host self-updateand anacpmux-update.timerkeep a Linux host's acpmux daemon on the newest feat-cmux-next build (once #24256 publishes the channel). Agents keep running across the restart, and a build that fails to come up is rolled back. Opt out withacpmux host setup --no-auto-update.
Tests
host_self_update_tests.rs:- a host that already has the channel's binary is left alone;
- a newer build is downloaded, checked and installed;
- a download whose sha256 does not match the channel is never installed;
- a channel without this host's target is refused;
- a build whose daemon does not come back is rolled back and not tried again;
- one self-update runs at a time, and the opt-out stops them;
- a Cloud VM is refused before anything is read;
- the timer units install once, and a unit someone else wrote is left alone;
a_host_update_and_a_self_update_of_one_host_never_run_at_once: each waits for the other's lock, andacpmux.prevstays the known-good binary (fails without the lock inhost update);a_daemon_whose_socket_a_drop_in_moved_is_checked_where_it_listens: the update and a rollback's check reach the drop-in's socket (fails without the environment read).
host_update_tests.rsupdated for the shared update path.
Observed / Not run
-
Observed: at 05746cd7 on cmux16,
cargo test -p acpmux --lib cli::hostpasses 94 of 94, six runs in a row, andcargo clippy -p acpmux --lib --tests -- -D warningsis clean. -
Not run: no fleet host was updated by hand while developing, and no live daemon was restarted.
-
Size: about 2,090 changed lines until #24304 lands; after the base merge, about 1,400, of which about 570 are tests and about 300 are
command.rsmoving intocommand_host.rs(the Rust god-file limit). -
Observed (adopted by daemon-autoupdate-2 on huge-blue): six Codex review rounds (gpt-6-astra xhigh, read-only) fixed an install that never restarted (finished by the next run), the rollback backup (only a binary the answering daemon runs replaces
acpmux.prev), a rolled-back mark that cannot be written (said, and retried), unit files (staged, written beside and renamed; a failed write starts nothing) and the opt-out (stops only acpmux's own timer). Each fix has a regression test that failed without it, in the same fast-checks run as its green: runs 40265668516, 40266620552, 40267789646, 40272269777. On the final head 5fbfed5a (a base merge):cargo test -p acpmux --lib cli::203 passed,cargo test -p cmux-tui remote_cli_docs5 passed, fmt and clippy (--lib,--lib --tests) clean, run 40273398483. -
Not run: no live host update or restart with the timer; the app compile is CI's.
-
Declined review findings: a same-commit binary with another sha is not reinstalled (same code, as
host updatedecides);XDG_CONFIG_HOMEis ignored, as the base'sacpmux.serviceis; a no-ophost updateon a current host does not install the timer (setup and the next real update do). Follow-up: the timer install's refusals go to stderr and are not yet in setup/update's result rows.
Takes back #24304: nothing of its behaviour. Its lease now also covers the timer's host self-update, so the doc comments and the "another update of {} is running" message that named only host update, host setup and host enroll --fix were reworded to say so.
No capture: no UI.
Agent session: Air-Blue
š¤ Generated with Claude Code