Capability pack
Text game
A browser game where the browser is not trusted. State lives in a row with a version column, so two tabs saving at once produce one winner and an honest error rather than a silently lost turn. Scores are computed on the server from the saved state, which is what makes a leaderboard mean anything.
Capabilities
5
All of them are built.
Watched in production
0
5 have never been watched against a real provider.
Provider variables for a fresh project
3
Including the packs it sits on. All set here.
Needed regardless of providers
- A Postgres database — save slots and the leaderboard.
What is in it
Each row links to where it lives, and carries the state it has in THIS deployment.
Three slots per player. The browser sends words and the version it saw; the server runs the engine and writes only where the version still matches, so a stale tab cannot undo a move. A guest plays the same engine in the browser with localStorage — both variants, one reducer.
SaveGame rows with JSON state and an optimistic-lock version, a pure reducer run on the server
No browser ever sends a score. Each move's new state is scored server-side and replaces the player's best only when higher. Standard competition ranking with ties; display names, never emails.
Score rows, best per player per board, computed from state on the server
Transactions
ReadyA change and its audit entry commit together, or not at all.
Prisma $transaction
Live updates
ReadyThe bell holds one EventSource on /api/events; the server checks the database every three seconds and writes an event only when this user's count changed, ends the stream before the platform would, and the browser reconnects. Scoped to the session, 401 for guests. NEXT_PUBLIC_REALTIME_TRANSPORT=poll falls back to the 30-second poll. WebSockets stay designed for when users must see each other.
Server-sent events (rung two of the realtime ladder), polling kept as the plain transport
- Ready
Written in the same transaction as the event they announce; mark-read scoped to the reader in the WHERE clause; the badge polls every 30s only while the tab is visible.
Notification table + a polling bell
First steps in a fork
- 1Replace the rules in src/lib/game with your own; the save and score seams do not change.
- 2Keep the version column: it is the whole reason two tabs cannot corrupt a save.
- 3Decide whether scores are public before anyone plays, not after.
What to delete
- The shop, the forum, and the CMS.
Provider variables a fresh project would set
AUTH_GOOGLE_IDAUTH_GOOGLE_SECRETRESEND_API_KEY
Shaded green: set on this deployment.
The full inventory, with the tests that cover each capability and what has actually been watched working.
Capability inventory