From 0519a79ac05ec983ae7ad8d4292f5cfa9fa321a9 Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Sat, 1 Aug 2026 11:02:37 -0700 Subject: NWC: own the relay set instead of borrowing the sync feature's MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The connection URI advertised up to four relays (sync relays + profile relays) while the subscription only ever listened on the sync relay, so a client could publish its request somewhere hal wasn't and spin forever waiting for a reply. Amethyst did exactly that. NWC now has its own relay list used for all three things that must agree: the URI, the subscription, and the replies. It no longer depends on device sync being enabled, which would otherwise have made the whole wallet service a silent no-op. Also republish the kind 13194 capability event on every start — a client that can't find it just spins, and a single publish at creation time is easy for a relay to miss. The relay transport is now injectable so the protocol can be exercised without a live relay (tools/nwc-test.js). Co-Authored-By: Claude Fable 5 --- src/nostr.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'src/nostr.js') diff --git a/src/nostr.js b/src/nostr.js index 652c579..1a86499 100644 --- a/src/nostr.js +++ b/src/nostr.js @@ -23,6 +23,20 @@ import { base64urlnopad } from '@scure/base'; // CLOSE-after-EOSE handshake) cleanly, so we never send on a half-closed socket. const pool = new SimplePool(); +// Subscribe / publish on an explicit relay set, independent of the sync +// feature's configuration. NWC needs this: the relays it advertises in a +// connection URI must be exactly the ones it listens on, or a client's +// request lands somewhere the wallet isn't and it waits forever. +export function subscribeOn(relays, filter, onEvent) { + try { + const sub = pool.subscribeMany(relays, [filter], { onevent: onEvent }); + return () => { try { sub.close(); } catch {} }; + } catch { return () => {}; } +} +export async function publishOn(relays, evt) { + try { await Promise.allSettled(pool.publish(relays, evt)); return true; } catch { return false; } +} + // --- locking a gift to a recipient's Nostr key --------------------------- // An npub or 64-hex nostr pubkey → hex, or null if it isn't one. // NIP-47 wallet services sign with their own per-connection key and must -- cgit v1.2.3