Context
The MV3 service worker sleeps after ~30s idle. The master is held in chrome.storage.session, which survives the worker nap. An alarm keyfount:auto-lock is supposed to wipe the master after autoLockMinutes.
Problem / Observation
- extension/src/background/session.ts:39-47 creates the alarm on
unlock(). The alarm itself survives SW sleep — chrome.alarms is OS-managed.
- BUT: if the worker is killed and respawned BEFORE
autoLockMinutes elapses, registerAutoLockHandler is called fresh and re-attaches onAlarm listeners (session.ts:84-90). This is fine for the future alarm fire — but there is no replay of the listener for an alarm that ALREADY fired while the SW was asleep.
- Concretely: SW sleeps at t=0, alarm fires at t=2min (autoLockMinutes=2), SW wakes at t=10min for some other reason →
onAlarm listener wasn't there at t=2min. The session is NOT wiped. The user thinks they were auto-locked but readMaster() still returns the value.
- Compounding:
chrome.alarms.create with when past or delayInMinutes very small can be coalesced. With autoLockMinutes < 1 (we technically allow 0-1440), the alarm may never fire.
Proposed approach
- On SW wake (
defineBackground init in entrypoints/background.ts), check the session payload's unlockedAt + autoLockMinutes*60*1000; if it's in the past, call lock() immediately. The check belongs in a void enforceAutoLockOnWake() next to void hardenSessionStorage().
- Document the floor: reject
setAutoLockMinutes with minutes > 0 && minutes < 1 (currently the router accepts 0..1440 integer, OK there).
- Add a vitest: simulate SW restart by clearing
chrome.alarms, set unlockedAt = Date.now() - 60_000 and autoLockMinutes = 0.5, run wake hook, assert readMaster() === null.
Acceptance criteria
Context
The MV3 service worker sleeps after ~30s idle. The master is held in
chrome.storage.session, which survives the worker nap. An alarmkeyfount:auto-lockis supposed to wipe the master afterautoLockMinutes.Problem / Observation
unlock(). The alarm itself survives SW sleep — chrome.alarms is OS-managed.autoLockMinuteselapses,registerAutoLockHandleris called fresh and re-attachesonAlarmlisteners (session.ts:84-90). This is fine for the future alarm fire — but there is no replay of the listener for an alarm that ALREADY fired while the SW was asleep.onAlarmlistener wasn't there at t=2min. The session is NOT wiped. The user thinks they were auto-locked butreadMaster()still returns the value.chrome.alarms.createwithwhenpast ordelayInMinutesvery small can be coalesced. WithautoLockMinutes < 1(we technically allow 0-1440), the alarm may never fire.Proposed approach
defineBackgroundinit inentrypoints/background.ts), check the session payload'sunlockedAt + autoLockMinutes*60*1000; if it's in the past, calllock()immediately. The check belongs in avoid enforceAutoLockOnWake()next tovoid hardenSessionStorage().setAutoLockMinuteswithminutes > 0 && minutes < 1(currently the router accepts 0..1440 integer, OK there).chrome.alarms, setunlockedAt = Date.now() - 60_000andautoLockMinutes = 0.5, run wake hook, assertreadMaster() === null.Acceptance criteria
enforceAutoLockOnWakeexists and is called once at SW init.tests/session.test.tscovers the missed-alarm case.unlock/locklifecycle.