# lila — backlog

Labels: `[feature]` `[infra]` `[security]` `[ux]` `[debt]`

---

## now

Things that are actively in progress or should be picked up immediately. Mostly operational risk and the remaining phase 7 hardening work.

- **Hetzner domain migration check** `[infra]`
  Verify whether the lilastudy.com domain needs to be migrated following a Hetzner DNS change. Check Hetzner dashboard for any pending migration notice.

---

## next

Clearly planned work, not yet started. No hard ordering — sequence based on what unblocks real users first.

- **Batch distractor queries to eliminate N+1** `[debt]`
  createGameSession calls getDistractors once per term in parallel — 3 queries for 3 rounds, 10 for 10. Each query does ORDER BY RANDOM() which can't use an index and gets slower as the translations table grows. Fix: add a getDistractorsForTerms(termIds[], ...) function to @lila/db that batches all distractor fetches into a single query and returns results grouped by term. The service distributes the results per question. Prerequisite: none. Blocked by: nothing, but coordinate with any ongoing @lila/db changes.

- **Atomic session creation** `[debt]`
  createGameSession reads from Postgres (getGameTerms, getDistractors) then writes to the session store (in-memory/Valkey). A crash between the two leaves the terms consumed with no session created — the user gets an error and retries, no data is corrupted, but the work is wasted. A true transaction boundary isn't achievable across two different systems (Postgres + Valkey have no shared coordinator). Options when revisiting: store sessions in Postgres instead of Valkey (full transactionality, higher latency), or accept the current behaviour and add retry logic on the client. Revisit after Valkey is in production and actual failure rates are observable.

- **Guest / try-now flow** `[feature]`
  Allow users to play a quiz without signing in so they can see what the app offers before creating an account. Make auth middleware optional on game routes, add a "Try without account" button on the landing/login page.

- **Favicon, page titles, Open Graph meta** `[ux]`
  Add favicon, set proper per-route page titles, add Open Graph meta tags for link previews.

- **Accessibility pass** `[ux]`
  Keyboard navigation for quiz buttons, ARIA labels on interactive elements, focus management during quiz flow.

- **Monitoring and logging** `[infra]`
  Uptime monitoring and centralized logging on the VPS. Options: `chkrootkit`/`rkhunter` for security, `logwatch`/`monit` for daily summaries.

- **Offsite backup storage** `[infra]`
  Database backups currently live on the same VPS. Add offsite copies to Hetzner Object Storage or an S3-compatible service to protect against VPS failure.

- **Valkey for game session store** `[infra]`
  Add Valkey to the production Docker stack. Implement `ValkeyGameSessionStore` against the existing `GameSessionStore` interface. Required before multiplayer scales.
  NOTE: the rate limiting middleware needs to be adjusted for valkey, see todo comment

- **User stats endpoint + profile page** `[feature]`
  `GET /users/me/stats` returning games played, score history, etc. Frontend profile page displaying the stats.

- **Admin dashboard** `[feature]`
  User management, overview of words and languages, and per-term stats. Not urgent but has real operational value once real users are present.

- **Email + password login** `[feature]`
  Traditional email/password auth as an alternative to social login. Configure via Better Auth.

- **Apple login** `[feature]`
  Add Apple as a social login option via Better Auth. Requires Apple Developer account and Sign in with Apple configuration.

- **Graceful WS reconnect** `[infra]`
  Handle WebSocket disconnections gracefully. Reconnect with exponential back-off. Restore game state on reconnection if a game is still in progress.

- **Configurable game settings in multiplayer lobby** `[feature]`
  Game settings (mode, round count, timer duration, target score) are currently hardcoded. The host should be able to configure these when creating a lobby. Settings should be stored in the settings jsonb column on the lobbies table and passed through to the game service at start.

- **Tighten CSP to remove unsafe-inline** `[security]`
  Current script-src uses 'unsafe-inline' to accommodate framework-injected inline scripts (likely TanStack Router hydration). Tightening this would require nonce-based CSP, which needs server-rendered HTML or a Caddy layer that injects per-request nonces. Not urgent — pragmatic CSP with 'unsafe-inline' is mainstream for SPAs at this scale. Revisit if the app handles more sensitive data or grows a meaningful user base

- **Publish Google OAuth consent screen** `[infra]`
  App is currently in testing mode, which caps OAuth sign-ins at 100 users. Before hitting that limit, publish the consent screen in Google Cloud Console. Basic scopes (email, profile, openid) require no Google review — just fill in branding fields (app name, logo, support email, privacy policy URL) and click publish. Trigger: do this before reaching 80 users.

---

## later

Directionally right, timing is unclear. Revisit when the next/now work is done.

- **Game modes** `[feature]`
  Five modes are designed in `game_modes.md` — TV Quiz Show, Race to the Top, Chain Link, Elimination Round, Cooperative Challenge. The lobby infrastructure is mode-agnostic; each mode adds game logic only. First mode to implement is TBD. This is effectively a new phase.

- **Single Player Extended** `[feature]`
  Expanded singleplayer flow. Possible directions: longer sessions with increasing difficulty, streak bonuses, mixed POS/language rounds, progress tracking across sessions, timed challenge mode.

- **Users in a separate database** `[infra]`
  Architectural separation of auth/user data from vocabulary and game data. No immediate benefit — revisit after hardening is complete and user growth justifies the complexity.

- **Modern env management** `[debt]`
  Replace `.env` files with a more robust approach (e.g. `dotenvx`, `infisical`). Current setup works but is error-prone and not versioned.

- **Reorganize datafiles and wordlists** `[debt]`
  The current layout of `data-sources/`, `scripts/datafiles/`, `scripts/data-sources/`, and `packages/db/src/data/` is confusing with overlapping content. Consolidate into a clear structure.

- **Resolve eslint peer dependency warning** `[debt]`
  `eslint-plugin-react-hooks 7.0.1` expects `eslint ^3.0.0–^9.0.0` but found `10.0.3`. Low impact but worth cleaning up when nearby.

- **husky + lint-staged** `[debt]`
  Set up husky and lint-staged to run linting and formatting checks before every commit. Prevents CI failures from formatting or lint issues that slipped through locally.

- **OpenAPI documentation for REST endpoints** `[feature]`
  Document the API surface using OpenAPI/Swagger. Covers all REST endpoints with request/response shapes. Useful groundwork for the admin dashboard and any future contributors.

- **Frontend tests** `[debt]`
  component tests for QuestionCard, OptionButton, ScoreScreen; consider Playwright or Vitest browser mode for e2e

---

## changelog

Shipped milestones, newest first.

- **04 - 2026 - t00001 - Docker credential helper**
- **04 - 2026 - Pin dependencies in package.json** - Unpinned deps in a CI/CD pipeline are a real risk.
- **04 - 2026 - React error boundaries** - Catch and display runtime errors gracefully instead of crashing the entire app.
- **04 - 2026 - 404 and redirect handling** - Unknown routes return raw errors. Add a catch-all route on the frontend for client-side 404s.
- **04 - 2026 - Multiplayer GameService unit tests** - round evaluation, scoring, tie-breaking, timeout handling
- **04 - 2026 - Security headers with helmet** - Add helmet middleware to set secure HTTP response headers.
- **04 - 2026 - Rate limiting on API endpoints** - At minimum: auth endpoints (brute force prevention) and game endpoints (spam prevention)
- **04 - 2026 — Migrations in deploy pipeline** — Drizzle migrate runs as a CI/CD step before the API container restarts
- **04 - 2026 — Phase 6: Production deployment** — Hetzner VPS, Caddy HTTPS, Forgejo CI/CD, daily DB backups, cross-subdomain auth
- **04 - 2026 — Phase 5: Multiplayer game** — real-time simultaneous play, 15s server timer, live scoring, winner screen
- **04 - 2026 — Phase 4: Multiplayer lobby** — WebSocket server, lobby create/join, real-time player list
- **04 - 2026 — Phase 3: Auth** — Better Auth, Google + GitHub social login, session middleware, auth guard
- **04 - 2026 — Phase 2: Singleplayer UI** — full quiz loop in browser, game setup, question card, score screen
- **04 - 2026 — Phase 1: Vocabulary data + API** — WordNet/OMW data pipeline, CEFR enrichment, game session endpoints
- **04 - 2026 — Phase 0: Foundation** — pnpm monorepo, TypeScript, ESLint, Vitest, Drizzle, Docker Compose
