From ef5c49f7cf98381e7902cc1289869dc2c7ac4980 Mon Sep 17 00:00:00 2001 From: lila Date: Sun, 19 Apr 2026 18:40:01 +0200 Subject: [PATCH 1/2] updating docs --- README.md | 169 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 169 insertions(+) diff --git a/README.md b/README.md index 675d039..32af038 100644 --- a/README.md +++ b/README.md @@ -1 +1,170 @@ # lila + +**Learn words. Beat friends.** + +lila is a vocabulary trainer built around a Duolingo-style quiz loop: a word appears in one language, you pick the correct translation from four choices. It supports singleplayer and real-time multiplayer, and is designed to work across multiple language pairs without schema changes. + +Live at [lilastudy.com](https://lilastudy.com). + +--- + +## Stack + +| Layer | Technology | +|---|---| +| Monorepo | pnpm workspaces | +| Frontend | React 18, Vite, TypeScript | +| Routing | TanStack Router | +| Server state | TanStack Query | +| Styling | Tailwind CSS | +| Backend | Node.js, Express, TypeScript | +| Database | PostgreSQL + Drizzle ORM | +| Validation | Zod (shared schemas) | +| Auth | Better Auth (Google + GitHub) | +| Realtime | WebSockets (`ws` library) | +| Testing | Vitest, supertest | +| Deployment | Docker Compose, Caddy, Hetzner VPS | +| CI/CD | Forgejo Actions | + +--- + +## Repository Structure + +``` +lila/ +├── apps/ +│ ├── api/ — Express backend +│ └── web/ — React frontend +├── packages/ +│ ├── shared/ — Zod schemas and types shared between frontend and backend +│ └── db/ — Drizzle schema, migrations, models, seeding scripts +├── scripts/ — Python scripts for vocabulary data extraction +└── documentation/ — Project docs +``` + +`packages/shared` is the contract between frontend and backend. All request/response shapes are defined there as Zod schemas and never duplicated. + +--- + +## Architecture + +Requests flow through a strict layered architecture: + +``` +HTTP Request → Router → Controller → Service → Model → Database +``` + +Each layer only talks to the layer directly below it. Controllers handle HTTP only. Services contain business logic only. Models contain database queries only. All database code lives in `packages/db` — the API never imports Drizzle directly for queries. + +--- + +## Data Model + +Words are modelled as language-neutral concepts (`terms`) with per-language `translations`. Adding a new language requires no schema changes — only new rows. CEFR levels (A1–C2) are stored per translation for difficulty filtering. + +Core tables: `terms`, `translations`, `term_glosses`, `decks`, `deck_terms` +Auth tables (managed by Better Auth): `user`, `session`, `account`, `verification` + +Vocabulary data is sourced from WordNet and the Open Multilingual Wordnet (OMW). + +--- + +## API + +``` +POST /api/v1/game/start — start a quiz session (auth required) +POST /api/v1/game/answer — submit an answer (auth required) +GET /api/v1/health — health check (public) +ALL /api/auth/* — Better Auth handlers (public) +``` + +The correct answer is never sent to the frontend — all evaluation happens server-side. + +--- + +## Multiplayer + +Rooms are created via REST, then managed over WebSockets. Messages are typed via a Zod discriminated union. The host starts the game; all players answer simultaneously with a 15-second server-enforced timer. Room state is held in-memory (Valkey deferred). + +--- + +## Infrastructure + +``` +Internet → Caddy (HTTPS) + ├── lilastudy.com → web (nginx, static files) + ├── api.lilastudy.com → api (Express) + └── git.lilastudy.com → Forgejo (git + registry) +``` + +Deployed on a Hetzner VPS (Debian 13, ARM64). Images are built cross-compiled for ARM64 and pushed to the Forgejo container registry. CI/CD runs via Forgejo Actions on push to `main`. Daily database backups are synced to the dev laptop via rsync. + +See `documentation/deployment.md` for the full infrastructure setup. + +--- + +## Local Development + +### Prerequisites + +- Node.js 20+ +- pnpm 9+ +- Docker + Docker Compose + +### Setup + +```bash +# Install dependencies +pnpm install + +# Create your local env file (used by docker compose + the API) +cp .env.example .env + +# Start local services (PostgreSQL, Valkey) +docker compose up -d + +# Build shared packages +pnpm --filter @lila/shared build +pnpm --filter @lila/db build + +# Run migrations and seed data +pnpm --filter @lila/db migrate +pnpm --filter @lila/db seed + +# Start dev servers +pnpm dev +``` + +The API runs on `http://localhost:3000` and the frontend on `http://localhost:5173`. + +--- + +## Testing + +```bash +# All tests +pnpm test + +# API only +pnpm --filter api test + +# Frontend only +pnpm --filter web test +``` + +--- + +## Roadmap + +| Phase | Description | Status | +|---|---|---| +| 0 | Foundation — monorepo, tooling, dev environment | ✅ | +| 1 | Vocabulary data pipeline + REST API | ✅ | +| 2 | Singleplayer quiz UI | ✅ | +| 3 | Auth (Google + GitHub) | ✅ | +| 4 | Multiplayer lobby (WebSockets) | ✅ | +| 5 | Multiplayer game (real-time, server timer) | ✅ | +| 6 | Production deployment + CI/CD | ✅ | +| 7 | Hardening (rate limiting, error boundaries, monitoring, accessibility) | 🔄 | + +See `documentation/roadmap.md` for task-level detail. From d033a08d877132d625e27a987efff106a1ee6cb6 Mon Sep 17 00:00:00 2001 From: lila Date: Sun, 19 Apr 2026 18:48:20 +0200 Subject: [PATCH 2/2] updating docs --- documentation/roadmap.md | 2 +- documentation/spec.md | 42 ++++++++++++++++++++++++---------------- 2 files changed, 26 insertions(+), 18 deletions(-) diff --git a/documentation/roadmap.md b/documentation/roadmap.md index ce35ecd..ffb15c6 100644 --- a/documentation/roadmap.md +++ b/documentation/roadmap.md @@ -18,7 +18,7 @@ Each phase produces a working increment. Nothing is built speculatively. - [x] Configure Drizzle ORM + connection to local PostgreSQL - [x] Write first migration (empty — validates the pipeline works) - [x] `docker-compose.yml` for local dev: `api`, `web`, `postgres`, `valkey` -- [x] `.env.example` files for `apps/api` and `apps/web` +- [x] Root `.env.example` for local dev (`docker-compose.yml` + API) --- diff --git a/documentation/spec.md b/documentation/spec.md index d2d320f..37d8636 100644 --- a/documentation/spec.md +++ b/documentation/spec.md @@ -7,7 +7,7 @@ ## 1. Project Overview -A vocabulary trainer for English–Italian words. The quiz format is Duolingo-style: one word is shown as a prompt, and the user picks the correct translation from four choices (1 correct + 3 distractors of the same part-of-speech). The long-term vision is a multiplayer competitive game, but the MVP is a polished singleplayer experience. +A vocabulary trainer for English–Italian words. The quiz format is Duolingo-style: one word is shown as a prompt, and the user picks the correct translation from four choices (1 correct + 3 distractors of the same part-of-speech). The app supports both singleplayer and real-time multiplayer game modes. **The core learning loop:** Show word → pick answer → see result → next word → final score @@ -29,13 +29,13 @@ The vocabulary data comes from WordNet + the Open Multilingual Wordnet (OMW). A - Multiplayer mode: create a room, share a code, 2–4 players answer simultaneously in real time, live scores, winner screen - 1000+ English–Italian nouns seeded from WordNet -This is the full vision. The MVP deliberately ignores most of it. +This is the full vision. The current implementation already covers most of it; remaining items are captured in the roadmap and the Post-MVP ladder below. --- ## 3. MVP Scope -**Goal:** A working, presentable singleplayer quiz that can be shown to real people. +**Goal:** A working, presentable vocabulary trainer that can be shown to real people (singleplayer and multiplayer), with a production deployment. ### What is IN the MVP @@ -45,16 +45,14 @@ This is the full vision. The MVP deliberately ignores most of it. - Clean, mobile-friendly UI (Tailwind + shadcn/ui) - Global error handler with typed error classes - Unit + integration tests for the API -- Local dev only (no deployment for MVP) +- Authentication via Better Auth (Google + GitHub) +- Multiplayer lobby + game over WebSockets +- Production deployment (Docker Compose + Caddy + Hetzner) and CI/CD (Forgejo Actions) ### What is CUT from the MVP | Feature | Why cut | | ------------------------------- | -------------------------------------- | -| Authentication (Better Auth) | No user accounts needed for a demo | -| Multiplayer (WebSockets, rooms) | Core quiz works without it | -| Valkey / Redis cache | Only needed for multiplayer room state | -| Deployment to Hetzner | Ship to people locally first | | User stats / profiles | Needs auth | These are not deleted from the plan — they are deferred. The architecture is already designed to support them. See Section 11 (Post-MVP Ladder). @@ -81,14 +79,14 @@ The monorepo structure and tooling are already set up. This is the full stack. | Deployment | Docker Compose, Caddy, Hetzner | ✅ | | CI/CD | Forgejo Actions | ✅ | | Realtime | WebSockets (`ws` library) | ✅ | -| Cache | Valkey | ❌ post-MVP | +| Cache | Valkey | ⚠️ optional (used locally; production/state hardening) | --- ## 5. Repository Structure ```text -vocab-trainer/ +lila/ ├── .forgejo/ │ └── workflows/ │ └── deploy.yml — CI/CD pipeline (build, push, deploy) @@ -154,7 +152,6 @@ vocab-trainer/ ├── scripts/ — Python extraction/comparison/merge scripts ├── documentation/ — project docs ├── docker-compose.yml — local dev stack -├── docker-compose.prod.yml — production config reference ├── Caddyfile — reverse proxy routing └── pnpm-workspace.yaml ``` @@ -311,12 +308,20 @@ After completing a task: share the code, ask what to refactor and why. The LLM s All are new tables referencing existing `terms` rows via FK. No existing schema changes required. -### Multiplayer Architecture (deferred) +### Multiplayer Architecture (current + deferred) -- WebSocket protocol: `ws` library, Zod discriminated union for message types -- Room model: human-readable codes (e.g. `WOLF-42`), not matchmaking queue -- Game mechanic: simultaneous answers, 15-second server timer, all players see same question -- Valkey for ephemeral room state, PostgreSQL for durable records +**Implemented now:** + +- WebSocket protocol uses the `ws` library with a Zod discriminated union for message types (defined in `packages/shared`) +- Room model uses human-readable codes (no matchmaking queue) +- Lobby flow (create/join/leave) is real-time over WS, backed by PostgreSQL for durable membership/state +- Multiplayer game flow is real-time: host starts, all players see the same question, answers are collected simultaneously, with a server-enforced 15s timer and live scoring +- WebSocket connections are authenticated (Better Auth session validation on upgrade) + +**Deferred / hardening:** + +- Valkey-backed ephemeral state (room/game/session store) where in-memory state becomes a bottleneck +- Graceful reconnect/resume flows and more robust failure handling (tracked in Phase 7) ### Infrastructure (current) @@ -331,7 +336,7 @@ See `deployment.md` for full infrastructure documentation. --- -## 12. Definition of Done (MVP) +## 12. Definition of Done (Current Baseline) - [x] API returns quiz terms with correct distractors - [x] User can complete a quiz without errors @@ -340,6 +345,9 @@ See `deployment.md` for full infrastructure documentation. - [x] No hardcoded data — everything comes from the database - [x] Global error handler with typed error classes - [x] Unit + integration tests for API +- [x] Auth works end-to-end (Google + GitHub via Better Auth) +- [x] Multiplayer works end-to-end (lobby + real-time game over WebSockets) +- [x] Production deployment is live behind HTTPS (Caddy) with CI/CD deploys via Forgejo Actions ---