Status: accepted · Date: 2026-11-09 · Amends: ADR-0003
Enforcement: packages/api/src/platform/rpc/session-authority.test.ts, packages/auth/src/auth-round-trips.test.ts, apps/web/src/routes/api/session-revocation.test.ts.
Every signed-in request previously read the Session and User from Postgres. A mobile scoped call read it twice. The files gateway read it twice and then read membership. Issue #189 and PRs #192–#194 reduced the browser presentation read; the owner accepted a 60 second revocation window for ordinary data access to remove the remaining serial session read.
Decision
- SSR, oRPC and the files gateway read the request session from Better Auth's signed compact cookie snapshot, at most 60 seconds old, or from Postgres on a miss.
- Procedures that create, reveal or redirect credentials or standing channels, change account control, administer Users, touch billing or push, or approve a pending agent tool call that lets a run continue (
agentTools.resolve) read the database session. Resolving a stored proposal (proposals.resolve) is a one-shot data write, like the manual path, and uses the request session.markAuthoritativeandsession-authority.test.tsclassify every procedure. Admin and sensitive Better Auth endpoints remain authoritative. - The membership reader checks
memberon every request. RLS checksapp.org_id()on every tenant statement. - The Better Auth handler strips the snapshot from every endpoint except the presentation
GET /get-session. - The cookie integration forwards
Set-Cookieon a miss. A hit does not re-issue the snapshot. - The mobile scope header is compared with the request session. Authoritative procedures compare it again with the database read.
Consequences
Session reads records the revocation behavior. A copied cookie can continue to access ordinary data for at most 60 seconds. An SSE request keeps the identity read at its start. Presigned download and upload URLs require a database session check before minting. A revoked session cannot mint a new URL; one minted before revocation remains usable until expiry, at most 15 minutes. Bump session.cookieCache.version when fields used by the server change. A mobile RPC response does not store the re-issued cookie, so subsequent requests can miss again. Local measurements are recorded in the measurement report.
Alternatives
- The status quo retains a serial database round trip on every request.
- KV
secondaryStorageis eventually consistent, has no atomic operations, and moves sessions out of Postgres. - Isolate-local memory has no cross-isolate invalidation.
- A shorter
maxAgereduces the window but increases misses.