a73x

8d698839

docs: Ctrl-\ c in the key table, and why a switch is an attach

a73x   2026-08-19 17:17

Commit message
docs: Ctrl-\ c in the key table, and why a switch is an attach

The README gained the chord and the one paragraph the multi-session story
was missing from the CLI's side: four named sessions, the first called 0,
and two ways to reach the others.

decisions.md records the two calls worth revisiting — that a switch
re-attaches rather than asking the daemon to move a live connection, and
that the client picks the new name from a list the daemon merely reports.

README.md
Old New
@@ -37,11 +37,17 @@ Inside a session:
37 |---|---| 37 |---|---|
38 | `Ctrl-\` | command prefix — press it, then a command key | 38 | `Ctrl-\` | command prefix — press it, then a command key |
39 | `Ctrl-\` `d` (or `Ctrl-\` `Ctrl-\`) | detach (session keeps running) | 39 | `Ctrl-\` `d` (or `Ctrl-\` `Ctrl-\`) | detach (session keeps running) |
40 | `Ctrl-\` `c` | create a session and switch to it (the old one keeps running) |
40 | `Shift+PageUp` / `Shift+PageDown` | scrollback (any other key returns to live) | 41 | `Shift+PageUp` / `Shift+PageDown` | scrollback (any other key returns to live) |
41 42
42 `mux` again reattaches — full TUI screens included. Kill the client with 43 `mux` again reattaches — full TUI screens included. Kill the client with
43 `kill -9` if you like; the session doesn't care. 44 `kill -9` if you like; the session doesn't care.
44 45
46 One daemon hosts up to four named sessions; the first is called `0`.
47 `Ctrl-\` `c` creates one named after the lowest free number and switches
48 to it, and `mux --session NAME` attaches to any of them by name (creating
49 it if there is room).
50
45 ## Quick start, remote over ssh 51 ## Quick start, remote over ssh
46 52
47 Works anywhere ssh works. On the remote host: put `muxd` on PATH, then: 53 Works anywhere ssh works. On the remote host: put `muxd` on PATH, then:
docs/decisions.md
Old New
@@ -3912,3 +3912,27 @@ compares the Origin header against the fixed strings `http://127.0.0.1:<port>`
3912 and `http://localhost:<port>`, and every mutating verb and every upgrade is 3912 and `http://localhost:<port>`, and every mutating verb and every upgrade is
3913 gated on that comparison. Leaking the names is the accepted cost of leaving GET 3913 gated on that comparison. Leaking the names is the accepted cost of leaving GET
3914 ungated; leaking a session is not, and does not happen. 3914 ungated; leaking a session is not, and does not happen.
3915
3916 **A switch is an attach, not a mode.** `Ctrl-\ c` ends the attached session and
3917 re-dials the same target under a new name: fresh transport, fresh replica,
3918 snapshot from seq 0. The alternative — a `switch_session` frame the daemon
3919 honours on a live connection — would have put the client's identity in the
3920 daemon's hands and given every session-scoped thing on that connection (the
3921 await watermark, the pending side events, the advertised size) a second way to
3922 change owner. Attaching already does all of it correctly, once. The seam is
3923 `attach()`, which loops on an `Outcome` from `session()` instead of returning
3924 its exit code; the transport was always built there, so a switch is one more
3925 turn around that loop rather than a new teardown path.
3926
3927 **The daemon names nothing; the client picks and the daemon refuses.** A new
3928 session is named after the lowest non-negative integer not already in use, so
3929 the series a user sees stays the one the default session `0` started, and a
3930 closed session's number comes back. That takes a list, which is all
3931 `sessions_req`/`sessions_reply` (0x0c/0x91) is: names '\n'-separated in slot
3932 order, no codec, safe to split because `validSessionName` admits no whitespace.
3933 Picking client-side means the pick can be refused — four sessions already exist
3934 — and the refusal is `exit_status 1` before any state, exactly like every other
3935 refused attach. That used to be fatal, and on a first attach it still is; a
3936 switch's arrival has somewhere to go instead, so it reports `.refused` and
3937 `attach()` returns to the session the chord was typed in with one line of
3938 explanation.