From fafc8789eb547c50601740eeaaed668ff92e6c3a Mon Sep 17 00:00:00 2001 From: ygq Date: Sun, 2 Aug 2026 12:45:07 +0800 Subject: [PATCH] fix(relay): normalize loopback relay URL to localhost for managed agents MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Managed agents spawned by the desktop launcher dialed ws://127.0.0.1:PORT even when the operator configured ws://localhost:PORT, because normalize_relay_url collapsed loopback to 127.0.0.1. The relay resolves community by the connection Host header, and the dev seed registers localhost (not 127.0.0.1) as a community host, so agents landed in a separate empty community and discovered 0 channels — while the desktop UI, which dials the URL the operator entered, worked in the same instance. Canonicalize loopback to localhost so the agent's dial host matches the host the relay's community lookup resolves. This is the only non-test call site of normalize_relay_url (runtime_types.rs:22); the NIP-42 AUTH comparison helper in buzz-auth/src/nip42.rs is a separate function and is not touched. The relay-side normalize_host cannot collapse loopback without breaking NIP-98 (nip98_expected_url builds the expected URL from tenant.host(), which would diverge from the agent's signed u tag). Refs #3505 #3283 #3033 #2444 Co-Authored-By: Claude Opus 4.8 (1M context) --- crates/buzz-core/src/relay.rs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/crates/buzz-core/src/relay.rs b/crates/buzz-core/src/relay.rs index 77c74a069a..294f5b0a47 100644 --- a/crates/buzz-core/src/relay.rs +++ b/crates/buzz-core/src/relay.rs @@ -54,7 +54,13 @@ pub fn normalize_relay_url(raw: &str) -> Result Host::Ipv6(address) => address.is_loopback(), }; if loopback { - url.set_host(Some("127.0.0.1")) + // Normalize all loopback forms to "localhost" (not 127.0.0.1) so the + // canonical URL matches the host the relay's community lookup resolves + // (the dev seed registers `localhost` as a community host). Pinning to + // 127.0.0.1 here previously caused managed agents — spawned with this + // canonical relay URL — to land in a separate, empty `127.0.0.1` + // community and discover 0 channels. + url.set_host(Some("localhost")) .map_err(|_| NormalizeRelayUrlError::MissingHost)?; } else if let Host::Domain(domain) = host { let lowercase = domain.to_ascii_lowercase(); @@ -88,7 +94,11 @@ mod tests { let localhost = normalize_relay_url("wss://localhost/").unwrap(); assert_eq!(ipv6, ipv4); assert_eq!(ipv4, localhost); - assert_eq!(localhost, "wss://127.0.0.1"); + // Canonical loopback form is "localhost", not "127.0.0.1": the dev seed + // (scripts/seed-local-community.sh) registers `localhost` as a community + // host, so a canonical relay URL pinned to 127.0.0.1 landed managed + // agents in a separate, empty community. See issue #3505. + assert_eq!(localhost, "wss://localhost"); } #[test]