7ab01fe3
docs: correct the claims this change made stale
a73x 2026-08-28 19:53
Commit message
README.md
| Old | New | ||
|---|---|---|---|
| @@ -594,6 +594,8 @@ typing stops waiting for the round trip, and the two numbers come from one | |||
| 594 | keystroke on one connection so the gap is not an artefact of comparing | 594 | keystroke on one connection so the gap is not an artefact of comparing |
| 595 | runs. Auto-start is here for local unix-socket attaches — `mux` and `mux | 595 | runs. Auto-start is here for local unix-socket attaches — `mux` and `mux |
| 596 | --sock PATH` start a daemon when nothing is serving that path — and the wall | 596 | --sock PATH` start a daemon when nothing is serving that path — and the wall |
| 597 | starts the one on the default socket for the same reason. Not yet here: | 597 | starts the one on the default socket for the same reason. A remote daemon |
| 598 | auto-start on a remote host, the browser hub on the `hosts` file, reconnect | 598 | is started only by the dial a user ASKED for — `mux HOST`, a picker Enter — |
| 599 | backoff tuning, prediction for backspace and for multi-byte input. | 599 | and never by a dial nobody asked for. Not yet here: the browser hub on the |
| 600 | `hosts` file, reconnect backoff tuning, prediction for backspace and for | ||
| 601 | multi-byte input. | ||
docs/decisions.md
| Old | New | ||
|---|---|---|---|
| @@ -7360,19 +7360,18 @@ fd pair — and `live` becomes the high-water mark, growth counted in `present`. | |||
| 7360 | 7360 | ||
| 7361 | ## 2026-08-28 — a daemon starts when somebody asks, never from a read | 7361 | ## 2026-08-28 — a daemon starts when somebody asks, never from a read |
| 7362 | 7362 | ||
| 7363 | `muxd endpoint` auto-started a daemon before answering, and the wall runs it | 7363 | `muxd endpoint` auto-started one before answering, and the wall runs it over |
| 7364 | over ssh once a second per listed host: a listed box with nothing on it got a | 7364 | ssh once a second per host: a listed box got a daemon and a shell in session |
| 7365 | daemon and a shell in session 0 from a READ, and a `muxd stop` typed there was | 7365 | 0 from a READ, and a `muxd stop` typed there was undone a second later. |
| 7366 | undone a second later. `muxd proxy` had the same call. | 7366 | `muxd proxy` had the same call. Both now refuse an empty box. |
| 7367 | 7367 | ||
| 7368 | Both now refuse an empty box. The ASK moves to the client, on the dial a user | 7368 | The ASK moved to the client — `mux HOST`'s entry, the picker's Enter — which |
| 7369 | waited for — `mux HOST`'s entry, the picker's Enter — which runs `ssh HOST | 7369 | runs `ssh HOST 'muxd start'` from the same `handoff.recipeFor`, only when the |
| 7370 | 'muxd start'` from the same `handoff.recipeFor` and reads the announce once | 7370 | remote exited 1 (ssh's own 255 is not an empty box), and reads the announce |
| 7371 | more. `HandoffTarget.asked` carries that permission and gates the fallback | 7371 | ONCE more. `HandoffTarget.report_fallback` became `asked` and now gates the |
| 7372 | line with it; `wallview.dial` spends it on the first attempt, so a start per | 7372 | start as well as that line; `wallview.pumpTile` spends it on the dial that got |
| 7373 | backoff is unreachable. The local client's own `spawn.ensureForAttach` is | 7373 | the link, so no reconnect can restart a box someone stopped. The local |
| 7374 | unchanged. | 7374 | `spawn.ensureForAttach` is unchanged. |
| 7375 | 7375 | ||
| 7376 | The break, deliberate: an OLD client against a new remote no longer gets a | 7376 | The break: an OLD client on a new remote gets no daemon from a cold `mux |
| 7377 | daemon from a cold `mux HOST` — the user runs `ssh HOST 'muxd start'`, as | 7377 | HOST` — `ssh HOST 'muxd start'`, as README said. No xversion probe covers it. |
| 7378 | README already said. `test/xversion.sh` has no cold-HOST probe. | ||
src/cli/main.zig
| Old | New | ||
|---|---|---|---|
| @@ -942,7 +942,13 @@ fn logHint(alloc: std.mem.Allocator, buf: []u8) []const u8 { | |||
| 942 | 942 | ||
| 943 | /// `muxd proxy` with a one-line preamble: ensure a key, ask the daemon for | 943 | /// `muxd proxy` with a one-line preamble: ensure a key, ask the daemon for |
| 944 | /// its QUIC port, print `endpoint <port> <hex-key>` (or `endpoint none`) as | 944 | /// its QUIC port, print `endpoint <port> <hex-key>` (or `endpoint none`) as |
| 945 | /// the FIRST bytes on stdout, then become exactly the proxy byte pump. | 945 | /// the FIRST bytes on `out_fd`, then become exactly the proxy byte pump. |
| 946 | /// | ||
| 947 | /// `out_fd` is a parameter so a test can read the announce back instead of | ||
| 948 | /// writing over the runner's stdout, and every production caller passes | ||
| 949 | /// STDOUT_FILENO — which is what makes "the announce, then the frames, on | ||
| 950 | /// one stream" true, since `proxy.run` below writes the pump to the real | ||
| 951 | /// stdout and knows nothing of this argument. | ||
| 946 | /// This is what `mux HOST` runs over ssh — and what its wall polls, which | 952 | /// This is what `mux HOST` runs over ssh — and what its wall polls, which |
| 947 | /// is why no daemon means exit 1 and not a daemon. | 953 | /// is why no daemon means exit 1 and not a daemon. |
| 948 | /// | 954 | /// |
src/cli/mux_main.zig
| Old | New | ||
|---|---|---|---|
| @@ -398,10 +398,11 @@ fn defaultSock(alloc: std.mem.Allocator) !?[]const u8 { | |||
| 398 | }; | 398 | }; |
| 399 | } | 399 | } |
| 400 | 400 | ||
| 401 | /// Auto-start: give a local attach a daemon to land on. False when it | 401 | /// Auto-start: give a local attach a daemon to land on, and the ONLY |
| 402 | /// cannot — the line saying why is already printed. Unix-socket transport | 402 | /// auto-start left. False when it cannot — the line saying why is already |
| 403 | /// only: quic:// has nothing local to spawn, and `--via`'s auto-starter is | 403 | /// printed. Unix-socket transport only: quic:// has nothing local to spawn, |
| 404 | /// the remote proxy. | 404 | /// and `--via` starts nothing at all, because the remote proxy reads a box |
| 405 | /// (README's `ssh HOST 'muxd start'` is the answer there). | ||
| 405 | fn ensureLocalDaemon(alloc: std.mem.Allocator, sock_path: []const u8) !bool { | 406 | fn ensureLocalDaemon(alloc: std.mem.Allocator, sock_path: []const u8) !bool { |
| 406 | const muxd_path = try spawn.findInPath( | 407 | const muxd_path = try spawn.findInPath( |
| 407 | alloc, | 408 | alloc, |
src/client.zig
| Old | New | ||
|---|---|---|---|
| @@ -434,7 +434,7 @@ pub const Transport = struct { | |||
| 434 | error.UserAbort => return err, | 434 | error.UserAbort => return err, |
| 435 | else => { | 435 | else => { |
| 436 | // The one line the fallback is allowed, and only on the | 436 | // The one line the fallback is allowed, and only on the |
| 437 | // attach that made the choice — see `report_fallback`. It | 437 | // attach that made the choice — see `asked`. It |
| 438 | // names what was tried and what is happening instead, and | 438 | // names what was tried and what is happening instead, and |
| 439 | // no cause: the client cannot tell a blocked port from a | 439 | // no cause: the client cannot tell a blocked port from a |
| 440 | // wrong key (both are silence), and guessing would be worse | 440 | // wrong key (both are silence), and guessing would be worse |
test/e2e_03_side.sh
| Old | New | ||
|---|---|---|---|
| @@ -83,7 +83,8 @@ grep -q 'starting' "$OUT.as.err" && { | |||
| 83 | cat "$OUT.as.err"; exit 1; } | 83 | cat "$OUT.as.err"; exit 1; } |
| 84 | assert_converged "$OUT.as" "$SOCK14" "attach over proxy" | 84 | assert_converged "$OUT.as" "$SOCK14" "attach over proxy" |
| 85 | 85 | ||
| 86 | # Silence is the fast path: a warm attach must print NO spawn progress. | 86 | # Silence is now BOTH paths: nothing on this road starts a daemon, so no |
| 87 | # attach to it may print spawn progress. | ||
| 87 | pipe_mux "$OUT.as2" "$OUT.as2.err" env SHELL=/bin/sh timeout 30 "$MUX" --via "$MUXD proxy --sock $SOCK14" | 88 | pipe_mux "$OUT.as2" "$OUT.as2.err" env SHELL=/bin/sh timeout 30 "$MUX" --via "$MUXD proxy --sock $SOCK14" |
| 88 | pipe_send 'printf "auto-%%s\\n" again\n' | 89 | pipe_send 'printf "auto-%%s\\n" again\n' |
| 89 | await_out "$OUT.as2" "auto-again" "warm attach: marker never reached the client" | 90 | await_out "$OUT.as2" "auto-again" "warm attach: marker never reached the client" |
| @@ -99,7 +100,7 @@ grep -q 'starting' "$OUT.as2.err" && { | |||
| 99 | kill -0 "$APID" || { | 100 | kill -0 "$APID" || { |
| 100 | echo "e2e FAIL: warm attach replaced the daemon (pid $APID is gone)" | 101 | echo "e2e FAIL: warm attach replaced the daemon (pid $APID is gone)" |
| 101 | cat "$OUT.as2.err"; exit 1; } | 102 | cat "$OUT.as2.err"; exit 1; } |
| 102 | assert_converged "$OUT.as2" "$SOCK14" "auto-start warm attach" | 103 | assert_converged "$OUT.as2" "$SOCK14" "second attach over proxy" |
| 103 | 104 | ||
| 104 | # The verb under test is the teardown: exit 0, stopped line, socket gone, | 105 | # The verb under test is the teardown: exit 0, stopped line, socket gone, |
| 105 | # and the daemon OBSERVED dead by pid — never the command's claim alone. | 106 | # and the daemon OBSERVED dead by pid — never the command's claim alone. |
test/e2e_04_handoff.sh
| Old | New | ||
|---|---|---|---|
| @@ -149,7 +149,7 @@ grep -q '^muxd: starting' "$OUT.h1.err" || { | |||
| 149 | HAPID=$(sed -n 's/.* pid=\([0-9]*\).*/\1/p' "$OUT.h1.err" | head -1) | 149 | HAPID=$(sed -n 's/.* pid=\([0-9]*\).*/\1/p' "$OUT.h1.err" | head -1) |
| 150 | defer_kill "$HAPID" | 150 | defer_kill "$HAPID" |
| 151 | [ -n "$HAPID" ] || { | 151 | [ -n "$HAPID" ] || { |
| 152 | echo "e2e FAIL: endpoint up-line carries no pid"; cat "$OUT.h1.err"; exit 1; } | 152 | echo "e2e FAIL: muxd start's up-line carries no pid"; cat "$OUT.h1.err"; exit 1; } |
| 153 | kill -0 "$HAPID" 2>/dev/null || { | 153 | kill -0 "$HAPID" 2>/dev/null || { |
| 154 | echo "e2e FAIL: the handoff daemon (pid $HAPID) is not alive"; exit 1; } | 154 | echo "e2e FAIL: the handoff daemon (pid $HAPID) is not alive"; exit 1; } |
| 155 | 155 | ||