aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-08-09 13:46:48 -0700
committerAdam Soltys <[email protected]>2026-08-09 13:46:48 -0700
commit75270f0329c95422d28d7c15d2b532673eb8343d (patch)
tree9d1a2eb71bdfe15ffc42f271cb0db364a8cdb4a7
parent8b2c709ca1891668a2bbbf373f388fa6271a37a7 (diff)
downloadcoinosv3-75270f0329c95422d28d7c15d2b532673eb8343d.tar.gz
coinosv3-75270f0329c95422d28d7c15d2b532673eb8343d.zip
A stuck forward now holds its tongue: retries back off to an hour, and an offer knock reads as money arriving, not leaving
Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01DbA53PJA8fgSvMcEzfuw22
-rw-r--r--names/server.js13
-rw-r--r--src/sw-nwc.js9
2 files changed, 18 insertions, 4 deletions
diff --git a/names/server.js b/names/server.js
index 3ebfa48..9931cae 100644
--- a/names/server.js
+++ b/names/server.js
@@ -325,13 +325,22 @@ async function settleLoop() {
}
}
}
-// retry queued forwards (e.g. after the float is topped up)
+// retry queued forwards (e.g. after the float is topped up). Each failure
+// backs off exponentially (1m → 1h cap): a stuck forward must not fire an
+// offer request — and push-wake the recipient's devices — every minute
+// forever.
setInterval(async () => {
const q = state.pending || [];
if (!q.length || !fwd) return;
const still = [];
for (const p of q) {
- try { await forward(p.name, p.sat); } catch { still.push(p); }
+ if (p.next && Date.now() < p.next) { still.push(p); continue; }
+ try { await forward(p.name, p.sat); }
+ catch {
+ p.tries = (p.tries || 0) + 1;
+ p.next = Date.now() + Math.min(3_600_000, 60_000 * 2 ** Math.min(p.tries - 1, 6));
+ still.push(p);
+ }
}
state.pending = still;
persist();
diff --git a/src/sw-nwc.js b/src/sw-nwc.js
index edf1f5e..a73a611 100644
--- a/src/sw-nwc.js
+++ b/src/sw-nwc.js
@@ -86,8 +86,13 @@ self.addEventListener('push', (e) => {
return;
}
if (handled) return;
- await self.registration.showNotification('Payment request', {
- body: 'An app is asking your wallet to pay. Open Coinos to approve.',
+ // A kind-21001 offer request is money coming IN (someone asking us to
+ // mint an invoice) — "an app wants to pay" would read as the opposite.
+ const incoming = data.event && data.event.kind === 21001;
+ await self.registration.showNotification(incoming ? 'Incoming payment' : 'Payment request', {
+ body: incoming
+ ? 'Someone is trying to send you money. Open coinos so your wallet can receive it.'
+ : 'An app is asking your wallet to pay. Open Coinos to approve.',
icon: 'icon-192.png', badge: 'icon-192.png',
tag: 'nwc-' + (data.servicePubkey || 'req'), renotify: false,
data: { url: './' },