fix(pairing): stop routing mobile pairing to a non-existent /pair sidecar - #5134
Draft
mfethe1 wants to merge 1 commit into
Draft
fix(pairing): stop routing mobile pairing to a non-existent /pair sidecar#5134mfethe1 wants to merge 1 commit into
mfethe1 wants to merge 1 commit into
Conversation
…ecar
Pairing from a membership-enforcing relay failed with
WebSocket connection failed: HTTP error: 404 Not Found
`probe_pairing_relay` treated a NIP-11 `supported_nips: [43]` advertisement
as "this relay has a /pair sidecar" and synthesised `wss://host/…/pair`.
No relay serves that path — `buzz-relay` exposes its WebSocket only at `/`
(crates/buzz-relay/src/router.rs), and the real dedicated pairing relay
(`buzz-pair-relay`) is a separate service advertised via NIP-11
`pairing_relay_url`. The `/pair` convention is dead infrastructure, and
NIP-43 was the wrong signal for it: NIP-43 means membership enforcement,
not the presence of a pairing sidecar.
Worse, the fallback could never have worked. On a membership-enforcing
relay an unpaired peer is rejected during NIP-42 AUTH
(crates/buzz-relay/src/handlers/auth.rs, via `enforce_relay_membership` in
crates/buzz-relay/src/api/mod.rs) and there is no exemption for pairing
kind:24134 or for ephemeral events. Such a relay genuinely requires a
separate open pairing relay; it cannot pair over its own root socket.
Replace `LegacyPath` with `MembershipWithoutPairingRelay`, which surfaces
an actionable error naming the operator fix instead of fabricating a URL
that 404s. The two paths that actually work are unchanged: an advertised
`pairing_relay_url` is used as-is, and an open relay pairs over its root
socket.
This is a client-side correctness fix. Operators of membership-enforcing
relays must still deploy a pairing relay (BUZZ_PAIRING_RELAY_URL, or Helm
`pairingRelay.enabled` + `pairingRelay.url`) for pairing to succeed — the
error now says so instead of reporting a bare 404.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TqBMPniDMJfXpwi4XicdMg
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The bug
Pairing a mobile device against a membership-enforcing relay fails with:
Root cause
probe_pairing_relaytreated a NIP-11supported_nips: [43]advertisement as "this relay has a/pairsidecar" and synthesisedwss://host/…/pair.Nothing serves that path.
buzz-relayexposes its WebSocket only at/(crates/buzz-relay/src/router.rs), and the real dedicated pairing relay (buzz-pair-relay) is a separate service, advertised via NIP-11pairing_relay_url. The/pairconvention is dead infrastructure.NIP-43 was also the wrong signal: it means membership enforcement, not "a pairing sidecar exists".
crates/buzz-relay/src/nip11.rsalready carries a regression test warning about exactly this ("advertising it on open relays misroutes pairing peers to a non-existent /pair sidecar").Worse, the fallback could never have worked. On a membership-enforcing relay an unpaired peer is rejected during NIP-42 AUTH (
crates/buzz-relay/src/handlers/auth.rs→enforce_relay_membershipincrates/buzz-relay/src/api/mod.rs), with no exemption for pairingkind:24134or for ephemeral events. Such a relay genuinely requires a separate open pairing relay — it cannot pair over its own root socket at all.The change
Replace
PairingRelay::LegacyPathwithPairingRelay::MembershipWithoutPairingRelay, which returns an actionable error naming the operator fix instead of fabricating a URL that 404s.The two paths that actually work are unchanged:
pairing_relay_urlpairing_relay_url/pair→ 404)Scope / operator note
This is a client-side correctness fix. It converts a mystifying 404 into a message that names the cause. Operators of membership-enforcing relays must still deploy a pairing relay for pairing to succeed:
(or
BUZZ_PAIRING_RELAY_URLdirectly — seedeploy/charts/buzz/README.md).Testing
cargo test pairing_relay— 7 passed, 0 failed.Updated the two tests that asserted the
/pairbehaviour; the replacement asserts the error never contains/pairand does name the missing pairing relay, so the fabrication can't regress.Open question for maintainers
Should the relay instead exempt pairing
kind:24134from the membership gate, so one relay can serve pairing without a sidecar? That would remove the need for a separate deployment entirely, but it's a change to the auth gate and wants a security review — deliberately not attempted here.🤖 Generated with Claude Code
https://claude.ai/code/session_01TqBMPniDMJfXpwi4XicdMg