Mint Cap'n Web authentication tickets during TanStack Start SSR
Context#
Today a browser explicitly authenticates its Cap'n Web socket from the
exact-origin HTTP-only cookie. Replace that bootstrap credential with a
short-lived, one-time ticket minted while TanStack Start renders the already
authenticated document. Hydration can then open /api and call
authenticate({ type: "ticket", ticket }) without an extra HTTP request. The
same mechanism should work for apps/os and project userspace apps.
Proposed flow#
- Server-only request code mints a ticket bound to the auth app, browser
session, exact origin, and
/apiaudience. - The private, non-cacheable SSR payload carries it to hydration.
authenticate({ type: "ticket", ticket })atomically consumes it and returns the same app-defined session capability as other bootstrap methods.- Reconnects obtain a fresh ticket through a documented same-origin path.
Security invariants#
- Mint only behind a TanStack server-only request boundary; ordinary loaders are isomorphic.
- Tickets are cryptographically strong, short-lived, single-use grants bound to the app, session, exact origin, and audience.
- The SSR response is private and non-cacheable. Tickets never enter URLs, reusable loader/prefetch caches, logs, traces, analytics, errors, or persisted client state.
- A ticket is JavaScript-visible by design. Treat XSS prevention and CSP as part of the boundary, and keep the long-lived session cookie HTTP-only.
- Failed, expired, replayed, wrong-origin, and wrong-audience redemption are explicit authentication outcomes rather than generic transport failures.
Acceptance criteria#
- One server-only ticket lets hydration authenticate
/apiwith no extra HTTP request and returns the ordinary app-session capability. - Replay, expiry, wrong app/origin/audience, and revoked sessions fail deterministically.
- Tests prove atomic consumption and absence from caches, URLs, and telemetry.
- Reconnection has a documented fresh-ticket path.
- Prove one userspace app before migrating
apps/osfromfrom-server-cookie.
Non-goals#
- The userspace Cap'n Web/LiveState runtime and React client APIs.
- Serializing an RPC stub into SSR output.
- Removing exact-origin cookie bootstrap before ticket reconnects are production-proven.