aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-09-05 21:47:30 -0700
committerAdam Soltys <[email protected]>2026-09-05 21:47:30 -0700
commit7e96dc4d4292b29156ec6b9b9c7009f8c60106f1 (patch)
tree5705b219a401bd419f523078d1c937ff0134425b /src
parentec1d6b8c6da0f9e4e169f4e8f95b88133207b741 (diff)
downloadcoinosv3-7e96dc4d4292b29156ec6b9b9c7009f8c60106f1.tar.gz
coinosv3-7e96dc4d4292b29156ec6b9b9c7009f8c60106f1.zip
Heal dead push subscriptions from a VAPID key rotation — the reason notifications went silent. registerPush reused whatever getSubscription() returned and only ever subscribed fresh when there was none; a subscription made against an OLD VAPID key can never receive a push (the notifier's send fails with a permanent 403, which was never pruned, and the client kept re-registering the same dead endpoint). Now the client fetches the current VAPID key first and, when an existing subscription's applicationServerKey doesn't match, unsubscribes and re-subscribes against the current key — so a device self-heals on next open. And the notifier prunes 403 endpoints alongside 404/410. Deployed nwcpush on cs
Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MGzU12QrAA4G6BYGzGVdXz
Diffstat (limited to 'src')
-rw-r--r--src/features/messages.js17
1 files changed, 15 insertions, 2 deletions
diff --git a/src/features/messages.js b/src/features/messages.js
index 582bf3b..66a1270 100644
--- a/src/features/messages.js
+++ b/src/features/messages.js
@@ -1101,10 +1101,23 @@ export function messagesFeature(ctx) {
if ((await Notification.requestPermission()) !== 'granted') return false;
}
const reg = await navigator.serviceWorker.ready;
+ const { publicKey } = await (await fetch(`${NOTIFIER}/vapid`)).json();
+ const wantKey = b64ToBytes(publicKey);
let sub = await reg.pushManager.getSubscription();
+ // A subscription made against a DIFFERENT VAPID key can never receive a
+ // push — the notifier's sends fail with a permanent 403 and it keeps
+ // reusing the dead sub, so the device goes silent forever (this is what
+ // stranded notifications after the key last rotated). Re-subscribe when
+ // the existing sub's server key doesn't match the current one.
+ const keyMatches = (s) => {
+ try {
+ const cur = new Uint8Array(s.options.applicationServerKey || []);
+ return cur.length === wantKey.length && cur.every((b, i) => b === wantKey[i]);
+ } catch { return false; }
+ };
+ if (sub && !keyMatches(sub)) { try { await sub.unsubscribe(); } catch {} sub = null; }
if (!sub) {
- const { publicKey } = await (await fetch(`${NOTIFIER}/vapid`)).json();
- sub = await reg.pushManager.subscribe({ userVisibleOnly: true, applicationServerKey: b64ToBytes(publicKey) });
+ sub = await reg.pushManager.subscribe({ userVisibleOnly: true, applicationServerKey: wantKey });
}
const r = await fetch(`${NOTIFIER}/register`, {
method: 'POST', headers: { 'content-type': 'application/json' },