Add PWA shell and a client-side biometric gate on secret reveal
PWA: hand-rolled service worker (public/sw.js, no build plugin) with a
precached shell, network-first navigations with a 4s timeout and an
inline offline page, and stale-while-revalidate for /assets and public
files that byte-compares in the background and replaces an entry only
when the server copy differs. API/auth/MCP/webhook traffic is never
intercepted or cached — cookie and secret responses stay out of cache
storage. manifest.webmanifest + theme-color + apple-touch-icon; icons
rasterized from the shield logo by tools/rasterize_icons.py (192/512,
any + maskable). SW registers in main.js under the same
secure-context condition the gate uses, with silent background update
ticks on visibility/online/15min.

Biometric gate (frontend/src/biometric.js): a platform-authenticator
passkey (Android fingerprints through BiometricPrompt, Touch ID,
Windows Hello) is enrolled opt-in from Settings; every reveal and
version reveal runs a fresh WebAuthn assertion with
userVerification:"required", whose clientData/authenticatorData/
signature are verified offline in the same browser against the stored
public key. The server never learns about biometrics — this is an
interface lock, not encryption. Disable requires the same proof, and
the toggle disables itself with an explanatory note on devices
without a platform authenticator.

Also fixes a pre-existing bug the verification run caught: the detail
panel's Hide button wrote `revealed.value = null` in the template,
where refs auto-unwrap, so the panel never returned to masked state.

Verified end to end with a CDP virtual authenticator: 16 checks cover
manifest/SW/offline shell/freshness/no-API-cache, enrollment, reveal
through the gate (HTTP 200), reveal blocked with no sensor, disable
refused without the sensor and allowed with it, and the disabled
toggle + note on a device without biometrics.

Co-Authored-By: Claude Code <noreply@anthropic.com>
1 parent 4b7e292 commit 4c6a4f7cad676cf8254d6785aa0e403172c7d380
@Eugene Sukhodolskiy Eugene Sukhodolskiy authored 3 hours ago
Showing 11 changed files
View
frontend/index.html
View
frontend/public/icons/icon-192-maskable.png 0 → 100644
View
frontend/public/icons/icon-192.png 0 → 100644
View
frontend/public/icons/icon-512-maskable.png 0 → 100644
View
frontend/public/icons/icon-512.png 0 → 100644
View
frontend/public/manifest.webmanifest 0 → 100644
View
frontend/public/sw.js 0 → 100644
View
frontend/src/App.vue
View
frontend/src/biometric.js 0 → 100644
View
frontend/src/main.js
View
tools/rasterize_icons.py 0 → 100644