From ecea41e4274c0082f4c94540012e53896c9f5d38 Mon Sep 17 00:00:00 2001 From: John Tennant Date: Thu, 5 Nov 2026 14:12:55 -0500 Subject: [PATCH] fix(communities): find self-hosted relays that predate host-bound NIP-11 The pre-join lookup treated a Buzz NIP-11 document without read_state_snapshot, buzz_v1 or icon as "No community is set up at this address". The released relay (relay-v0.2.1, the Helm chart's image) never sends the first two and omits icon until an admin sets one, so every self-hosted community without an icon was refused, invite links included. Classify "no community" from the relay's own unbound-host answer (a 404 with "relay: no community is configured for this host") and accept any other Buzz NIP-11 document. Co-authored-by: Martha <73923046810b68ae182000e74ef7c444383a907df20f81f2ba1008870a6f45a1@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 Signed-off-by: John Tennant --- .../communities/FindCommunity.test.tsx | 4 +++- .../communities/find-community.test.ts | 21 +++++++++++++++++-- src/features/communities/find-community.ts | 15 ++++++------- 3 files changed, 30 insertions(+), 10 deletions(-) diff --git a/src/features/communities/FindCommunity.test.tsx b/src/features/communities/FindCommunity.test.tsx index dfd1dbbcf1..682f03b6ea 100644 --- a/src/features/communities/FindCommunity.test.tsx +++ b/src/features/communities/FindCommunity.test.tsx @@ -425,7 +425,9 @@ it.each([ ], [ "a Buzz relay with no community here", - Response.json({ name: "Buzz Relay" }), + new Response("relay: no community is configured for this host", { + status: 404, + }), "No community is set up at this address.", ], ])("%s offers only Cancel", async (_label, answer, message) => { diff --git a/src/features/communities/find-community.test.ts b/src/features/communities/find-community.test.ts index 263a644548..753d975865 100644 --- a/src/features/communities/find-community.test.ts +++ b/src/features/communities/find-community.test.ts @@ -116,8 +116,9 @@ it.each([ [ "a Buzz relay with no community bound to this host", { - "/": Response.json({ name: "Buzz Relay", version: "1.4.2" }), - "/api/join-policy": Response.json({}), + "/": new Response("relay: no community is configured for this host", { + status: 404, + }), }, "no-community", ], @@ -130,6 +131,22 @@ it.each([ }, ); +it("finds a released relay that does not advertise host-bound fields", async () => { + // relay-v0.2.1 (the Helm chart's image) serves NIP-11 without + // read_state_snapshot or buzz_v1, and without icon until one is set. + await expect( + find({ + "/": Response.json({ + name: "Buzz Relay", + software: "https://github.com/block/buzz", + version: "0.2.1", + self, + }), + "/api/join-policy": Response.json({}), + }), + ).resolves.toMatchObject({ host: "relay.example.com:8443", policy: null }); +}); + it.each([ ["404", html(404), null], ["{}", Response.json({}), null], diff --git a/src/features/communities/find-community.ts b/src/features/communities/find-community.ts index f192c67433..16b6e5a0f5 100644 --- a/src/features/communities/find-community.ts +++ b/src/features/communities/find-community.ts @@ -77,8 +77,16 @@ async function policyAnswer(response: Response) { } } +/** buzz-relay's NIP-11 answer for a host it maps to no community. */ +const NO_COMMUNITY = "relay: no community is configured for this host"; + async function relayInfo(response: Response) { if (response.status >= 500) throw new FindCommunityError("unreachable"); + if ( + response.status === 404 && + (await response.text().catch(() => "")) === NO_COMMUNITY + ) + throw new FindCommunityError("no-community"); const body: unknown = response.ok ? await response.json().catch(() => undefined) : undefined; @@ -94,12 +102,5 @@ async function relayInfo(response: Response) { ) ) throw new FindCommunityError("not-buzz"); - // Host-bound fields are present only when the relay maps this host to a community. - if ( - info.read_state_snapshot === undefined && - info.icon === undefined && - info.buzz_v1 === undefined - ) - throw new FindCommunityError("no-community"); return info; }