Skip to content

fix(nip46): always include Amber-friendly fallback relays in the pairing set - #74

Merged
oth-body merged 1 commit into
masterfrom
fix/nip46-pairing-relay-coverage
Sep 15, 2026
Merged

oth-body merged 1 commit into
masterfrom
fix/nip46-pairing-relay-coverage

Conversation

@oth-body

Copy link
Copy Markdown
Owner

Summary

After PR #73 (refuse to dial through inherited *_PROXY env vars), the user reported a second failure mode:

after approval in amber signer there is no response from hoot

The proxy fix correctly unblocks the initial sign-in dial. The new failure is in the post-pairing subscribe/recv path: Amber approves the QR, publishes the connect ack to one of the relays in the URI, but hoot never sees the event.

Root cause

The user's relays.txt contains:

wss://purplerelay.com/
wss://nos.lol/
wss://relay.damus.io
wss://relay.nostr.band

Neither purplerelay.com nor nos.lol are relays Amber is willing to publish to, and the user's list omits wss://nostr.wine (Amber's documented default bunker relay). Amber's published fallback chain is wss://relay.damus.io → wss://nostr.band → wss://nostr.wine. If hoot's subscription is on none of those (because the user's relays.txt made the dial fail or be skipped), the connect ack arrives at a relay hoot isn't listening on.

Fix

nip46.PairingRelays(userRelays) always merges a hard-coded fallback set (wss://relay.damus.io, wss://nostr.wine) into whatever the user configured, dedup'd, preserving user order. Both the nostrconnect:// URI and hoot's Session.RelayURLs use this combined list, so:

  • Amber always sees a relay it trusts to publish to
  • hoot always subscribes to a relay Amber is willing to use

Also:

  • ConnectRelays now skips relays whose connection died between dial and subscribe (IsConnected() == false). Previously we'd subscribe against a closed relay, producing a never-firing event channel that fooled CheckConnection into "no events yet" forever.
  • CheckConnection now distinguishes "all subscriptions closed" from "no events yet" — the former returns an actionable error pointing at the network so users don't re-scan fruitlessly.

Tests

TestPairingRelaysIncludesFallbacks / PreservesUserOrder / Dedups / EmptyUser pin the new helper.

TestConnectRelaysSkipsDisconnectedRelays exercises the defensive skip path with an unresolvable host.

All existing tests still pass (4 master + 11 PR#73 + 5 new = 20 nip46 tests).

CI: 7/7 (test + 6 cross-platform builds).

…ing set

Hoot subscribed to kind:24133 events only on relays from the
user's relays.txt. If that list was sparse (the user's
relays.txt has only purplerelay.com, nos.lol, damus.io,
relay.nostr.band — no nostr.wine) or contained relays Amber
didn't recognise, Amber published the connect ack to its own
preferred relay (wss://relay.damus.io / wss://nostr.wine) and
hoot never saw it. Symptom: scan approved in Amber, no
response in hoot — the TUI spins forever on the QR screen.

Fix: nip46.PairingRelays() always appendes a hard-coded set of
fallback relays (wss://relay.damus.io, wss://nostr.wine) to
whatever the user configured, dedup'd. Both the nostrconnect://
URI and hoot's subscription set use this combined list, so:

  - Amber always sees a relay it trusts in the URI
    (so it picks one to publish to), AND
  - hoot always subscribes to a relay Amber is willing to use
    (so the connect ack actually arrives)

Also: ConnectRelays now skips relays whose connection died
between dial and subscribe (IsConnected() == false). Previously
we'd subscribe against a closed relay, producing a never-firing
event channel that fooled CheckConnection into 'no events yet'
forever.

CheckConnection now distinguishes 'all subscriptions closed'
from 'no events yet' — the former returns an actionable error
pointing at the network so users don't re-scan fruitlessly.

Tests:
  - TestPairingRelaysIncludesFallbacks / PreservesUserOrder /
    Dedups / EmptyUser pin the new helper's behaviour.
  - TestConnectRelaysSkipsDisconnectedRelays exercises the
    defensive skip path with an unresolvable host.

hoot.go's OnInitQR now calls nip46.PairingRelays(getRelayList())
instead of getRelayList() directly.
@oth-body
oth-body merged commit dede58b into master Sep 15, 2026
7 checks passed
@oth-body
oth-body deleted the fix/nip46-pairing-relay-coverage branch September 15, 2026 17:09
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