Security model
Authorization is enforced explicitly in application code rather than delegated to the database, so every check is visible at the route that depends on it.
- Every
/api/engine/*route is token-gated; the socket upgrade included (its?token=is verified by the same middleware). - Reads are uid-scoped: "my games", ratings, rating history, and profile all
filter to the caller.
getPlayersreturns only public identity (username, display name, avatar, anonymity) — never email. A user's own email is returned only by their own/me. - Game visibility is a capability model. A game id is an unguessable UUID; a private game is unlisted (never in the lobby) and joinable only by someone who holds its id or shortCode. Reading a game summary requires the id, and the sensitive part — the game state (frames) — is separately gated: only a participant, or anyone for a finished public game, may fetch frames.
- Seat ownership is enforced at the DO against its authoritative roster, so a client (or a misbehaving external bot) can never act on a seat it doesn't hold — a clean 403, not a crash.
- Bot webhooks are HMAC-authenticated with domain-bound, constant-time verification (see Bots); the client cannot forge a bot move and a bot cannot reflect a wake into an action.
- Tokens are RS256-pinned and issuer/audience-checked; secrets
(
BOT_SIGNING_SECRET, theFIREBASE_*service account) are read from env by convention and absent by default (each feature is simply off when unconfigured). - The Worker strips inbound
x-eigen-*headers before forwarding a socket upgrade to the DO and sets the principal itself — a client cannot spoof its identity to the DO.