8d698839
docs: Ctrl-\ c in the key table, and why a switch is an attach
a73x 2026-08-19 17:17
Commit message
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. | ||