Skip to content

fix(relay): warn at startup when the IPv6 loopback port is squatted#2559

Open
luthen-seas wants to merge 1 commit into
block:mainfrom
luthen-seas:fix/warn-ipv6-loopback-squatter
Open

fix(relay): warn at startup when the IPv6 loopback port is squatted#2559
luthen-seas wants to merge 1 commit into
block:mainfrom
luthen-seas:fix/warn-ipv6-loopback-squatter

Conversation

@luthen-seas

Copy link
Copy Markdown
Contributor

What

Adds a warn-only startup probe to buzz-relay: after binding, if the relay is bound IPv4-only (the 0.0.0.0:3000 default) and another process is listening on [::1]:<port>, log a warning naming the fix (BUZZ_BIND_ADDR=[::]:<port> or freeing the port). No binding behavior changes.

Why

On macOS, localhost resolves to ::1 first. With the IPv4-only default bind, any unrelated listener on [::1]:3000 (a Vite dev server is the classic case, since 3000 is a popular default) silently intercepts local desktop traffic. Dev servers answer GETs with 200 (SPA fallback) and writes with 404 — during first-launch onboarding this presents as relay returned 404 Not Found on profile save while the relay logs show zero inbound traffic. We lost a debugging session to exactly this. The desktop's HTML-interception detection (classify_intercepted_response) softens the symptom client-side, and #1245 fixed a sibling v4/v6 localhost issue for media proxy URLs, but nothing warns at the source.

Changing the default bind to [::]:3000 would fix it structurally but changes dual-stack behavior across platforms/containers — deliberately not done here; happy to discuss that as a follow-up if maintainers prefer it.

How

  • ipv6_loopback_squatter(port) — a 250ms TcpStream::connect_timeout probe against [::1]:<port>; runs once at startup, only when the bind address is IPv4 (an [::] bind would connect to itself).
  • Warning logged right after the buzz-relay TCP listening line.
  • No new unwrap()/expect(); probe failure can never affect binding.

Testing

  • Unit test ipv6_loopback_squatter_detects_listener (binds an ephemeral [::1] listener, asserts detection, then asserts clean after drop; skips silently on IPv6-less environments).
  • cargo fmt clean; cargo clippy -p buzz-relay --all-targets clean; cargo test -p buzz-relay ipv6_loopback_squatter passes.
  • Manual repro of the warning: bind anything on [::1]:3000 (e.g. a Vite dev server), start the relay with defaults, observe the warning.

🤖 Generated with Claude Code

The default bind is IPv4-only 0.0.0.0:3000, and localhost resolves
IPv6-first on macOS - so an unrelated process on [::1]:3000 (commonly a
Vite dev server) silently intercepts local clients. Dev servers answer
GETs with 200 and writes with 404, which presents as a relay bug during
first-launch onboarding.

Adds a warn-only probe after binding: when bound IPv4-only and something
is listening on the IPv6 loopback at the same port, log a warning with
the BUZZ_BIND_ADDR=[::]:<port> remediation. No binding behavior changes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@luthen-seas
luthen-seas requested a review from a team as a code owner July 23, 2026 16:46
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