diff options
| author | Adam Soltys <[email protected]> | 2026-08-01 12:45:01 -0700 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2026-08-01 12:45:01 -0700 |
| commit | ef7f5aa8d713bf0e754607f9fcc0527c5a10e737 (patch) | |
| tree | 227d1d63ec6a3ddf70f362ece1db42579556e0d2 | |
| parent | 37989576d91d21b4f727a67470eef9e07e85dd07 (diff) | |
| download | coinosv3-ef7f5aa8d713bf0e754607f9fcc0527c5a10e737.tar.gz coinosv3-ef7f5aa8d713bf0e754607f9fcc0527c5a10e737.zip | |
Surface refused relay publishes; publish NWC info once per session
relay.coinos.io's write policy was rejecting every 23195/13194 hal sent
(kinds were whitelisted to coinos server pubkeys — incompatible with
hal's per-connection service keys) and the rejections temp-banned the
IP. publishOn swallowed all of it, which disguised a relay-side policy
problem as a client bug. The relay policy is fixed separately; here,
refused publishes now log the relay and reason, and the 13194 info
event goes out once per connection per session instead of on every
listen() so replies keep their share of the relay's per-IP rate budget.
Co-Authored-By: Claude Fable 5 <[email protected]>
| -rw-r--r-- | src/features/nwc.js | 15 | ||||
| -rw-r--r-- | src/nostr.js | 13 |
2 files changed, 23 insertions, 5 deletions
diff --git a/src/features/nwc.js b/src/features/nwc.js index 66d348f..f561a8a 100644 --- a/src/features/nwc.js +++ b/src/features/nwc.js @@ -339,6 +339,7 @@ export function nwcFeature(ctx) { // ---- lifecycle -------------------------------------------------------- let unsubs = []; + const infoPublished = new Set(); // connection ids whose 13194 went out this session let listenGen = 0; // TEMP instrumentation: which listen() generation is live function stop() { if (unsubs.length) console.log(`nwc: stop() closing ${unsubs.length} sub(s) from gen ${listenGen}`); @@ -364,10 +365,16 @@ export function nwcFeature(ctx) { }, { onclose: (reasons) => console.log(`nwc: gen ${gen} sub CLOSED for conn=${c.id}:`, JSON.stringify(reasons)) }, )); - // Republish the capability event on every start: a client that can't - // find kind 13194 just spins, and a single publish at creation time can - // easily have been missed by a relay. - publishInfo(c).catch(() => {}); + // Republish the capability event once per session per connection: a + // client that can't find kind 13194 just spins, and a single publish at + // creation time can easily have been missed by a relay. But listen() + // re-runs on every sync snapshot and wake, and relays rate-limit + // accepted events per IP — repeated republishing eats the budget the + // replies need (and once got this IP temp-banned). + if (!infoPublished.has(c.id)) { + infoPublished.add(c.id); + publishInfo(c).catch(() => {}); + } } } diff --git a/src/nostr.js b/src/nostr.js index e1f1c14..006a74c 100644 --- a/src/nostr.js +++ b/src/nostr.js @@ -37,7 +37,18 @@ export function subscribeOn(relays, filter, onEvent, extra = {}) { } catch { return () => {}; } } export async function publishOn(relays, evt) { - try { await Promise.allSettled(pool.publish(relays, evt)); return true; } catch { return false; } + try { + const results = await Promise.allSettled(pool.publish(relays, evt)); + // A rejected publish is a relay refusing the event (policy, rate limit, + // ban). Swallowing that silently once hid a relay-side rejection behind + // what looked like a client bug — always say which relay refused and why. + results.forEach((r, i) => { + if (r.status === 'rejected') { + console.warn(`nostr: publish kind ${evt.kind} refused by ${relays[i]}:`, r.reason?.message || r.reason); + } + }); + return results.some((r) => r.status === 'fulfilled'); + } catch { return false; } } // --- locking a gift to a recipient's Nostr key --------------------------- |
