One refused request no longer ends the call and the session

Fixkamo-internal
Shipped
September 6, 2026 at 12:55 AM UTC
Author
Kamo
Commit
f68bcce

The last way a member could lose a live call and everything in their tool windows without doing anything wrong. `authenticatedFetch` answered EVERY 401 with `window.location.href = '/logout'` — from seventy-one call sites, with no check that the session had actually ended. That is the heaviest thing this app can do from the client: it tears the document down, which ends the SIP session living in it, and it deletes the shared Redis session that the member's other tabs are still working in. A 401 is simply the commonest way a server says "not this request"; it is not evidence that the session is over. The two code paths either side of it already knew that and this one did not. `sessionMonitor` requires two consecutive 401s before calling a token rejected, and `SessionManager`'s expiry warning probes /api/session/info and signs out only on 404, 401 or a TTL at zero, on the stated grounds that "a transient network blip or 5xx must never sign out an active user". This makes the third agree with them: - A 401 now confirms before it redirects, using that same probe. 404, 401 or TTL<=0 means gone and the logout proceeds exactly as before. A 5xx, an offline probe, or a live session means the endpoint refused this one request, which is handed to the caller as an ordinary `Unauthorized` failure instead of being answered by destroying the tab. - A tab holding no token of its own is never signed out on a 401, for the reason sessionMonitor spells out for its own branch: it was never authenticated HERE, so its 401 means "no credentials in this tab", and redirecting would delete a Redis session its siblings are still using. - The probe is shared while in flight. A page that fires six authenticated requests at once gets six 401s at once, and that is one question, not six. - useEntitlements' `isAbandoned` still reads "Session expired" and therefore still means "the document is leaving"; a live-session 401 now falls through to the loader's ordinary retry and self-heal, which is what should always have happened to it. And pin the call protections, which nothing was holding in place: A SIP session lives in this document and nothing on the server holds a handle to it, so a teardown ends the call outright. Five surfaces stand between a call and that, and every one is an effect inside a large file — the beforeunload confirm, the activity heartbeat that keeps the TTL alive while somebody is listening rather than typing, the pagehide BYE for when a teardown is unavoidable anyway, the tool-window close veto, and now the 401 confirmation. liveCallSurvival.test pins all five, and fails when any is removed (verified by removing three). Source greps rather than behavioural tests, deliberately: vitest.config.ts collects app/lib, app/components, app/patients, app/stores, app/network and app/utils — NOT app/contexts or app/hooks, where three of the five live. A test written beside them would be collected by nothing and pass by never running. 456 test files pass; eslint is clean on every file touched.

All changes

Like what you see shipping?

Every one of these updates lands in your workspace automatically. Start free and watch it grow week after week.

Start Free ForeverView Pricing