feat(relay,desktop,trpc): presence from the relay, roster never polled
Why
The desktop polled v2Host.list every 30s per window. That procedure minted a JWT, called the relay's /presence, and the relay called back into host.checkAccess once per host on a cache miss: the API asking itself through Cloudflare. It was 27% of api requests plus a share of the 14% access checks on the Vercel spend page.
What
Roster and presence are separate now.
v2Host.listreturns membership only, takes the organization id as input (org-scoped query key), is fetched on mount, focus and reconnect, and is persisted through the query client's IndexedDB persister. The hand-rolleduseKnownHostssnapshot from the Electric era is deleted.- Presence is the state of each host's relay-routed event socket.
useKnownHostsholds one open per roster host; the relay preflight distinguishes not-connected (503) from denied (403), and an offline host is re-probed every 30s instead of on partysocket's seconds-scale backoff. - Every desktop consumer reads
useKnownHosts(); the presence polling hook is deleted. The host settings page keeps "last seen" through one on-demand relay read.
Relay
- The access decision is cached in the host's Durable Object storage keyed by user, so every isolate and colo shares one API call per (user, host) per TTL. A host that has never connected has no object and answers offline with no access check at all.
/presencetrusts API-minted scoped tokens (automation-presence,host-presence), so server-side reads skip access checks.
Also
- Host-service keeps the relay URL across reconnects and re-asks only after a connect attempt fails. Desktop holds it for the life of the window.
- Mobile merges relay presence in
useOrgHosts;isOnlineno longer comes from the API. - The automation failure badge polls every 120s instead of 30s; the automations pages every 60s.
Verified
Local dev stack (wrangler dev relay + api + desktop) driven over CDP:
- One
v2Host.listfetch per load, none over the following 100s. Zero/presencecalls from the renderer. - Offline roster hosts re-probed at 33s, 64s, 96s; one event socket for the online host; never-placed hosts answered 503 in a few ms with no API call.
- Roster present in the persisted IndexedDB cache; Hosts settings shows online/offline groups from socket state; offline host page renders with one on-demand presence read.
- Durable Object cache: after an isolate reload with the front cache cold, the probe returned 200 with no
host.checkAccesson the API and no "access check via api" log line, while the first cold probe did go to the API once.
Suites: desktop 3751 pass, host-service 1720 pass, mobile 91 pass, workspace-client 29 pass, trpc 253 pass (4 pre-existing sitemap.test.ts env failures).
Plan: plans/20260911-remote-hosts-realtime.md (local, not committed). PR 2 adds the realtime nudge channel.
https://claude.ai/code/session_01LrqbaXvTkvex2tarYQPd59
Summary by cubic
Separates host roster membership from live presence so the desktop and mobile stop polling the API every 30s, which was driving 27% of API requests and repeated access checks.
Roster and presence
v2Host.listnow returns membership only, is org-scoped by input, fetched on mount/focus/reconnect, and persisted via the query client's IndexedDB persister.- Desktop and mobile read presence from each host's relay event socket; offline hosts are re-probed every 30s instead of using partysocket's seconds-scale backoff.
- The hand-rolled
useKnownHostssnapshot and the Electric-era sidebar resilience doc are deleted;isOnlineno longer comes from the API. - Automation lists and failure badges poll every 60−120s instead of 15−30s.
Relay
- Each host's Durable Object authorizes its own calls, caching the decision keyed by user, so all isolates and colos share one API call per (user, host) per TTL.
- A host that has never connected returns 503 with no access check.
/presenceskips the access check for API-mintedautomation-presencetokens, so server-side reads skip access checks.- Host-service re-asks for the relay URL only after a connect attempt fails.
The organizationId input to v2Host.list is optional so older clients keep scoping by the active-org header; the fallback is dated for removal once the fleet has moved past 1.28.
Summary by CodeRabbit
-
New Features
- Host settings now show when an offline host was last seen.
- Relay requests now apply access permissions consistently.
-
Improvements
- Host availability is presented more consistently across desktop and mobile host pickers, workspace views, and settings.
- Automation and host data refresh less frequently to improve efficiency.
- Connection retries better handle temporarily offline hosts.
- Host lists can be restored after reloads through cached query data.