Skip to content

docs(nostr): note AUTH ordering and Host-based community routing - #6293

Open
CedricConday wants to merge 1 commit into
block:mainfrom
CedricConday:docs/nostr-third-party-client-notes
Open

docs(nostr): note AUTH ordering and Host-based community routing#6293
CedricConday wants to merge 1 commit into
block:mainfrom
CedricConday:docs/nostr-third-party-client-notes

Conversation

@CedricConday

Copy link
Copy Markdown

Summary

Two behaviours of a self-hosted relay that aren't documented, both hit while writing a third-party NIP-29 client against ghcr.io/block/buzz:main (relay 0.2.1, 93114c9). Neither is a relay bug — in both cases the relay is correct and the symptom points somewhere else, which is what cost the time.

1. AUTH ordering. The relay challenges proactively on connect. A client that opens the socket and sends a REQ immediately races that challenge: the relay answers the premature subscription with CLOSED: auth-required while the client is still signing its kind:22242 response. An auth routine reading the same socket consumes that CLOSED, discards it as "not the OK I'm waiting for", and auth then succeeds — but the subscription is gone and no EOSE is coming. It presents as a hang on first query, not an auth error.

Distinct from #4498, which is a cold-start timing race in the desktop client (signer not warm inside the 5s window). This one is ordering rather than timing, and reproduces regardless of how fast the client signs.

2. Host-based community routing. Community resolution keys on the Host header, so an SSH tunnel or a misconfigured reverse proxy points at a host with no community and the WebSocket upgrade 404s. The relay's error body is explicit — relay: no community is configured for this host — but most WebSocket client libraries drop the response body on a failed upgrade and surface a bare 404.

The part that makes this hard to diagnose: /_liveness and NIP-11 both return 200 for that same wrong Host. Monitoring reports the relay healthy while nothing can connect through that path.

$ curl -so /dev/null -w '%{http_code}\n' -H 'Host: 100.117.105.102:3000' \
    -H 'Upgrade: websocket' -H 'Connection: Upgrade' \
    -H "Sec-WebSocket-Key: $K" -H 'Sec-WebSocket-Version: 13' $RELAY/
101

$ curl -s -H 'Host: 127.0.0.1:13000' \
    -H 'Upgrade: websocket' -H 'Connection: Upgrade' \
    -H "Sec-WebSocket-Key: $K" -H 'Sec-WebSocket-Version: 13' $RELAY/
relay: no community is configured for this host          # 404

$ curl -s -H 'Host: 127.0.0.1:13000' $RELAY/_liveness
ok                                                       # 200

The docs now point at the log line that resolves it, since it names the host each community was registered under:

INFO Deployment community ensured  host="100.117.105.102:3000"  community=<uuid>

Docs only — no code changes, no behaviour changes.

Related issue

#4498 — related but distinct (cold-start timing vs. connection ordering); this documents the ordering requirement rather than changing behaviour. No existing issue or PR found for the Host/proxy case. Adjacent docs work: #3551, #3188.

Testing

Both reproductions were run against a single-node deploy/compose stack on relay 0.2.1 (93114c9), with BUZZ_REQUIRE_RELAY_MEMBERSHIP=true and BUZZ_REQUIRE_AUTH_TOKEN=false. The curl sequence above is verbatim output from that host.

The AUTH ordering claim was verified by reordering a client that previously hung on its first REQ: authenticating before subscribing, channel discovery (39000/39002), posting kind:9, reading, NIP-50 search and kind:7 reactions all succeed, with two separate agent identities in one channel.

Per AGENTS.md's Product Contract — this is a docs-only change to the self-hosted third-party-client surface and doesn't contradict VISION_SOVEREIGN.md; if anything the friction it documents is friction against self-hosting. Happy to fold in a "Tested Clients" row, split this into two PRs, or move the content elsewhere if NOSTR.md isn't where you want it.

Two behaviours that cost time when writing a third-party NIP-29 client
against a self-hosted relay, neither currently written down.

AUTH ordering: the relay challenges proactively on connect, so a client
that sends a REQ before completing NIP-42 gets CLOSED: auth-required for
that subscription while it is still signing its kind:22242 response. An
auth routine reading the same socket discards the CLOSED as unmatched,
auth then succeeds, and the subscription is silently gone. It presents as
a hang on first query rather than an auth failure. Relay is per-spec here;
documenting the ordering requirement and the misleading symptom.

Host header: community resolution keys on Host, so any tunnel or proxy
that rewrites it gets a 404 on the WebSocket upgrade. The relay's body
says 'no community is configured for this host', but most WebSocket
clients drop the body on a failed upgrade and surface a bare 404. GET
/_liveness and NIP-11 both return 200 for the same wrong Host, so health
checks stay green while no client can connect. Added the reproduction,
the log line that identifies the registered host, and the proxy config.

Verified against ghcr.io/block/buzz:main, relay 0.2.1 (93114c9).

Signed-off-by: Cedric Conday <cedric@condaydigital.com>
@CedricConday
CedricConday requested a review from a team as a code owner August 19, 2026 07:14
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