docs(relay): say plainly that the protocol negotiation is not reachable yet

Two claims that read as live and are not.

`relay-protocol-version.ts` documents a negotiation that no live path can reach, and
reads as the fix for #13852. It is not. The deploy path namespaces the relay directory
by the CLIENT'S OWN build hash — `remoteInstallDirName` is `relay-<fullVersion>` — and
the daemon socket lives inside it; the short-socket fallback derives its segment from
the same directory, and every `runConnectHandshake` caller takes its path from that one
deploy result. So a bridge can only ever meet a daemon of its own build,
`msg.version === launchVersion` short-circuits, and `relayProtocolOfferAdmits` never
decides anything. The stranded incumbent the feature exists for sits in
`relay-<otherVersion>/`, which nothing dials. What ships is three log lines;
`MIN_RELAY_PROTOCOL_VERSION` and the `minProtocolVersion` wire field have no live reader.

Keeping it is right — the mechanism is correct and hostile-input-safe (20 malformed and
hostile offers refused against a real daemon, which stayed up and still admitted a valid
cross-build offer afterwards), and it is the part that has to exist first. What was
missing is the statement of what else has to land with it, which is now written where
someone editing this file will see it: route the bridge to the incumbent's socket, and
stop `validateGrant` refusing on `serverBuildId`. That second one matters because its
premise, "client and relay ship in one build", is still TRUE today and becomes false the
instant the first lands — it is a second gate that would refuse what the handshake just
admitted, and shipping only the routing change would look like a regression in the
negotiation rather than a missed dependency.

`lingerMs` on the coordinator contract reads as a policy knob. No production path sets
it: the only `new RelayAuthCoordinator` is in `desktop-relay-service.ts` and passes
neither it nor `random`, so every shipped linger is the hardcoded ten-minute default.
Labelled a test seam so the next person tunes the default, which is the only thing a
user can feel.

No behaviour change. Both found by a dead-code and unread-field sweep over the stack.
This commit is contained in:
Neil
2026-09-10 17:54:24 -07:00
parent 9ff0af8b43
commit 94ba0b9f05
2 changed files with 37 additions and 0 deletions
@@ -33,6 +33,12 @@ export type RelayAuthCoordinatorOptions = {
refreshAccessToken: () => Promise<string | null>
}) => Promise<CoordinatedRelayBroker>
onStatus: (status: RelayBrokerStatus, cellUrl?: string) => void
/**
* Test seam, not a policy knob. The only production construction is in `desktop-relay-service.ts`
* and it passes neither this nor `random`, so every shipped build linger is the hardcoded
* ten-minute default in `relay-auth-coordinator.ts`. Read it as such before tuning it: changing
* the default is the only thing that can affect a user.
*/
lingerMs?: number
random?: () => number
}
+31
View File
@@ -13,9 +13,40 @@
*/
export const RELAY_PROTOCOL_VERSION = 1
/**
* ## This negotiation is not reachable yet. Do not read it as fixing #13852.
*
* Measured, not inferred: the deploy path namespaces the relay directory by the CLIENT'S OWN build
* hash — `remoteInstallDirName` is `relay-<fullVersion>` — and the daemon socket lives inside that
* directory (`ssh-relay-deploy.ts`, `--connect --sock-path ~/.orca-remote/relay-<ownVersion>/…`;
* the short-socket fallback derives its segment from the same version dir). Every
* `runConnectHandshake` caller takes its path from that one deploy result. So a bridge can only
* ever meet a daemon of its own build, `msg.version === launchVersion` short-circuits first, and
* `relayProtocolOfferAdmits` never decides anything on any live path. The stranded incumbent this
* exists for sits in `relay-<otherVersion>/`, which nothing dials.
*
* What ships today is therefore three log lines: `describeRelayProtocolVersion` in
* `relay-handshake.ts`. `MIN_RELAY_PROTOCOL_VERSION` and the `minProtocolVersion` wire field have
* no live reader at all.
*
* Keep it — the mechanism is correct and hostile-input-safe, and it is the part that has to exist
* first. Making it live needs two more changes, both of which must land together:
* 1. route the bridge to the incumbent's socket rather than to its own version directory;
* 2. stop `validateGrant` (`ssh-pty-consumer-session.ts`) refusing on `serverBuildId`. Its
* premise, "client and relay ship in one build", is still TRUE today and becomes false the
* moment (1) lands — it is a second gate that would refuse what the handshake just admitted.
*
* Until both land, a change here cannot be validated by any end-to-end test, only by the
* handshake's own unit suite.
*/
/**
* Oldest peer protocol this build can still speak. Raising it strands every relay below the new
* floor for good, so it moves only when serving a version is actually impossible.
*
* No live reader — see the note above. It is serialized onto every handshake and reply so that the
* field exists on the wire before any peer needs it (Rule 1, docs/reference/remote-wire-
* compatibility.md); an older peer ignoring it today is the point.
*/
export const MIN_RELAY_PROTOCOL_VERSION = 1