Skip to content

fix(desktop): normalize loopback host for HTTP writes and stop Reminders nav flicker#1086

Merged
wpfleger96 merged 1 commit into
mainfrom
duncan/loopback-and-reminders-fix
Jun 17, 2026
Merged

fix(desktop): normalize loopback host for HTTP writes and stop Reminders nav flicker#1086
wpfleger96 merged 1 commit into
mainfrom
duncan/loopback-and-reminders-fix

Conversation

@wpfleger96

Copy link
Copy Markdown
Collaborator

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, localhost resolves to both ::1 and 127.0.0.1. A local relay binds 0.0.0.0:3000 (IPv4 only), so nothing listens on ::1:3000. The WebSocket client dials 127.0.0.1 directly and connects, but reqwest's happy-eyeballs tries ::1 first for HTTP writes — the connection is refused, is_connect() is true, and classify_request_error emits relay unreachable: could not connect to relay. The result is a desktop that connects over WS but fails every create_channel/submit_event against a local relay.

relay_http_base_url now rewrites an exact localhost host to 127.0.0.1 when deriving the HTTP base, so reqwest never attempts ::1. Scoped strictly to the loopback case:

  • only the host segment is rewritten, and only when it is exactly localhost (port and path preserved);
  • remote ws:///wss:// hosts and IP literals are untouched;
  • both the ws://http:// and wss://https:// arms normalize identically;
  • localhost.evil.com is not rewritten (exact-match, not prefix/substring).

Fix 2 — Reminders nav flicker / trap

Navigating to /reminders flickered "Setting up your workspace..." and trapped the user with no in-UI back. RemindersScreen rolled its own useQuery({ queryKey: ["identity"], queryFn: getIdentity }) with no staleTime (defaults to 0). On mount it saw the cached ["identity"] entry as stale and fired a background refetch. React Query flips fetchStatus to fetching for every observer of that key — including the onboarding gate's useIdentityQuery(). The gate reads that as identityIsFetching, resolveOnboardingGateStage returns blocking, and AppReady swaps RouterProvider for the loading screen, killing the nav chrome.

RemindersScreen now consumes the shared useIdentityQuery() (which sets staleTime: Infinity), removing the second observer entirely. Navigating to /reminders no longer triggers an identity refetch, so the gate never flips to blocking. The !identityQuery.data?.pubkey guard reads identically against the shared hook's return shape; the now-unused useQuery/getIdentity imports are dropped.

…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>
@wpfleger96 wpfleger96 merged commit 771086f into main Jun 17, 2026
19 checks passed
@wpfleger96 wpfleger96 deleted the duncan/loopback-and-reminders-fix branch June 17, 2026 16:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant