# Server reads the session from the cookie cache

The repository's architectural decision, rationale and consequences.

# Server reads the session from the cookie cache

**Status:** accepted · **Date:** 2026-11-09 · **Amends:** [ADR-0003](0003-organizations-are-the-tenant.md)

**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 [#decision]

1. 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.
2. 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. `markAuthoritative` and `session-authority.test.ts` classify every procedure. Admin and sensitive Better Auth endpoints remain authoritative.
3. The membership reader checks `member` on every request. RLS checks `app.org_id()` on every tenant statement.
4. The Better Auth handler strips the snapshot from every endpoint except the presentation `GET /get-session`.
5. The cookie integration forwards `Set-Cookie` on a miss. A hit does not re-issue the snapshot.
6. The mobile scope header is compared with the request session. Authoritative procedures compare it again with the database read.

## Consequences [#consequences]

[Session reads](/docs/features/auth#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](../measurements/2026-11-09-server-session-cache.md).

## Alternatives [#alternatives]

- The status quo retains a serial database round trip on every request.
- KV `secondaryStorage` is eventually consistent, has no atomic operations, and moves sessions out of Postgres.
- Isolate-local memory has no cross-isolate invalidation.
- A shorter `maxAge` reduces the window but increases misses.

