diff options
| author | Adam Soltys <[email protected]> | 2026-09-05 21:47:30 -0700 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2026-09-05 21:47:30 -0700 |
| commit | 7e96dc4d4292b29156ec6b9b9c7009f8c60106f1 (patch) | |
| tree | 5705b219a401bd419f523078d1c937ff0134425b /src/features | |
| parent | ec1d6b8c6da0f9e4e169f4e8f95b88133207b741 (diff) | |
| download | coinosv3-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/features')
| -rw-r--r-- | src/features/messages.js | 17 |
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' }, |
