EE gate cookie vs browser keys
2026-10-19 investigation of team.hyper.media showing content while the header offered sign-in. The gate cookie and the browser key store are two independent states, and Chrome dropped one of them.

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

__Host-HM-Session (+ __Host-HM-Auth-Token) cookies, HttpOnly, set by POST /hm/api/auth

"this browser belongs to member X"

Caddy forward_auth → /hm/api/session, the document gate, /api/*

30 days (daemon token TTL)

IndexedDB keyStore-04 / keys-01

the Ed25519 session key delegated by the vault account

the header (useLocalKeyPair), every signing action, the sign-in page's "already signed in" path

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