- Ya
- 3 Septemba 2026, 17:24 UTC
- Mwandishi
- Kamo
- Ahadi ya
- 969ccb7
GET /pty upgrades the connection to `kamo-pty` and becomes a shell: one PTY per browser tool window, `su - <user>` into `tmux new-session -A`, frames pumped both ways over the raw socket. A 101 rather than a WebSocket. The caller is SecurityService holding the bearer token, not a browser, so there is no reason to hand-roll RFC 6455 framing to stay stdlib-only. After the status line it is `uint32 length | uint8 type | payload`, and ThreadingHTTPServer's thread-per-connection is exactly the shape this needs. tmux, not a bare shell, and that is the whole reason it is here. A raw PTY dies with its socket, so a page refresh, a closed laptop or a wifi blip would kill a running build — and kamo-internal restores tool windows on load, so a restored terminal would come back holding a corpse. `new-session -A` makes first-connect and reconnect the same command. Security follows the file's existing rule: the username arrives already resolved from the member's own session and is re-validated against USERNAME_RE and the reserved list before any process starts, session ids are checked because tmux reads `.` and `:` as address syntax, sizes are clamped because they allocate a framebuffer on a shared machine, and a disabled account is refused even though `su` would still work for it. No password is read at all — the agent is root and drops to the account, so unlike the Guacamole path the stored credential never leaves the database. TERM is forced through `env` because `su -` rebuilds the environment from scratch, and a terminal that comes up as `dumb` has no vim, htop or less. Abandoned tmux sessions are pruned when that member next connects, which needs neither a timer nor a list of every account on the machine. 24 tests: argv shape, reserved/invalid refusals, size clamping, frame codec including a frame split across reads and an absurd declared length, control frame handling, and the reaper's four cases. 107 pass.