acpmux: session/auth asks the peer and lists sessionAuth
Leo: “you gotta re-authenticate. You gotta log in again on the modal picker before the user even sends a prompt.”
This is the fourth of four PRs for _acpmux/session/auth, and it finishes the contract: it asks peers and lists the sessionAuth feature. It builds on #26700 (route probe and target), which has merged. Together with #26700, this was one PR. That PR got a SHIP from the reviewer and was split to stay under 800 lines; the reviewer then caught one more gap in this half (a target's routeId lost in the fallback replies), fixed in the last commit.
Peer link (evidence 1). A peer's chat, or a target whose host names a peer, is forwarded to that peer. The answer comes back with host set, and login.host set for a terminal login.
- If the link is down, or the request isn't sent, or the link drops under it, this daemon answers peerUnreachable. The source is peerLink and
evidenceAtis when the link went down. "Isn't sent" and "drops under it" arepeer_unreachableandpeer_disconnectedfrom #26756. - If the link is up but the peer doesn't answer in time, the answer is unknown.
- If the peer runs an acpmux that doesn't list
sessionAuth, the answer is unknown, with a detail naming its build andacpmux host update NAME. - Any other error is the peer's own answer (a chat it doesn't have, bad params) and is returned as is.
- The forward carries the Web origin mark (
remote_guard::mark_forwarded), so a browser page can't run a peer's probe or check through this daemon. - An unknown peer name is invalid params.
- A target's own
routeIdstays in every reply this daemon gives for a peer that can't answer; a chat's route is the peer's to know, so it stays null.
Listing.
sessionAuthis ininitialize's features.- The method is in
extensions. peer.rsreads the peer'ssessionAuthat handshake (supports_session_auth).capabilitieshassession-auth-v1.docs/acpmux-schema.jsonhas the method.peer_forwardstill excludes the method, so its own handler forwards it.
Tests. After is this head, on cmux-ci run 40694009841 at the rebased head (presence and subagent_retry in hub_integration are flaky on base; codex_auth_broker fails the same way on base, run 40665877991; the earlier head dfb39906 passed every suite on run 40669592664): fmt and clippy -D warnings are clean; acpmux lib, session_auth, hub_integration, the route suites, typed_errors, harness_health, claude_auth_env and no_keychain pass. The before runs, where each failed on the tree without its fix, are base 18af98cc with only the tests added (run 40634054284) and run 40664015700:
session_auth_asks_the_peer_and_answers_for_a_peer_that_is_down, with fake peer daemons:- A peer with the feature answers (signedOut, with
hostandlogin.hostset). - A peer whose link went down gives peerUnreachable.
- A peer that drops the link on the request gives peerUnreachable.
- A peer never reached, for a target, gives peerUnreachable with
evidenceAt. - A peer without the feature gives unknown, naming its build.
- A routed target on a peer that can't answer keeps its
routeId; this failed before the fix (run 40675958828). - Before: FAILED (run 40634054284). After: ok.
- A peer with the feature answers (signedOut, with
session_auth_is_listed_in_initialize: before FAILED (run 40634054284), after ok.server::session_auth::tests::a_link_failure_is_read_from_the_links_own_words: the link's "timed out after Ns", "connection closed" and "dropped the request", and never another peer's words. Before FAILED (run 40664015700), after ok.
Risk: low. It is read-only. A request goes to a peer only when its chat or target names that peer, and a peer that can't answer gets an answer from the link instead.
No capture: no UI changes; this is a daemon method no view calls yet.
Agent session: agent session
🤖 Generated with Claude Code