A live socket stays with the machine it was opened for

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Mitchell-Johnson
2026-09-21 23:53:18 -07:00
co-authored by Claude Opus 5
parent 3c7e46104c
commit 5412e316de
3 changed files with 18 additions and 2 deletions
+9
View File
@@ -580,6 +580,15 @@ against what it holds, a request against the page that should have made it.
5. ✅ **"Everything is up to date." sat in a 20px sliver of a card**, flush to its border, where
the package list it shares the card with is meant to be.
6. ✅ **A terminal of the hub's was asked for on a node.** Found in Firefox against the live
console, which says out loud what Chromium swallows: `wss://…/api/nodes/grid/terminals/
adfdeecb/attach`, where `adfdeecb` was one of the hub's — `grid` had no terminals at all. A
pane builds its address when it connects, from whichever machine the console is pointed at
*now*; switch machine while one is reconnecting and the reconnect goes to the new one. The
address is fixed when the pane opens. The same was true of a service's log socket, and is
fixed with it. Verified by walking the console around the fleet with a terminal open and
checking every socket against the machine that actually owns what it asked for.
## Checked and fine
Machine titles on every page and against an offline or never-reached machine; the colour a
+4 -1
View File
@@ -52,9 +52,12 @@ export function Logs({ unit }: { unit: string }) {
let readyTimer: number | undefined
let sock: WebSocket | null = null
let retry: number | undefined
// fixed when the log opens, for the same reason as a terminal's: a reconnect must not
// follow the console to another machine
const url = wsURLPath(`/api/services/${encodeURIComponent(unit)}/logs/tail?n=200`)
const connect = () => {
if (closed) return
const s = new WebSocket(wsURLPath(`/api/services/${encodeURIComponent(unit)}/logs/tail?n=200`))
const s = new WebSocket(url)
sock = s
s.onopen = () => { setLive(true); setErr(''); readyTimer = window.setTimeout(() => setReady(true), 1500) }
s.onmessage = (ev) => {
+5 -1
View File
@@ -60,9 +60,13 @@ export default function TerminalPane({ id, active, register, onStatus, onExit }:
sendResize()
}
// The address is fixed when the pane opens, not rebuilt on each attempt: it carries which
// machine this terminal belongs to, and the console may have moved to another machine
// before a reconnect fires — which had the hub's terminals being asked for on a node.
const url = wsURL(id)
const connect = () => {
if (closed) return
const sock = new WebSocket(wsURL(id))
const sock = new WebSocket(url)
sock.binaryType = 'arraybuffer'
ws = sock
sock.onopen = () => { retry = 0; setConn('open'); cbs.current.onStatus(true); lastSize = ''; doFit(); while (queued.length) sock.send(enc.encode(queued.shift()!)) }