# CLAUDE.md Guidance for working in this repo. Read `docs/PLAN.md` for the full design. ## What this is Self-hosted real-time SSBU scoreboard PWA. pnpm monorepo: - `packages/shared` — `@sbt/shared`. zod schema + types for `ScoreboardState` and the WebSocket protocol. **Single source of truth imported by both web and server** — change state shape here and both sides follow. No build step (consumed as TS source). - `apps/server` — `@sbt/server`. Fastify + raw `ws`, Drizzle + Postgres. Runs via `tsx` (no compile step in dev or prod). - `apps/web` — `@sbt/web`. React + Vite + Tailwind PWA. ## Real-time architecture (don't break this) `apps/server/src/rooms.ts` is the authority: in-memory state per board, broadcast to all sockets on `update`, debounced Postgres save. `apps/server/src/ws.ts` resolves role + board from the connect query (`overlay=`/`control=`/`board=`). The web side has exactly one sync primitive — `apps/web/src/hooks/useScoreboardSync.ts` — reused by Editor, Control, Overlay. When adding scorebug features: extend `ScoreboardStateSchema` in `packages/shared/src/state.ts`, then render in `apps/web/src/components/ScoreBug.tsx` and add controls in `PlayerPanel`/ `MatchControls`. Sync, persistence, and overlay come for free. ## Commands ```bash pnpm install pnpm dev # web :5173 + server :3000 (Vite proxies /api and /ws) pnpm test # vitest (shared) pnpm typecheck docker compose up -d --build # production-style: Postgres + server ``` Server env: `DATABASE_URL`, `JWT_SECRET`, `COOKIE_SECURE`, `UPLOAD_DIR`, `WEB_DIR`, `PORT` (see `apps/server/src/env.ts`). ## Conventions - Keep modules small and single-purpose; match the existing terse style. - Validate all external input with zod (REST bodies and WS messages already do). - Owner REST routes go through `requireAuth`; public flows are token-only via WebSocket. - The overlay route must stay uncached (see `navigateFallbackDenylist` in `vite.config.ts`).