From 5fc3284a57c897fa1efc10bf2edd10b67defe28c Mon Sep 17 00:00:00 2001 From: archipelago Date: Fri, 31 Jul 2026 07:04:11 -0400 Subject: [PATCH] feat(pwa): auto-apply service worker updates instead of prompting Alpha-stage, user-approved: a prompt only reaches users who click it, so security fixes sat unapplied in long-lived sessions (installed PWA, kiosk displays). Extends the existing kiosk-only auto-apply to all non-demo clients. Deliberately routed through the existing SKIP_WAITING message rather than build-time skipWaiting/clientsClaim, so both activation guards survive: reloadAfterCinematic() holds the reload until the splash/dashboard cinematic finishes, and the hadController check ignores the first-install claim. A build-time skipWaiting would bypass both. Co-Authored-By: Claude Opus 5 (1M context) --- neode-ui/src/components/PWAUpdatePrompt.vue | 26 ++++++++++++++------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/neode-ui/src/components/PWAUpdatePrompt.vue b/neode-ui/src/components/PWAUpdatePrompt.vue index 91d0c9b9..5c3a9eb9 100644 --- a/neode-ui/src/components/PWAUpdatePrompt.vue +++ b/neode-ui/src/components/PWAUpdatePrompt.vue @@ -109,14 +109,24 @@ onMounted(() => { registration.waiting.postMessage({ type: 'SKIP_WAITING' }) } } - // The kiosk is unattended — nobody can click "Update Now", - // so it kept running stale bundles forever (stale FIPS state, - // old assets). Auto-apply there; prompt everywhere else. - if (localStorage.getItem('kiosk')) { - updateCallback() - } else { - showUpdatePrompt.value = true - } + // Auto-apply everywhere (alpha, user-approved 2026-07-31): + // a prompt only reaches users who happen to click it, so + // security fixes sat unapplied in long-lived sessions (the + // installed PWA and kiosk displays especially). The kiosk + // already auto-applied for exactly that reason; the rest of + // the fleet now does too. + // + // Deliberately still routed through SKIP_WAITING rather than + // build-time `skipWaiting: true`, so activation keeps the two + // guards below: reloadAfterCinematic() holds the reload until + // the splash/dashboard cinematic is over, and the + // hadController check ignores the first-install claim. A + // build-time skipWaiting would bypass both and could reload + // the app out from under someone. + // + // Revisit at beta: restore the prompt (showUpdatePrompt.value + // = true) if users should choose their own update moment. + updateCallback() } }) }