# Courtside A self-hosted tournament desk for small volleyball tournaments (HOA days, club nights, beach 2s). One QR code carries a team from check-in, through registration, to live scores and "you're up on Court 2." - **One QR code, four phases.** The code printed on the check-in table points at `/t/`. While registration is open it is the sign-up form. After the organizer closes registration it shows the roster, then the live board, then the final results. Nobody re-scans anything. - **Two courts (or one, or four).** A court engine assigns the next match to whichever court frees up, keeps an "up next" queue with ETAs, and handles pauses, court swaps, and teams that leave mid-tournament. - **Formats.** Round-robin pools, single elimination, double elimination (with bracket reset), pools-then-bracket, 2s/3s/4s/6s, rally scoring with configurable points, win-by, cap, and best-of. - **Live everywhere.** Scores entered on the organizer's phone appear on every open page within a second over a WebSocket. Team pages get an on-screen alert (and a browser notification if allowed) when the team is on deck or up. - **Nothing to install for players.** Plain web pages that work on any phone. Organizers get a mobile-friendly desk at `/admin`. - **Small footprint.** One Node process, SQLite via Node's built-in `node:sqlite`, two npm dependencies (`ws`, `qrcode`), no build step. Runs comfortably on the smallest container you have. Status: MVP. It has run a full simulated day and an HTTP smoke test but has not yet run a real tournament. See [docs/ROADMAP.md](docs/ROADMAP.md) for what's next and [docs/KNOWN-GAPS.md](docs/KNOWN-GAPS.md) for what to watch. ## Quick start (local) Requires Node 22.13 or newer (for `node:sqlite`). ```bash git clone https://gitea.cloudfreeiot.com/bkvargyas/courtside.git cd courtside npm ci ADMIN_PASSWORD=letmein npm start # open http://localhost:3000 — organizer desk at /admin ``` To see it with data, seed the demo tournament first (server stopped): ```bash npm run demo ADMIN_PASSWORD=letmein npm start # open http://localhost:3000/t/demo-labor-day-2s ``` ## Deploy with Docker behind a Cloudflare Tunnel ```bash cp .env.example .env # set ADMIN_PASSWORD, SESSION_SECRET, CLOUDFLARE_TUNNEL_TOKEN docker compose up -d --build ``` The app container publishes no host ports; the only way in is the tunnel, whose public hostname should route to `http://app:3000`. Full instructions, including the Cloudflare side, backups, and updating, are in [docs/DEPLOY.md](docs/DEPLOY.md). For a local run without Cloudflare use `docker compose -f docker-compose.local.yml up --build`. ## How a tournament day runs 1. **Create** the tournament in the desk: name, date, courts, team size, scoring rules, stages. Print the QR code from the desk. 2. **Check-in.** Teams scan and register themselves: team name, captain, mobile, player count. Each team gets a private link (`/t//team/`) that will show their schedule and alerts. The organizer can edit, add, remove, and reseed teams. 3. **Close registration**, generate pools (or a bracket), and **go live**. The engine puts the first matches on the courts and texts, well, shows, the next teams that they're on deck. 4. **Score** from the desk: a +/− pad per court shows the running score on the public board; "Mark final" records the result and the engine assigns the next match. Any score can be corrected later from the "All matches" list. 5. **Pool play ends**, generate the bracket from pool standings (top N per pool plus wildcards), keep scoring. 6. **Final.** The QR page becomes the results page and stays up. The organizer guide in [docs/ORGANIZER-GUIDE.md](docs/ORGANIZER-GUIDE.md) covers withdrawals, pausing a court, moving a match, pushing a match back, broadcasts, and the TV display mode. ## Repository layout ``` engine/ Tournament logic with no I/O: formats, state, standings, court engine. Tested. server/ HTTP + WebSocket server, SQLite store, page templates, static assets. scripts/ seed-demo.js — create a demo tournament. test/ node:test unit tests for the engine; smoke.sh end-to-end HTTP test. docs/ Deploy guide, architecture, organizer guide, API, roadmap, known gaps. simulate.js Plays out a full 12-team day on 2 courts and checks invariants. Dockerfile, docker-compose.yml, docker-compose.local.yml, .env.example ``` ## Development ```bash npm test # engine unit tests npm run sim # simulated day; try --seed=3 --bracket=double npm run dev # server with auto-restart ADMIN_PASSWORD=test PORT=3123 npm start & BASE=http://localhost:3123 ADMIN_PASSWORD=test npm run smoke ``` Architecture and the reasoning behind it: [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md). Endpoints: [docs/API.md](docs/API.md). ## License MIT. See [LICENSE](LICENSE).