Ask whose computer this is, and believe the answer #123

Closed
opened 2026-08-28 21:15:30 +00:00 by jcoffey-dev · 0 comments
Owner

Sign-out never cleared local storage. It stopped push, flushed settings and removed the subscription — that last one reasoning explicitly that a browser left holding someone's mail becomes somebody else's next — and then left the settings cache and <accountId>:recent on disk. That list is other people's email addresses, and nothing ever removed it.

Changes

1. Sign-out clears local data, unconditionally. Lending a laptop is the same exposure as a public machine, only quieter. Clear-by-default with a short keep-list — lastUser (only a trusted device writes it), the trust flag, and the random push device id — so a key added later is forgotten by default rather than by nobody having thought about it. Also runs on a 401.

2. The checkbox asks a question people can answer. "Keep me signed in on this device" defaulted to true, which assumed the answer most costly to get wrong: someone on a library machine got a 30-day cookie unless they noticed a ticked box.

before after
question "Keep me signed in on this device" "This is my own device"
default ticked unticked
hint none says what each answer does

3. Untrusted means untrusted: session cookie only, nothing written locally, no push subscription, and a 5-minute idle sign-out. Reads are gated as well as writes — a machine trusted once still has residue — and an untrusted sign-in purges it outright.

On the idle timer instead of a reminder

A "remember to sign out" prompt can't work: custom beforeunload text was removed from browsers years ago, and no event fires at all for walking away from a signed-in screen, which is the case that matters. A timer needs nobody's cooperation.

Note on remember

The wire field keeps its name. It is persisted inside SESSION_FILE, so renaming it would invalidate every session file on upgrade for a change of vocabulary. Client-side the concept is isDeviceTrusted(), derived from the session so a reload reaches the same answer without storing it — which an untrusted device could not do anyway.

Verification

Tests: 359 web (+8 new covering both sides of the gate and the timer) and 80 server, typecheck and build clean.

Driven in a real browser against the mock, because a privacy control that only passes unit tests isn't verified:

path result
untrusted sign-in, full session, folder expanded localStorage empty throughout
trusted sign-in writes settings, a1:recent, lastUser as before
sign-out from trusted recent and settings gone, lastUser kept
untrusted sign-in afterwards clears even lastUser

🤖 Generated with Claude Code

https://claude.ai/code/session_011pJ3S59iaT91dqW8rV3xFa

Merged 2026-08-28 as coffey-labs/ihasmail@ddd1bbf9b3

Rebuilt from: git history, session transcript.

Sign-out never cleared local storage. It stopped push, flushed settings and removed the subscription — that last one reasoning explicitly that a browser left holding someone's mail becomes somebody else's next — and then left the settings cache and `<accountId>:recent` on disk. **That list is other people's email addresses, and nothing ever removed it.** ## Changes **1. Sign-out clears local data, unconditionally.** Lending a laptop is the same exposure as a public machine, only quieter. Clear-by-default with a short keep-list — `lastUser` (only a trusted device writes it), the trust flag, and the random push device id — so a key added later is forgotten by default rather than by nobody having thought about it. Also runs on a 401. **2. The checkbox asks a question people can answer.** "Keep me signed in on this device" defaulted to `true`, which assumed the answer most costly to get wrong: someone on a library machine got a 30-day cookie unless they noticed a ticked box. | | before | after | |---|---|---| | question | "Keep me signed in on this device" | "This is my own device" | | default | ticked | **unticked** | | hint | none | says what each answer does | **3. Untrusted means untrusted:** session cookie only, nothing written locally, no push subscription, and a **5-minute idle sign-out**. Reads are gated as well as writes — a machine trusted once still has residue — and an untrusted sign-in purges it outright. ## On the idle timer instead of a reminder A "remember to sign out" prompt can't work: custom `beforeunload` text was removed from browsers years ago, and no event fires at all for walking away from a signed-in screen, which is the case that matters. A timer needs nobody's cooperation. ## Note on `remember` The wire field keeps its name. It is persisted inside `SESSION_FILE`, so renaming it would invalidate every session file on upgrade for a change of vocabulary. Client-side the concept is `isDeviceTrusted()`, derived from the session so a reload reaches the same answer without storing it — which an untrusted device could not do anyway. ## Verification Tests: **359 web** (+8 new covering both sides of the gate and the timer) and **80 server**, typecheck and build clean. Driven in a real browser against the mock, because a privacy control that only passes unit tests isn't verified: | path | result | |---|---| | untrusted sign-in, full session, folder expanded | `localStorage` **empty throughout** | | trusted sign-in | writes `settings`, `a1:recent`, `lastUser` as before | | sign-out from trusted | `recent` and `settings` gone, `lastUser` kept | | untrusted sign-in afterwards | clears even `lastUser` | 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_011pJ3S59iaT91dqW8rV3xFa **Merged** 2026-08-28 as coffey-labs/ihasmail@ddd1bbf9b357 <sub>Rebuilt from: git history, session transcript.</sub>
This repo is archived. You cannot comment on issues.