preview demo: drop a stored login for another server before the app reads it #80

Merged
void merged 1 commit from void/demo-homeserver-unreachable into dev 2026-11-10 18:24:00 +00:00
Member

Root cause

All previews of this site share the origin minds.gamecult.org, so they share one localStorage. A login another preview left there (cinny_hs_base_url = a real homeserver, plus token, user id and device id) is read by the demo build via getFallbackSession; the app then loads that server's spec versions, guardedFetch refuses every non-demo origin, and SpecVersions shows "Unable to connect to the homeserver".

Reproduced on Starfire (headless Chrome, desktop 1920x1080 and phone 390x844@2x) against the live https://minds.gamecult.org/void/rat-dev-66c4c637/ with localStorage pre-seeded as another preview would leave it (cinny_hs_base_url=https://chat.gamecult.org): the dialog appears, with
PAGEERROR TypeError: preview: pretend server only; fetch to https://chat.gamecult.org/_matrix/client/versions refused.
It reproduced with a clean service-worker state, with an already registered service worker, and at both viewports. Clean profile: login and sign-in to the room list work. A stored demo session (base https://demo.invalid): lands on the room list, no error. A stale service worker is not the cause (each preview registers its own subpath scope; the foreign case fails with or without one). The pretend server answers /versions fine, and config.json/hashRouter/subpath handling are fine.

Fix

installDemo (src/preview-demo/install.ts) calls dropForeignSession first: a stored login whose base URL is not the demo origin is removed before the app reads it. The earlier banner text for a foreign session is deleted (the state it described can no longer exist). In src/index.tsx the service worker is registered after the demo has settled storage, so the worker is never handed the dropped session. getFallbackSession/removeFallbackSession take an optional Storage (default localStorage) so the rule is testable.

Tests

src/preview-demo/server.test.ts: dropForeignSession removes a real-homeserver session and nothing else, keeps the demo session; installDemo over a stored foreign login leaves storage empty. Full npx vitest run: 49 files, 708 tests pass. vite build (VITE_PREVIEW_DEMO=1) passes.

Starfire results (scratch build /void/rat-demo-repro-b5449936/)

Per viewport (desktop and phone), all of: clean, foreign session, demo session, SW registered first, foreign session after SW warm-up (a sixth state in the script, "no prior SW", was identical to the foreign one). Every state without a stored demo session reaches the login screen and demo sign-in reaches the room list (GameCult / General / Commons / Projects / DMs); the demo-session state goes straight to the room list. No "Unable to connect" in any. Console: only the known OIDC issuer errors. Before the fix the three foreign states showed the dialog at both viewports.

Not checked

Real touch, a real person's browser with other profile state (IndexedDB sync stores from a real build are untouched; only the four session keys are dropped). Symmetric hazard left alone: a demo visit leaves cinny_hs_base_url=https://demo.invalid, which a real-homeserver preview on the same origin will read and fail on; that needs its own fix in the real build, not here.

🤖 Generated with Claude Code

## Root cause All previews of this site share the origin minds.gamecult.org, so they share one localStorage. A login another preview left there (`cinny_hs_base_url` = a real homeserver, plus token, user id and device id) is read by the demo build via `getFallbackSession`; the app then loads that server's spec versions, `guardedFetch` refuses every non-demo origin, and `SpecVersions` shows "Unable to connect to the homeserver". Reproduced on Starfire (headless Chrome, desktop 1920x1080 and phone 390x844@2x) against the live https://minds.gamecult.org/void/rat-dev-66c4c637/ with localStorage pre-seeded as another preview would leave it (cinny_hs_base_url=https://chat.gamecult.org): the dialog appears, with `PAGEERROR TypeError: preview: pretend server only; fetch to https://chat.gamecult.org/_matrix/client/versions refused`. It reproduced with a clean service-worker state, with an already registered service worker, and at both viewports. Clean profile: login and sign-in to the room list work. A stored demo session (base https://demo.invalid): lands on the room list, no error. A stale service worker is not the cause (each preview registers its own subpath scope; the foreign case fails with or without one). The pretend server answers /versions fine, and config.json/hashRouter/subpath handling are fine. ## Fix `installDemo` (src/preview-demo/install.ts) calls `dropForeignSession` first: a stored login whose base URL is not the demo origin is removed before the app reads it. The earlier banner text for a foreign session is deleted (the state it described can no longer exist). In src/index.tsx the service worker is registered after the demo has settled storage, so the worker is never handed the dropped session. `getFallbackSession`/`removeFallbackSession` take an optional Storage (default localStorage) so the rule is testable. ## Tests src/preview-demo/server.test.ts: `dropForeignSession` removes a real-homeserver session and nothing else, keeps the demo session; `installDemo` over a stored foreign login leaves storage empty. Full `npx vitest run`: 49 files, 708 tests pass. `vite build` (VITE_PREVIEW_DEMO=1) passes. ## Starfire results (scratch build /void/rat-demo-repro-b5449936/) Per viewport (desktop and phone), all of: clean, foreign session, demo session, SW registered first, foreign session after SW warm-up (a sixth state in the script, "no prior SW", was identical to the foreign one). Every state without a stored demo session reaches the login screen and demo sign-in reaches the room list (GameCult / General / Commons / Projects / DMs); the demo-session state goes straight to the room list. No "Unable to connect" in any. Console: only the known OIDC issuer errors. Before the fix the three foreign states showed the dialog at both viewports. ## Not checked Real touch, a real person's browser with other profile state (IndexedDB sync stores from a real build are untouched; only the four session keys are dropped). Symmetric hazard left alone: a demo visit leaves cinny_hs_base_url=https://demo.invalid, which a real-homeserver preview on the same origin will read and fail on; that needs its own fix in the real build, not here. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
preview demo: drop a stored login for a real homeserver before the app reads it
All checks were successful
Build pull request / Build pull request (pull_request) Successful in 3m53s
5bb78b21cf
Every preview shares the origin minds.gamecult.org and so one localStorage. A login another
preview left (cinny_hs_base_url of a real homeserver) made the demo connect to that server, which
the guard refuses, so SpecVersions showed 'Unable to connect to the homeserver'. installDemo now
drops any stored session whose base is not the demo origin, and the service worker is registered
after that so it is never handed the dropped session. Replaces the foreign-session banner text.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
void merged commit 84770a2d2c into dev 2026-11-10 18:24:00 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
GameCult/Ratatoskr!80
No description provided.