Symptom (2026-10-19, Eric, team.hyper.media)
Pages render (the sign-in gate lets the browser through), but the top-right header shows the empty account circle, and clicking it starts the vault sign-in flow again.
Why: two stores that can disagree
An Enterprise site keeps a person's sign-in in two places that nothing reconciles:
store | what | who checks it | lifetime |
|---|---|---|---|
| "this browser belongs to member X" | Caddy | 30 days (daemon token TTL) |
IndexedDB | the Ed25519 session key delegated by the vault account | the header ( | until the browser drops it |
The header renders the placeholder avatar whenever the key pair is missing (web-utils.tsx, if (!keyPair)), regardless of the cookie. The gate never looks at the keys. So "cookie present, keys gone" = exactly this symptom; "keys present, cookie gone" = the sign-in page's silent re-auth, which is why the opposite case is invisible.
Evidence on yacht
Chrome profile "Eric" (Profile 3, ericvicenti@gmail.com, the active profile) holds both site cookies created 2026-10-14 21:45Z, valid to 2026-11-13, last sent 2026-10-19 10:22Z. Chrome History shows the vault sign-in completing in that profile on 2026-10-14 21:44Z and the visits at 10:21–10:22Z today that produced the report.
That profile's IndexedDB/https_team.hyper.media_0.indexeddb.leveldb was created at 10:22:04Z today ("Creating DB … since it was missing", leveldb LOG). Chrome's WebStorage/QuotaManager has a fresh bucket row (id 4582) for the origin created at the same moment; the Oct 14 bucket is gone.
It was not only this site: the profile's IndexedDB directories for hyper.media, calendar.google.com, mail.proton.me and platform.claude.com were recreated on 2026-10-16 16:47–17:51 local, and github.com, seed.hyper.media, host.seed.hyper.media, mail.google.com, meet.google.com, seedteamtalks.hyper.media and team.hyper.media today between 10:25 and 12:22 local. Cookies and HTTP caches were untouched. The Data volume is at 92 % (77 GB free). No extension has the browsingData permission; Chrome's clear-data prefs do not include site data. The exact Chrome mechanism (quota eviction, or a Chrome 155 cleanup) is not pinned down.
The server is fine: SEED_SESSION_SECRET unchanged since the instance was created on 2026-10-14 17:07Z (config and container env hash agree), release 2026.10.6 on both web and daemon, no DELETE /hm/api/auth in 5 days of logs. The vault membership for eric@seedhypermedia.com is account z6MkvHny… ("ERIC").
The second Chrome profile ("Seed Hypermedia", Profile 5) still has its keys and re-authenticated normally at 10:02:56Z today.
What to change (proposal, not yet done)
Reconcile on load. The root loader already knows whether a gate session exists (getWebSession). Expose a boolean to the client; once useLocalKeyPairLoaded() is true and there is no key pair (or the key's delegatedAccountUid differs from the cookie's account), call DELETE /hm/api/auth and do a full navigation to /hm/sign-in?next=<current>. Skip on the open pages (/hm/sign-in, /hm/auth/callback, /hm/email-notifications). The person then lands on the normal sign-in page instead of a half-signed-in site. On hosted sites (no gate) there is nothing to do: the daemon cookie without keys is harmless.
Ask Chrome to keep the key store. Call navigator.storage.persist() after a successful sign-in; Chrome grants it silently for engaged/bookmarked/installed sites and it exempts the origin from best-effort eviction. Cheap, and it protects hosted sites too.
Optional: when the key is missing but the cookie is valid, the sign-in page could say "Your browser lost its sign-in key; sign in again" rather than the generic text, so people do not think they were logged out by the server.
Related: the Enterprise working notes at /enterprise-edition/working-notes.
Do you like what you are reading? Subscribe to receive updates.
Unsubscribe anytime