d825ab6a
docs: a daemon lives until muxd stop
a73x 2026-08-28 19:53
Commit message
CLAUDE.md
| Old | New | ||
|---|---|---|---|
| @@ -117,6 +117,13 @@ real pty), `wsclient` (browser stand-in), `rawmode`, `delaypipe`, `render`. | |||
| 117 | a masterless pty across the exec. A daemon with no arm answers nothing | 117 | a masterless pty across the exec. A daemon with no arm answers nothing |
| 118 | and the client banners `[daemon too old to end a session]`. The tile | 118 | and the client banners `[daemon too old to end a session]`. The tile |
| 119 | leaves on the next list, not on the keypress. | 119 | leaves on the next list, not on the keypress. |
| 120 | - **A daemon lives until `muxd stop`; emptiness is not an exit.** `x` ends a | ||
| 121 | session, never a box: `reap` and `pumpOnce` answer nothing, a shell's code | ||
| 122 | reaches that shell's own clients as `exit_status`, and an emptied daemon | ||
| 123 | keeps serving so a birth can take the default name back. One idle process | ||
| 124 | per machine is the accepted cost. `muxd run` exits 0 on every shutdown — | ||
| 125 | a supervisor reads nonzero on a clean stop as a crash — and nonzero only | ||
| 126 | for the boot failures `main.run` picks before anything binds. | ||
| 120 | - **The layout sidecar is derived convenience, not authored intent.** | 127 | - **The layout sidecar is derived convenience, not authored intent.** |
| 121 | `$XDG_STATE_HOME/mux/layout` stores the pane tree on last detach, from | 128 | `$XDG_STATE_HOME/mux/layout` stores the pane tree on last detach, from |
| 122 | every wall left on a TERMINAL (`shared.is_tty`) — a piped `mux` is a wall | 129 | every wall left on a TERMINAL (`shared.is_tty`) — a piped `mux` is a wall |
README.md
| Old | New | ||
|---|---|---|---|
| @@ -114,6 +114,11 @@ later; there is no session that cannot be ended. Dropping a whole MACHINE | |||
| 114 | is `mux hosts rm`, not a chord: it is rarer than ending a shell and must not | 114 | is `mux hosts rm`, not a chord: it is rarer than ending a shell and must not |
| 115 | sit one key from `n`. | 115 | sit one key from `n`. |
| 116 | 116 | ||
| 117 | `x` ends a session, never a box. A daemon whose last session ends stays up | ||
| 118 | and empty, and the next `mux HOST` is born into it — so emptying a machine | ||
| 119 | costs you nothing but the shell you meant to end. Ending the daemon itself | ||
| 120 | is `muxd stop`. | ||
| 121 | |||
| 117 | A session whose shell exits ends `mux` with that shell's exit code when it | 122 | A session whose shell exits ends `mux` with that shell's exit code when it |
| 118 | was the only tile on the wall — so `mux` stays something you can put in a | 123 | was the only tile on the wall — so `mux` stays something you can put in a |
| 119 | script — and drops you to the wall when it was not, with the tile that ended | 124 | script — and drops you to the wall when it was not, with the tile that ended |
| @@ -505,7 +510,9 @@ wins). A session survives logout (this assumes systemd-logind's default | |||
| 505 | `KillUserProcesses=no`; a box configured to kill user processes at logout | 510 | `KillUserProcesses=no`; a box configured to kill user processes at logout |
| 506 | kills the daemon with them), though not a reboot. | 511 | kills the daemon with them), though not a reboot. |
| 507 | `muxd run` refuses a socket another daemon already owns; there is no | 512 | `muxd run` refuses a socket another daemon already owns; there is no |
| 508 | socket-stealing. | 513 | socket-stealing. It runs until something stops it — no session's exit ends |
| 514 | it — and exits 0 when `muxd stop` or a supervisor's SIGTERM asks; a nonzero | ||
| 515 | exit means it never got as far as serving. | ||
| 509 | 516 | ||
| 510 | Rolling a new daemon out does **not** mean killing the sessions. Install the | 517 | Rolling a new daemon out does **not** mean killing the sessions. Install the |
| 511 | new binary and run `muxd upgrade` — the new binary asks the running daemon | 518 | new binary and run `muxd upgrade` — the new binary asks the running daemon |
docs/decisions.md
| Old | New | ||
|---|---|---|---|
| @@ -7302,3 +7302,22 @@ writes that file any more, so a fresh machine's hub wall is empty until | |||
| 7302 | someone names tiles on its command line or in the page. Stated in the README | 7302 | someone names tiles on its command line or in the page. Stated in the README |
| 7303 | rather than smoothed over, because a hub that quietly shows a different wall | 7303 | rather than smoothed over, because a hub that quietly shows a different wall |
| 7304 | from the terminal is the two-lists bug again, one layer out. | 7304 | from the terminal is the two-lists bug again, one layer out. |
| 7305 | |||
| 7306 | ## 2026-08-28 — a daemon lives until `muxd stop` | ||
| 7307 | |||
| 7308 | **`x` ends a session, never a box.** `reap` returned the last dead shell's | ||
| 7309 | code and `pumpOnce` handed it to `run`, so a daemon left with its last | ||
| 7310 | session. Invisible while the wall listed daemons from a file; with the wall | ||
| 7311 | listing live sessions and a picker offering hosts to be born into, it means | ||
| 7312 | emptying a host destroys it and the picker offers what is not there. | ||
| 7313 | |||
| 7314 | So `reap` and `pumpOnce` answer nothing. A shell's code goes to that shell's | ||
| 7315 | own clients (`exit_status`) and nowhere else; an emptied table is a daemon | ||
| 7316 | with nothing on it. The picker says `no sessions`, a birth brings it back, | ||
| 7317 | and one idle process per machine is the accepted cost — one daemon per | ||
| 7318 | machine is the real use. | ||
| 7319 | |||
| 7320 | `muxd run` exits 0 on every shutdown, `muxd stop` and SIGTERM alike: a | ||
| 7321 | supervisor reads a nonzero exit on a clean stop as a crash. Nonzero is a boot | ||
| 7322 | failure only, stated in `main.run`'s doc block. The 130 that imitated SIGINT | ||
| 7323 | went with the shell's code. | ||