// a local box authorizing its own checkout for browser access. // // the bridge daemon serves the `plane:*` envelope, but it will not open a root // nobody has authorized, because a WebSocket to 127.0.0.1 is not subject to // CORS and any page the user visits can open one. so the box registers its // root and holds the socket for as long as it runs; when the box exits, the // registration goes with it and the tab loses reach. // // THIS NEVER BLOCKS THE BOX. an earlier version awaited the socket before // printing the prompt and cost 1450ms of start time on every box, because a // daemon built before `plane:*` existed accepts the socket, ignores the // message, and never replies, so the client sat on its timeout. a box that // cannot share its plane is a box with one capability missing, not a box that // starts slowly. // // IT ALSO REFUSES TO GUESS A PORT. when no lockfile claims a bridge, // resolveBridgeWorld reports source 'default' and hands back // DEFAULT_SOOTSIM_BRIDGE_PORT, which on a machine running a real daemon is // that daemon. registering there would attach a test's or a benchmark's box to // another session's daemon, which is exactly what a private RNX_HOME // exists to prevent. so a guessed port is refused by name. import { WebSocket } from 'ws' import { bridgeWsUrl, resolveBridgeWorld } from '../../ws-bridge' const REGISTER_TIMEOUT_MS = 1500 export interface PlaneShare { /** * resolves when the daemon has accepted the root, or with a reason it did * not. `holders` is how many boxes now hold this checkout, including this * one, so more than one means another box is writing the same tree. */ readonly settled: Promise<{ port: number; holders: number } | { declined: string }> close(): void } function target( explicitPort: number | undefined, ): { port: number } | { declined: string } { if (explicitPort) return { port: explicitPort } const world = resolveBridgeWorld() if (world.source === 'default') { return { declined: 'no daemon or dev bridge claims a port, and the default is another session’s daemon on a shared machine', } } return { port: world.bridgePort } } /** * register `root` with the bridge daemon in the background. returns * immediately; the box never waits on it. */ export function sharePlane(root: string, opts: { port?: number } = {}): PlaneShare { const chosen = target(opts.port) if ('declined' in chosen) { return { settled: Promise.resolve(chosen), close: () => {} } } const port = chosen.port let socket: WebSocket | null = null let closed = false const settled = new Promise<{ port: number; holders: number } | { declined: string }>( (resolve) => { let done = false const finish = ( value: { port: number; holders: number } | { declined: string }, ) => { if (done) return done = true clearTimeout(timer) resolve(value) } const timer = setTimeout(() => { // an older daemon accepts the socket and ignores plane:* entirely, so // silence is the expected shape of "not supported" rather than a fault. socket?.close() finish({ declined: `bridge on port ${port} did not answer plane:register` }) }, REGISTER_TIMEOUT_MS) try { socket = new WebSocket(bridgeWsUrl(port)) } catch (error) { finish({ declined: error instanceof Error ? error.message : String(error) }) return } if (closed) { socket.close() finish({ declined: 'box exited before the plane registered' }) return } socket.on('error', (error) => finish({ declined: error.message })) socket.on('close', () => finish({ declined: `bridge on port ${port} closed` })) socket.on('open', () => { // node sends no Origin, so this socket is already cli-role. a browser // client has to send bridge:hello to be corrected off the provisional // sim role; see plans/rnx-cloud/transport-decision.md. socket?.send(JSON.stringify({ type: 'plane:register', id: 'register', root })) }) socket.on('message', (data) => { let parsed: unknown try { parsed = JSON.parse(String(data)) } catch { return } if (typeof parsed !== 'object' || parsed === null) return const reply: Record = { ...parsed } if (reply.id !== 'register') return if (reply.error) { socket?.close() finish({ declined: String(reply.error) }) return } // the daemon nests its payload under `result`; reading holders off the // envelope silently reported 1 forever, which is the exact silence this // field exists to remove. const result = (reply.result ?? {}) as Record finish({ port, holders: Number(result.holders ?? 1) }) }) }, ) return { settled, close: () => { closed = true try { socket?.close() } catch {} }, } } /** * How many boxes hold `root` right now, without registering anything. * * `plane:register` cannot answer this: registering is what makes a root * reachable over a loopback socket, so a readiness probe that asked with it * would both authorize the root and count itself in the answer. */ export async function planeStatus( root: string, opts: { port?: number } = {}, ): Promise<{ holders: number; registered: boolean } | { declined: string }> { const chosen = target(opts.port) if ('declined' in chosen) return chosen const port = chosen.port return new Promise((resolve) => { let done = false let socket: WebSocket | null = null const finish = ( value: { holders: number; registered: boolean } | { declined: string }, ) => { if (done) return done = true clearTimeout(timer) try { socket?.close() } catch {} resolve(value) } const timer = setTimeout( () => finish({ declined: `bridge on port ${port} did not answer plane:status` }), REGISTER_TIMEOUT_MS, ) try { socket = new WebSocket(bridgeWsUrl(port)) } catch (error) { finish({ declined: error instanceof Error ? error.message : String(error) }) return } socket.on('error', (error) => finish({ declined: error.message })) socket.on('close', () => finish({ declined: `bridge on port ${port} closed` })) socket.on('open', () => { socket?.send(JSON.stringify({ type: 'plane:status', id: 'status', root })) }) socket.on('message', (data) => { let parsed: unknown try { parsed = JSON.parse(String(data)) } catch { return } if (typeof parsed !== 'object' || parsed === null) return const reply: Record = { ...parsed } if (reply.id !== 'status') return if (reply.error) { finish({ declined: String(reply.error) }) return } // the daemon nests its payload under `result`, the same shape // plane:register answers in. const result = (reply.result ?? {}) as Record finish({ holders: Number(result.holders ?? 0), registered: Boolean(result.registered), }) }) }) }