Goal
Derive "am I signed in" from the engine, not from a tab-local store.
Problem
apps/web/src/stores/auth.store.ts is an independent, optimistic, per-tab writer, and #911 made it the thing the login redirect keys on. blueprint/web-client.md "UI state law" says the opposite: rendered state is the engine's word, with no independent writers, and unauthenticated pages redirect on facade auth state.
The engine event stream carries nothing auth-shaped — packages/client/src/worker/protocol.ts has snapshotUpdated, stalenessChanged, withheldUpdateEscalation, deadLetter, attributableAbuse, renewalFailed, opProgress — so there is nothing to derive from yet.
Consequences today:
facade.logout is origin-wide: it zeroizes the shared leader engine. authStore is per-tab memory, so logging out in tab A leaves tab B rendering /files as authenticated over a dead engine.
- A token refusal, or a failover whose
SecretSource re-export fails, leaves the UI signed in over an engine that is not.
- This is the v1 two-store desync class re-forming one layer up.
Work
- Add a session/auth event to the engine facade event stream and thread it through
crates/wasm and packages/client.
- Project it through the existing
useSyncExternalStore snapshot adapter.
- Reduce
authStore to chrome only: the login method and the email to display.
- Add the
RequireAuth route guard for /files on top of the derived state, which App.tsx still lacks.
Gate
web unit + the web-e2e login spec. Both halves can run: tests/web-e2e/ exists (#809, #1078).
Dependencies
None outstanding. #804 (the auth store and login redirect), #805 (the useSyncExternalStore snapshot adapter), #809 (the web-e2e harness), and #910 (the threading surface this shares) have all landed — so there is no longer a land-one-rebase-the-other conflict with #910 to sequence around.
Blocks
Corrected 2026-08-06 against main @ 3d087d673. Stale before this edit: the ## Depends on section named #804, #805, #809 and #910, all now closed, so the issue read as blocked; the "land one, rebase the other" note against #910 is likewise discharged.
Part of #642
Goal
Derive "am I signed in" from the engine, not from a tab-local store.
Problem
apps/web/src/stores/auth.store.tsis an independent, optimistic, per-tab writer, and #911 made it the thing the login redirect keys on.blueprint/web-client.md"UI state law" says the opposite: rendered state is the engine's word, with no independent writers, and unauthenticated pages redirect on facade auth state.The engine event stream carries nothing auth-shaped —
packages/client/src/worker/protocol.tshassnapshotUpdated,stalenessChanged,withheldUpdateEscalation,deadLetter,attributableAbuse,renewalFailed,opProgress— so there is nothing to derive from yet.Consequences today:
facade.logoutis origin-wide: it zeroizes the shared leader engine.authStoreis per-tab memory, so logging out in tab A leaves tab B rendering/filesas authenticated over a dead engine.SecretSourcere-export fails, leaves the UI signed in over an engine that is not.Work
crates/wasmandpackages/client.useSyncExternalStoresnapshot adapter.authStoreto chrome only: the login method and the email to display.RequireAuthroute guard for/fileson top of the derived state, whichApp.tsxstill lacks.Gate
web unit + the web-e2e login spec. Both halves can run:
tests/web-e2e/exists (#809, #1078).Dependencies
None outstanding. #804 (the auth store and login redirect), #805 (the
useSyncExternalStoresnapshot adapter), #809 (the web-e2e harness), and #910 (the threading surface this shares) have all landed — so there is no longer a land-one-rebase-the-other conflict with #910 to sequence around.Blocks
Corrected 2026-08-06 against
main@3d087d673. Stale before this edit: the## Depends onsection named #804, #805, #809 and #910, all now closed, so the issue read as blocked; the "land one, rebase the other" note against #910 is likewise discharged.Part of #642