fix: collapse loopback variants in normalize_host to prevent tenant split#2619
fix: collapse loopback variants in normalize_host to prevent tenant split#2619AIandI0x1 wants to merge 3 commits into
Conversation
…plit `normalize_host` (buzz-core::tenant) and `normalize_relay_url` (buzz-core::relay) disagreed on loopback address handling: `normalize_relay_url` collapses `localhost`, `127.0.0.1`, and `[::1]` to `127.0.0.1`, but `normalize_host` did not. This caused a silent tenant split in local development: - The desktop app spawns agents with `ws://127.0.0.1:3000` (after `normalize_relay_url`) - The frontend connects via `ws://localhost:3000` (default) - The relay's `bind_community` uses the raw `Host` header, so `127.0.0.1:3000` and `localhost:3000` map to **different communities** - Agents discover 0 channels because their community has no channel data - The UI shows no channels because its community is different/empty Additionally, `normalize_url` in buzz-auth::nip98 did not collapse loopback variants either, causing NIP-98 HTTP auth 401 failures when the client signs auth events with `http://localhost:3000/query` but the relay expects `http://127.0.0.1:3000/query`. This commit: 1. Adds `normalize_loopback_host()` to buzz-core::tenant and calls it from `normalize_host`, collapsing `localhost`, `127.0.0.1`, `0.0.0.0`, and `[::1]` to `127.0.0.1` (with port preserved). 2. Updates `normalize_url` in buzz-auth::nip98 to normalize the host via `normalize_host`, so NIP-98 URL comparison agrees with community binding. 3. Updates affected tests across buzz-core, buzz-auth, and buzz-relay. Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> Signed-off-by: AIandI0x1 <AIandI0x1@users.noreply.github.com>
c778018 to
b402cfd
Compare
|
P0 triage — merge candidate, one thing to verify. 🐝 Confirmed the tenant-split: The fix routes the authority through Security check before merge (the reason the old test existed): the old comment framed distinct loopback hosts as a deliberate "host-check side door" guard. Collapsing them is correct only for loopback — please confirm |
|
Thanks for the triage. Confirmed on both points: 1. Loopback-only — no routable host merging. 2. Parse-failure no-op is safe. In Net: the |
Problem
normalize_host(buzz-core::tenant) andnormalize_relay_url(buzz-core::relay) disagree on loopback address handling:normalize_relay_urlcollapseslocalhost,127.0.0.1, and[::1]to127.0.0.1normalize_hostdid not collapse loopback variantsThis causes a silent tenant split in local development:
ws://127.0.0.1:3000(afternormalize_relay_url)ws://localhost:3000(default)bind_communityuses the rawHostheader, so127.0.0.1:3000andlocalhost:3000map to different communitiesAdditionally,
normalize_urlinbuzz-auth::nip98did not collapse loopback variants either, causing NIP-98 HTTP auth 401 failures when the client signs auth events withhttp://localhost:3000/querybut the relay expectshttp://127.0.0.1:3000/query.Fix
buzz-core::tenant — Add
normalize_loopback_host()helper and call it fromnormalize_host. Collapseslocalhost,127.0.0.1,0.0.0.0, and[::1]to127.0.0.1(port preserved). This mirrors the loopback collapsing already innormalize_relay_url.buzz-auth::nip98 — Update
normalize_urlto normalize the host vianormalize_host, so NIP-98 URL comparison agrees with community binding.Tests — Updated affected tests across buzz-core, buzz-auth, and buzz-relay to use the canonical
127.0.0.1form. The previous testloopback_aliases_are_distinct_hosts(which asserted localhost ≠ 127.0.0.1) is replaced withloopback_aliases_collapse_to_same_host.Verification
cargo test -p buzz-core --lib tenant::tests— 11/11 passcargo test -p buzz-auth --lib nip98— 18/18 passcargo test -p buzz-relay --lib(affected tests) — 5/5 passlocalhost:3000and127.0.0.1:3000return the same data from the relay