mancala/apps/backend/README.md
Halit Aksoy b472c2813c docs: document the game clock
Backend README (GAME_CLOCK_MS), AGENTS.md notes, TODO items for the open points and the mobile release order; game-history R3.3 and R3.4 marked as replaced. Checked end to end in Chrome: moves, a backend restart, a timeout and the replay.
2026-10-16 01:54:01 +03:00

3.3 KiB
Raw Blame History

Mancala Backend

This is the backend code for the Mancala project, written in Express.js.

Getting Started

Install Packages

yarn install

Start in Development Mode

yarn dev

Start in Release Mode

yarn build
yarn start

Development Dependencies

This package links the local packages @mancala/core and mancala.js. Build them first with yarn build:packages in the repo root.

Authentication

Clients log in with Keycloak (see apps/keycloak), either as a guest or with an account, and send the access token over the WebSocket right after connecting:

{ "channel": "auth", "message": { "token": "<Keycloak access token>" } }

The server answers on on_auth with the user ({ id, name, isGuest }) or on on_auth_error and closes the socket with code 4001. Other messages are ignored until the socket is authenticated, and connections that do not authenticate within 5 seconds are closed. Send auth again with the new token after refreshing it. User ids of Keycloak users are the token subject prefixed with kc:.

Variable Default Purpose
KEYCLOAK_ISSUER http://localhost:8080/realms/mancala Token issuer; must match the iss claim exactly
KEYCLOAK_JWKS_URL issuer + /protocol/openid-connect/certs Where to fetch signing keys, if the backend reaches Keycloak at a different address
KEYCLOAK_CLIENT_IDS mancala-web Comma-separated clients whose tokens are accepted
LEGACY_USERKEY_AUTH true Also accept old clients that connect with ?userKey=<uuid> and the /register endpoint. Turn off once no released client uses it, because a userKey is not a secret the server can check

Saved games and live games

Finished games are saved in Postgres; live games are copied to Valkey after every change and restored when the backend starts, so a restart or deploy does not end them. The design is in docs/specs/game-history.

Variable Default Purpose
DATABASE_URL postgres://mancala:mancala@localhost:5433/mancala Saved games; migrations run on startup
VALKEY_URL redis://localhost:6379 Live game state and short-lived caches

The backend keeps running when either is down: games are still played, finished games are saved once Postgres is back, and lists are served without the missing data.

Game clock

Each player has a clock for the whole game; only the clock of the player whose turn it is runs, also while they are offline. A move that arrives after the mover's time has run out is refused, and GameClockScheduler ends games whose running clock reaches zero once a second, so the opponent wins on time even if nobody is connected. Time while the backend is down is not charged. The design is in docs/specs/game-clock.

Variable Default Purpose
GAME_CLOCK_MS 300000 Time each player has for a game; a short value such as 40000 helps when testing by hand
Route Auth Response
GET /game/:id – A live game, or { status: "finished", record }
GET /games/popular?limit=5 – { kind: "live" | "finished", games }
GET /games/me/active Bearer token { game } or { game: null }
GET /games/me?limit=3 Bearer token { games }, newest first

Tests use Docker (testcontainers) for Postgres and Valkey.