Skip to content
All packs

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.
Sits on top ofAccounts10 inherited capabilities, counted once in the number above.

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

    Ready

    A change and its audit entry commit together, or not at all.

    Prisma $transaction

  • Live updates

    Ready

    The 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

  • 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

  1. 1Replace the rules in src/lib/game with your own; the save and score seams do not change.
  2. 2Keep the version column: it is the whole reason two tabs cannot corrupt a save.
  3. 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_ID
  • AUTH_GOOGLE_SECRET
  • RESEND_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