Hosted Relay sockets: one Durable Object per account - #869
Conversation
Deploying mouseterm with
|
| Latest commit: |
f07045c
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://462e1895.mouseterm.pages.dev |
| Branch Preview URL: | https://remote-network-hosted-do.mouseterm.pages.dev |
6286a90 to
465ca9e
Compare
dormouse-bot
left a comment
There was a problem hiding this comment.
This is early feedback on a draft, not a merge verdict. Mark the PR ready when you want the full review.
There's one inline point: the object holds blockConcurrencyWhile while it waits on the row read through RelayRows, so a stalled database read could reset the whole account's object rather than fail only the one upgrade.
465ca9e to
2aeaad6
Compare
dormouse-bot
left a comment
There was a problem hiding this comment.
I found nothing to change in the diff. The row read the draft review raised now gives up at RELAY_ROW_READ_TIMEOUT_MS and answers 503, and the stalled-read test covers that.
I'm leaving this as a comment rather than an approval because the Argos checks are red (argos/storybook-chromium, argos/storybook-webkit: 33 changed, waiting for a decision). The same 33-screenshot diff is on #867 and #868, and this PR touches no UI, so the diff most likely comes from the base of the stack. Accepting or rejecting it in Argos is up to a maintainer.
RelayRoom reproduces the self-host Relay's routing — bind on init, client-gone, burrow-gone, 4000/4001/1008/1009/1013 — over WebSocket hibernation, rebuilding from attachments and tags and storing only the account id it checks every socket against. The relay Worker authenticates each upgrade and hands the object only verified fields; removing a computer closes its socket through an RPC with no public route. Both ends ping the relay with an auto-response that never wakes the object, enforcing a deadline only once a pong has arrived, and the self-host Relay answers it too. The two Relays share one parity suite. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…hourly Review fixes: the object reads the Burrow row before accepting its socket and every hour while it holds Burrows (through a RelayRows entrypoint, since a Hyperdrive connection pins the object out of hibernation), so a removal race or a lost entitlement closes the socket within the hour. Frames are bounded in UTF-8 bytes, the frame layer is one shared module the e2e lint holds textually, and one visibility handler drives both Pocket timers. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts: # scripts/spec-word-budgets.json
2aeaad6 to
f07045c
Compare
|
@dormouse-bot The maintainer has explicitly instructed us to IGNORE ARGOS for this stack and will handle the visual decisions separately. All other CI must pass, and we need your approval before merging. Please review current head f07045c and approve if no findings remain; please do not merge, since I am handling the stack bottom to top. The account object's row read remains bounded by |
Third of the Hosted persistent stack, on #868. It adds the Hosted Relay's sockets:
/ws/burrowand/ws/clientonrelay.dormouse.sh, routed through oneRelayRoomDurable Object per account.Why per account, not per Burrow (decided 2026-09-30): Pocket keeps one relay socket for every Burrow it talks to and binds with
init. An account-keyed object keeps that wire and Pocket unchanged. It also means an object only ever holds one account's sockets, so a cross-tenant binding can't happen.The object:
RelayHubexactly: bind on init,client-gone,burrow-gone, and close codes 4000, 4001, 1008, 1009, 1013. The frame layer (guards, error strings, field-by-field envelopes) is now one shared module,remote-lib-common/src/remote/relay-routing.ts. One parity suite runs against both Relays.ct;scripts/e2e-lint.mjsholds that textually, with self-test mutations.Origin; a Burrow upgrade refuses anyOrigin.hosted.dormouse.shcloses the live socket through an RPC with no public route. A backstop alarm rechecks every held Burrow's row and its owner's entitlement hourly.RELAY_PINGevery 30 s to the object's auto-response. Each end enforces its pong deadline only once a first pong has arrived, so an older self-host Relay costs nothing. The self-host Relay now answers the ping too.Not verified against Cloudflare: the object reads Postgres through a
RelayRowsentrypoint viactx.exports. In Miniflare, an object that opens a Hyperdrive connection itself can never hibernate again. Thectx.exportspath works in Miniflare andwrangler deploy --dry-run, but hasn't been tried in production.Test plan
pnpm testgreen. The Hosted Docker suite passed 128/129 just before a one-line rebase fix in a Docker-free test, and its runs before the rebase were green. It covers parity cases, hibernation eviction mid-routing, the alarm and sweep, tenant isolation, revocation, and a never-waking ping.🤖 Generated with Claude Code