aboutsummaryrefslogtreecommitdiffstats
path: root/nwcpush/server.js
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-09-05 21:38:09 -0700
committerAdam Soltys <[email protected]>2026-09-05 21:38:09 -0700
commitec1d6b8c6da0f9e4e169f4e8f95b88133207b741 (patch)
tree9e733166d98b7f1d64401e03fc4130629f9027d7 /nwcpush/server.js
parent9ee75115c3cabee661866a8ade836cc6d9e519ac (diff)
downloadcoinosv3-ec1d6b8c6da0f9e4e169f4e8f95b88133207b741.tar.gz
coinosv3-ec1d6b8c6da0f9e4e169f4e8f95b88133207b741.zip
Payment push notifications are scoped to their network, so a mainnet payment stops opening the staging PWA. The same seed derives the same nostr pubkey on mainnet and staging, so both PWAs registered push under one pubkey and the notifier — matching only by pubkey — fanned every mainnet payment out to the staging subscription too; tapping it opened staging.coinos.io. Now the client stamps its network on the push registration (pushWatch net: getNetwork()), the registrar declares its own network on the /notify call (CFG.forwarder.network), and nwcpush delivers a payment only to registrations whose net matches — with a rollout-safe fallback: a device that hasn't re-registered yet (net=null) still gets the push, and a caller that omits net still reaches everyone, so nobody goes dark mid-deploy. Deployed names + nwcpush on cs
Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MGzU12QrAA4G6BYGzGVdXz
Diffstat (limited to 'nwcpush/server.js')
-rw-r--r--nwcpush/server.js15
1 files changed, 14 insertions, 1 deletions
diff --git a/nwcpush/server.js b/nwcpush/server.js
index 7efebb1..61cab60 100644
--- a/nwcpush/server.js
+++ b/nwcpush/server.js
@@ -331,9 +331,18 @@ const server = Bun.serve({
if (!CFG.notifyToken || body?.token !== CFG.notifyToken) return json({ error: 'forbidden' }, 403);
const pk = body?.pubkey;
if (!/^[0-9a-f]{64}$/.test(pk || '')) return json({ error: 'pubkey required' }, 400);
+ // Network scoping: the same pubkey exists on mainnet and staging, so a
+ // payment goes only to devices on its own network. A device that hasn't
+ // re-registered since this shipped has net=null and still gets it (so
+ // no one goes dark mid-rollout); once both sides send a net, mismatches
+ // are filtered. A caller that omits net pushes to everyone (legacy).
+ const wantNet = typeof body.net === 'string' ? body.net : null;
let n = 0;
for (const [id, r] of Object.entries(notifyRegs)) {
- if ((r.ptags || []).includes(pk)) { pushNotify(id, r, 'payment', { amountSat: Number(body.amountSat) || undefined }).catch(() => {}); n++; }
+ if (!(r.ptags || []).includes(pk)) continue;
+ if (wantNet && r.net && r.net !== wantNet) continue;
+ pushNotify(id, r, 'payment', { amountSat: Number(body.amountSat) || undefined }).catch(() => {});
+ n++;
}
return json({ ok: true, devices: n });
}
@@ -367,6 +376,10 @@ const server = Bun.serve({
ptags: hex(body.notify.ptags, 8),
authors: hex(body.notify.authors, 64),
reasons,
+ // the network this device is on: the same seed yields the same
+ // pubkey on mainnet and staging, so a payment push is delivered
+ // only to devices on the payment's network (see /notify)
+ net: typeof body.notify.net === 'string' ? body.notify.net.slice(0, 16) : null,
};
persist();
resubscribeNotify();