Legacy /pair pairing fallback cannot pass NIP-42 — desktop signs the URL with path, relay expects bare origin
Nobody has claimed this yet.
Assessment
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Newbie friendliness
- 78/100
- Issue type
- Bug
- Clarity
- Clearly specified
- Activity status
- Quiet
- Tech stack
- rust
- Domain
- authentication, desktop
Research direction
Start in desktop/src-tauri/src/commands/pairing.rs at resolve_pairing_relay_url and handle_nip42_auth, then compare the expected value in crates/buzz-relay/src/api/bridge.rs and normalization in crates/buzz-auth/src/nip42.rs. Reproduce legacy /pair pairing against relay-v0.2.0 and verify that NIP-42 authentication succeeds without the relay URL mismatch.
Written by the indexing model from the issue text.
Description
Summary
On relays whose NIP-11 lacks pairing_relay_url (which includes both tagged relay releases, relay-v0.1.1 and relay-v0.2.0), Buzz Desktop's device pairing falls back to the legacy convention wss://<relay-host>/pair — and then cannot authenticate, because the desktop signs its NIP-42 AUTH event with that URL including the path, while the relay expects the bare origin. The mismatch is unconditional, so legacy-path pairing fails for every operator on the tagged releases.
The two sides of the contract
Desktop (desktop/src-tauri/src/commands/pairing.rs): when NIP-11 has no pairing_relay_url but advertises NIP-43, resolve_pairing_relay_url builds <main relay>/pair (PairingRelay::LegacyPath), and handle_nip42_auth signs the AUTH event with that full URL:
let relay_url_parsed = nostr::RelayUrl::parse(relay_url)...; // wss://<host>/pair
... s.sign_event(nostr::EventBuilder::auth(challenge, relay_url_parsed))
Relay (crates/buzz-relay/src/api/bridge.rs, nip42_expected_relay_url): the expected value is derived from the tenant host, explicitly pathless — the comment states the contract the desktop is violating:
/// Path is empty — clients put the bare WS origin (`ws://host[:port]`) in the
/// `relay` tag, matching how `EventBuilder::auth` accepts a [`nostr::RelayUrl`].
format!("{scheme}://{}", tenant.host())
normalize_relay_url in crates/buzz-auth/src/nip42.rs preserves paths (it only trims a trailing slash), so wss://<host>/pair vs wss://<host> never match. Since every WS connection must complete NIP-42 within the fixed 5s window, the connection is then closed.
Observed behaviour
Desktop v0.5.4 against relay-v0.2.0, with an open pairing relay served behind a path-stripping proxy at <relay-host>/pair (so the transport itself works — the WS upgrade succeeds and the QR renders briefly):
Relay log, every attempt:
WARN NIP-42 auth failed error="relay url mismatch"
WARN NIP-42 auth timeout — closing connection timeout_secs=5
INFO WebSocket connection closed
Desktop surfaces this as relay closed waiting for EOSE a few seconds after the QR appears.
Suggested fix (either side resolves it)
- Desktop: sign the bare origin of the pairing URL in the AUTH event — strip the path from
relay_urlbeforeEventBuilder::authinhandle_nip42_auth. One line, and it matches the relay's documented expectation. - Or relay: make
nip42_expected_relay_url(or the comparison) tolerant of the client's connect path.
Operator workaround meanwhile
Run a relay recent enough to support BUZZ_PAIRING_RELAY_URL, and advertise the open pairing relay at a dedicated hostname (path /). The desktop then takes the PairingRelay::Configured route, dials a pathless URL, signs the bare origin, and auth succeeds. Verified working end-to-end; only the legacy path convention is broken.
Happy to provide fuller logs or test against a patched build.
- Dominant language
- Rust
- Stars
- 33.7k
- Forks
- 4.4k
- Avg merge
- 1d 21h
- Merged PRs (30d)
- 239
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from block/buzz
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 86/100
-
workflow_sink's mention parser never masks code regions — @name inside a code span wakes the agent Open
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
-
Difficulty 2/5 Half a day Newbie friendliness 88/100
-
Difficulty 1/5 Under an hour Newbie friendliness 92/100
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
Eynzof/Hermes-CN-Desktop#610 ·
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
gitbutlerapp/gitbutler#15998 · 1 comment ·
-
bug triage:deciding
Difficulty 1/5 Under an hour Newbie friendliness 88/100
open-telemetry/otel-arrow#4132 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 84/100