diff options
| author | Adam Soltys <[email protected]> | 2026-08-04 09:33:27 -0700 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2026-08-04 09:33:27 -0700 |
| commit | 103001c49da84f9cd6b6d8c6eb682550442dc8e5 (patch) | |
| tree | 0a998b0c79cff64bb45f1aad800a38cbdc961779 /lib | |
| parent | fb63b96ec90e32bc8adc5b77b2735097dde16bbd (diff) | |
| download | coinos-server-103001c49da84f9cd6b6d8c6eb682550442dc8e5.tar.gz coinos-server-103001c49da84f9cd6b6d8c6eb682550442dc8e5.zip | |
nwc: republish 13194 info events unconditionally every 2 minutes
The stored replaceable 13194 info event was intermittently disappearing
from the relay, leaving NWC clients (Alby etc.) with "no info event"
until the next hourly existence-check happened to republish it. Drop the
check-then-publish dance and just republish both server info events every
2 minutes — a replaceable event overwrites in place, so it's cheap and
self-heals immediately. Silence the per-publish success log since it now
fires on every interval.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Dpqu6wizYe1c1g4k5Gd8JK
Diffstat (limited to 'lib')
| -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; |
