- CHANGELOG.md following Keep a Changelog, covering 0.1.0 and 0.2.0 - server/version.js reads the version from package.json; /healthz returns it and the footer renders it, so a deployment always states what it is serving - bump package.json 0.1.0 -> 0.2.0
4.9 KiB
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/<slug>. 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 for what's next and docs/KNOWN-GAPS.md for what to watch.
Quick start (local)
Requires Node 22.13 or newer (for node:sqlite).
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):
npm run demo
ADMIN_PASSWORD=letmein npm start
# open http://localhost:3000/t/demo-labor-day-2s
Deploy with Docker behind a Cloudflare Tunnel
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. For a local run without Cloudflare use docker compose -f docker-compose.local.yml up --build.
How a tournament day runs
- Create the tournament in the desk: name, date, courts, team size, scoring rules, stages. Print the QR code from the desk.
- Check-in. Teams scan and register themselves: team name, captain, mobile, player count. Each team gets a private link (
/t/<slug>/team/<code>) that will show their schedule and alerts. The organizer can edit, add, remove, and reseed teams. - 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.
- 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.
- Pool play ends, generate the bracket from pool standings (top N per pool plus wildcards), keep scoring.
- Final. The QR page becomes the results page and stays up.
The organizer guide in 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
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. Endpoints: docs/API.md.
Releases
Versions and what changed in each: CHANGELOG.md. The running
version is in the page footer and at GET /healthz.
License
MIT. See LICENSE.