Session tokens are opaque strings, generated by NextAuth. They aren't JWTs and can't be decoded.
isAdmin is checked server-side by AdminGuard, which reads req.user.is_admin off the row SessionGuard attached. session.user.isAdmin (the NextAuth session, set in the callback in auth.ts) and the admin-list DTO's isAdmin field both exist for UI rendering only - the landing nav's Backoffice link and the users table respectively - and neither is what enforces access.
AUTH_SECRET must match between frontend and backend. NextAuth uses it for cookie signing; the backend uses it for nothing today (it's there for forward compatibility).
Expired sessions are rejected by AuthService, and expired rows are swept on a schedule. session-cleanup.worker.ts is a wired @Cron(CronExpression.EVERY_HOUR) job that deletes rows whose expires has passed from sessions, verification_tokens and magic_link_attempts, and rows older than 30 days from billing_webhook_events and billing_payment_hook_claims. All five DELETEs fire concurrently against the same pool.
Magic-link sentinel admin: the seeded MCP admin user uses email [email protected]. The .invalid suffix is RFC 6761 reserved (no MX possible) and the auth-adapter short-circuits magic-link issuance for it. Both halves are load-bearing: if you rename it to an address that can receive mail, anyone able to request a magic link for it can sign in as the MCP admin.