fix(desktop): normalize loopback host for HTTP writes and stop Reminders nav flicker#1086
Merged
Merged
Conversation
…ers nav flicker macOS resolves `localhost` to both `::1` and `127.0.0.1`. The relay binds `0.0.0.0` (IPv4 only), so reqwest's happy-eyeballs tries `::1` first and the connection is refused, surfacing as `relay unreachable: could not connect to relay` on every HTTP write while the WebSocket (which dials `127.0.0.1` directly) works. `relay_http_base_url` now rewrites an exact `localhost` host to `127.0.0.1`, scoped strictly to the loopback case so remote hosts and `localhost.evil.com` are untouched. `RemindersScreen` opened its own `["identity"]` query observer without a `staleTime`, firing a background refetch on mount that flipped every observer of that key to fetching — including the onboarding gate, which then swapped the router for the loading screen and trapped the user. It now consumes the shared `useIdentityQuery()` (staleTime: Infinity), removing the second observer. Co-authored-by: Will Pfleger <pfleger.will@gmail.com> Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two independent desktop fixes, surfaced together while dogfooding the reminder flow against a local relay.
Fix 1 — loopback host normalization
The desktop talks to the relay over two transports: WebSocket (subscriptions) and HTTP REST (every write). On macOS,
localhostresolves to both::1and127.0.0.1. A local relay binds0.0.0.0:3000(IPv4 only), so nothing listens on::1:3000. The WebSocket client dials127.0.0.1directly and connects, butreqwest's happy-eyeballs tries::1first for HTTP writes — the connection is refused,is_connect()is true, andclassify_request_erroremitsrelay unreachable: could not connect to relay. The result is a desktop that connects over WS but fails everycreate_channel/submit_eventagainst a local relay.relay_http_base_urlnow rewrites an exactlocalhosthost to127.0.0.1when deriving the HTTP base, soreqwestnever attempts::1. Scoped strictly to the loopback case:localhost(port and path preserved);ws:///wss://hosts and IP literals are untouched;ws://→http://andwss://→https://arms normalize identically;localhost.evil.comis not rewritten (exact-match, not prefix/substring).Fix 2 — Reminders nav flicker / trap
Navigating to
/remindersflickered "Setting up your workspace..." and trapped the user with no in-UI back.RemindersScreenrolled its ownuseQuery({ queryKey: ["identity"], queryFn: getIdentity })with nostaleTime(defaults to0). On mount it saw the cached["identity"]entry as stale and fired a background refetch. React Query flipsfetchStatustofetchingfor every observer of that key — including the onboarding gate'suseIdentityQuery(). The gate reads that asidentityIsFetching,resolveOnboardingGateStagereturnsblocking, andAppReadyswapsRouterProviderfor the loading screen, killing the nav chrome.RemindersScreennow consumes the shareduseIdentityQuery()(which setsstaleTime: Infinity), removing the second observer entirely. Navigating to/remindersno longer triggers an identity refetch, so the gate never flips toblocking. The!identityQuery.data?.pubkeyguard reads identically against the shared hook's return shape; the now-unuseduseQuery/getIdentityimports are dropped.