diff options
| -rw-r--r-- | lib/nwc.ts | 71 |
1 files changed, 18 insertions, 53 deletions
@@ -62,10 +62,6 @@ export default () => { let heartbeatInterval: any; let infoCheckInterval: any; - // Tracks which server pubkeys already have their kind 13194 info event on the - // relay, populated by the existence query in ensureInfo() below. - const infoSeen = new Set<string>(); - // Publish the kind 13194 info event and CONFIRM the relay stored it. The // long-lived NWC socket's send is fire-and-forget (await wait_connected; ws.send) // and was called without await — so a send that rejected (e.g. ws in CLOSING @@ -86,11 +82,10 @@ export default () => { }, hexToBytes(sk), ); + // No success log: this now runs every 2 minutes per pubkey, so a "published" + // line each time is pure noise. Only the failure paths below log. for (let attempt = 1; attempt <= 3; attempt++) { - if (await sendEventConfirmed(info)) { - l("nwc published 13194 info event", pk); - return true; - } + if (await sendEventConfirmed(info)) return true; warn(`nwc 13194 publish not confirmed (attempt ${attempt})`, pk); await sleep(1000); } @@ -129,22 +124,20 @@ export default () => { } // The kind 13194 info event (NWC capability advertisement) is REPLACEABLE, so - // it's meant to be published once and persist on the relay. NWC clients (Alby - // Go, Lightning Piggies, etc.) fetch it before they'll talk to the wallet — - // without it they report "no info event" and show an unknown balance. + // it's meant to persist on the relay. NWC clients (Alby Go, Lightning Piggies, + // etc.) fetch it before they'll talk to the wallet — without it they report + // "no info event" and show an unknown balance. // - // Rather than blindly republishing, query the relay on connect and publish - // only the pubkeys whose info event is missing. This is normally a no-op, but - // self-heals the case where the event went missing (e.g. the strfry restart on - // 2026-05-29 that left clients broken for days). The "infocheck" subscription - // is handled by the event/eose branches below. + // We used to query the relay and publish only the missing pubkeys, but the + // stored event kept vanishing between checks (cause never pinned down), which + // left clients broken until the next hourly check. Republishing both server + // info events unconditionally on a short interval is cheap (a replaceable + // event, so it just overwrites in place) and self-heals immediately. + // publishInfo() uses its own short-lived socket, so this works regardless of + // the long-lived nwc socket's state. function ensureInfo() { - if (!r?.ws || r.ws.readyState !== 1) return; - infoSeen.clear(); - r.subscribe("infocheck", { - kinds: [13194], - authors: [serverPubkey, serverPubkey2], - }); + publishInfo(serverPubkey, serverSecret); + publishInfo(serverPubkey2, serverSecret2); } function connect() { @@ -172,25 +165,12 @@ export default () => { }, 30000); r.subscribe("nwc", { kinds: [23194], "#p": [serverPubkey, serverPubkey2], since: Math.floor(Date.now() / 1000) - nwcEventMaxAgeSeconds }); - // Verify the 13194 info events exist on the relay, publish any missing. + // Publish the 13194 info events immediately, then keep republishing every + // 2 minutes so a vanished event is never missing for long. ensureInfo(); - - // Re-check hourly so a relay restart that leaves our socket intact (or any - // other cause of the info event going missing) self-heals without waiting - // for the next app reconnect. - infoCheckInterval = setInterval(ensureInfo, 60 * 60 * 1000); + infoCheckInterval = setInterval(ensureInfo, 2 * 60 * 1000); }); - r.on("eose", (sub) => { - if (sub !== "infocheck") return; - for (const [pk, sk] of [ - [serverPubkey, serverSecret], - [serverPubkey2, serverSecret2], - ]) { - if (!infoSeen.has(pk)) publishInfo(pk, sk); - } - try { r.unsubscribe("infocheck"); } catch (_) {} - }); r.on("close", () => { warn("nwc strfry connection lost, reconnecting in 5s"); @@ -203,21 +183,6 @@ export default () => { r.on("event", async (sub, ev) => { try { - if (sub === "infocheck") { - // Only count the stored info event as present if it's CURRENT — - // otherwise a stale replaceable event (old method list, missing - // encryption tag) would never get refreshed, since we only publish - // the ones not "seen". - if (ev.kind === 13194) { - const current = - ev.content === methods.join(" ") && - ev.tags?.some( - (t) => t[0] === "encryption" && t[1] === encryptionSchemes, - ); - if (current) infoSeen.add(ev.pubkey); - } - return; - } if (sub !== "nwc") return; const now = Math.floor(Date.now() / 1000); if (ev.created_at && now - ev.created_at > nwcEventMaxAgeSeconds) return; |
