80c68b96
docs: side-channel passthrough, and what it measured
a73x 2026-08-16 07:44
Commit message
README.md
| Old | New | ||
|---|---|---|---|
| @@ -127,6 +127,14 @@ kills the daemon with them), though not a reboot. | |||
| 127 | `muxd run` refuses a socket another daemon already owns; there is no | 127 | `muxd run` refuses a socket another daemon already owns; there is no |
| 128 | socket-stealing. | 128 | socket-stealing. |
| 129 | 129 | ||
| 130 | Copy and paste work through the session: an application's OSC 52 write | ||
| 131 | reaches your terminal's clipboard (including from a remote box over QUIC, | ||
| 132 | where nothing else can), a paste arrives bracketed when the application | ||
| 133 | asked for bracketed paste, the window title follows the session, and a | ||
| 134 | bell rings. The clipboard READ direction (`OSC 52` query) is refused | ||
| 135 | deliberately — answering it would let anything in any session read | ||
| 136 | whatever you last copied. | ||
| 137 | |||
| 130 | ## How it works | 138 | ## How it works |
| 131 | 139 | ||
| 132 | Both ends run a real terminal engine. The daemon's is authoritative: shell | 140 | Both ends run a real terminal engine. The daemon's is authoritative: shell |
docs/decisions.md
| Old | New | ||
|---|---|---|---|
| @@ -3620,3 +3620,43 @@ alone. The claim that native selection works is reasoned plus confirmed in | |||
| 3620 | the negative (the escapes are absent); nobody has dragged a mouse across a | 3620 | the negative (the escapes are absent); nobody has dragged a mouse across a |
| 3621 | real mux screen to check wide chars and trailing spaces, and that is named | 3621 | real mux screen to check wide chars and trailing spaces, and that is named |
| 3622 | as unverified in `063cec67` rather than assumed. | 3622 | as unverified in `063cec67` rather than assumed. |
| 3623 | |||
| 3624 | ## 2026-08-16 (side-channel passthrough close-out) | ||
| 3625 | |||
| 3626 | **Classification came before implementation.** Four symptoms reduce to two | ||
| 3627 | delivery mechanisms, which is why this landed as one coherent change rather | ||
| 3628 | than four special cases. Sampled state — bracketed-paste mode and title — is | ||
| 3629 | read after the engine feed, sent on change and on attach, then undone when | ||
| 3630 | the client detaches. Events — clipboard writes and bells — are intercepted | ||
| 3631 | at the engine boundary, drained by the daemon and replayed by the client. | ||
| 3632 | That split gives a new client the state it needs on attach without turning a | ||
| 3633 | bell into state or losing the occurrence of a clipboard write. | ||
| 3634 | |||
| 3635 | **Replay follows the grid's own resync verdict.** Every event is stamped | ||
| 3636 | with the grid sequence current when it was observed. A reconnect receiving a | ||
| 3637 | delta gets the last pending event of each kind whose watermark it can serve; | ||
| 3638 | a reconnect receiving a snapshot gets none. Pending storage is one slot per | ||
| 3639 | kind, so a reconnect can deliver the current clipboard write and one bell, | ||
| 3640 | not a stale history or an unbounded queue. An event whose watermark becomes | ||
| 3641 | unservable is dropped rather than waiting indefinitely. | ||
| 3642 | |||
| 3643 | **Clipboard is write-only by design.** OSC 52 set is forwarded only for the | ||
| 3644 | allowed clipboard target and bounded base64 payload; its query/read form is | ||
| 3645 | refused. Replying to that query would make any program in any session able | ||
| 3646 | to read the host's last copied text, which is a boundary mux must not cross. | ||
| 3647 | |||
| 3648 | **The measurements inverted.** The discovery capture counted zero OSC 52 | ||
| 3649 | writes at the host; the e2e capture now counts one. The real-editor pin | ||
| 3650 | measured the nvim autoindent staircase changing from eight spaces to the | ||
| 3651 | four spaces the pasted block contains. The xterm title-stack probe was run | ||
| 3652 | in Alacritty and restored the original title, so mux pushes on terminal | ||
| 3653 | setup and pops on teardown rather than leaving the session title behind. | ||
| 3654 | |||
| 3655 | **The gates were made able to disagree.** Removing the clipboard | ||
| 3656 | interception makes precisely the new OSC 52 e2e fail. Neutering the | ||
| 3657 | bracketed-paste mirror makes the nvim scenario fail on the four-space | ||
| 3658 | assertion. Cross-version runs against the real v0.0.1-5 build passed on | ||
| 3659 | socket and QUIC; replacing the old client with the new one failed both legs, | ||
| 3660 | naming the clipboard, title and bracketed-paste frames it rendered. Bells | ||
| 3661 | coalesce to one replay per drain, so a binary full of BEL bytes cannot turn | ||
| 3662 | into one host-terminal write per byte. | ||
docs/roadmap.md
| Old | New | ||
|---|---|---|---|
| @@ -404,6 +404,21 @@ that understood the name can produce. | |||
| 404 | the M18 block drives it by name and asserts it returns one session's | 404 | the M18 block drives it by name and asserts it returns one session's |
| 405 | grid and not its neighbour's. `--diff-since` is still owed. | 405 | grid and not its neighbour's. `--diff-since` is still owed. |
| 406 | 406 | ||
| 407 | ## Side-channel passthrough — complete | ||
| 408 | |||
| 409 | Shipped 2026-08-16. The three discovery issues are now accounted for: | ||
| 410 | `7c777ec6`'s OSC 52 **set** reaches the host clipboard (the query direction | ||
| 411 | is deliberately refused, with a 64 KiB cap); `ee062dd9`'s bracketed-paste | ||
| 412 | half is mirrored; and `063cec67` remains the deliberately separate | ||
| 413 | copy-mode problem. Window-title state and bells rode the same delivery | ||
| 414 | mechanisms. | ||
| 415 | |||
| 416 | What remains banked is intentionally narrower: mouse mirroring, whose | ||
| 417 | terminal-side reporting would take away native drag selection until | ||
| 418 | copy-mode exists; OSC 10/11 colour queries; and browser clipboard support. | ||
| 419 | The cross-version gate now also proves, over socket and QUIC, that a real | ||
| 420 | v0.0.1-5 client ignores the new daemon-to-client frames and keeps parsing. | ||
| 421 | |||
| 407 | ## M16 candidates — and still outranked by trial feedback | 422 | ## M16 candidates — and still outranked by trial feedback |
| 408 | 423 | ||
| 409 | What remains is what was ranked behind M13 and M14 and survived M15, | 424 | What remains is what was ranked behind M13 and M14 and survived M15, |