🪪 Identity
Lifecycle: CURRENT
Guest-first Nakama identity with one request-boundary owner and one versioned cookie pair is the platform rule.
Dual-corpus note: older material underdev/docs/content/_attic/legacy-wiki-funday-gg/is historical and Quartz-ignored.
Request lifecycle
frontend/src/hooks.server.ts is the only ordinary-request identity owner. It delegates to resolveRequestIdentity() before route code runs:
- Parse
funday-identityv1 and the independentfunday-device-idrecovery cookie. - Require agreement among JWT claims, session user ID/username, account ID/username, and the owned device.
- Hydrate or refresh the existing Nakama session; otherwise restore the same device.
- Create a guest only after Nakama explicitly reports
device_not_found. - Populate
event.locals; routes consume those locals instead of parsing cookies or provisioning identity.
Both cookies use Path=/, HttpOnly, SameSite=Lax, a one-year maximum age, and Secure on HTTPS. SvelteKit subrequests may hydrate a valid canonical identity, but they never provision, reconcile, rotate, or write identity cookies. The sole explicit rotation path is /api/auth/ensure-session?fresh=1.
Nakama unavailability is an explicit 503. There is no local_fallback, synthetic account, or degraded identity success.
Mutation rule
Identity mutations are paired and sequential, not a cross-system transaction:
- Update the Nakama account.
- Rewrite the complete validated
funday-identity+funday-device-idpair.
A failed Nakama write must not mutate local identity or report success. Evidence: frontend/src/routes/api/user/display-name/+server.ts, frontend/src/routes/api/user/username/+server.ts, and frontend/src/routes/api/user/avatar/+server.ts.
Handles
- New guest
usernameand initialdisplayNamevalues use the same alphabetic three-word name from a measured 30,483,706-name namespace. Device-derived collision attempts remain alphabetic and never append numeric/hash suffixes. - Guest usernames are fixed policy. Registered users may change username through the username API.
displayNameremains a mutable persona value.
Profiles and leaderboards
/profilewithoutuserIdresolves the authenticated viewer. A supplied ID must be a valid real Nakama user: malformed targets return400, missing users return404, and dependency failure returns503; the viewer is never substituted.- Profile activity and friend data call shared server-domain helpers directly. Same-app HTTP self-fetches are prohibited.
- Leaderboard IDs/defaults come from game manifests. Reads use the isolated Nakama system-reader session. Empty boards, unavailable boards, and unknown IDs are distinct; mock leaderboard rows are prohibited.
Source inventory: funday-codex.yml under identity.
Proof links
| Claim | Proof |
|---|---|
| Single request-boundary owner and subrequest no-write law | frontend/tests/unit/hooks.identity.test.ts, frontend/src/lib/server/auth/requestIdentity.test.ts |
| Versioned pair validation, migration, and token-claim agreement | frontend/src/lib/server/identityCookieHelper.test.ts, frontend/src/lib/server/auth/sessionHydration.test.ts |
| Device restore/create boundary and collision policy | frontend/src/lib/server/auth/guestSessionEngine.test.ts, frontend/src/lib/utils/usernameGenerator.test.ts |
| Stable browser identity across reloads/routes/tabs and distinct contexts | frontend/tests/e2e/identity-persistence.spec.ts |
| Real target profile status law | frontend/src/routes/profile/page.server.test.ts |
| Manifest-derived Nakama leaderboard reads | frontend/src/lib/server/leaderboards.test.ts, frontend/src/routes/api/leaderboards/[id]/server.test.ts |
| Isolated system-reader lifecycle | frontend/src/lib/server/auth/systemSession.test.ts |
Custom Node adapter preserves repeated Set-Cookie headers | frontend/server/requestOrigin.test.ts |