refserver#
Tournament engine and public API for SpinShare tournament tooling.
refserver is the single source of truth for match state, event data, player/chart caches, and external service integrations (start.gg, SpinShare, spindata). refbot and refapp are thin clients that call this service.
Status#
Phases 1–8 complete. refserver owns all match state, the MongoDB models,
the start.gg / SpinShare / spindata integrations, and the WebSocket event
stream. The full HTTP/WS API surface is implemented (events, matches + lifecycle,
charts, players, pools, guild config, API-key management, auth, rate limiting)
and documented in docs/openapi.yaml. 279 headless node:test tests pass
(npm test). See docs/plan.md for the roadmap.
Stack#
- Node.js 24+ ES modules
- TypeScript 5.6+ (strict)
- Express 5
wsWebSocket server- Mongoose 9 / MongoDB
pinostructured JSON logginghelmet,cors,express-rate-limit
Getting started#
cp .env.example .env # fill in MONGODB_TEST_URI for local dev
npm install
npm run dev # tsx watch, reads .env
# or
npm run build && npm start
GET /v1/health returns 503 until MongoDB is connected; 200 once it is.
The WebSocket server listens on WS_PORT (default 3002) and sends a
connection.ack on connect and pong in reply to {type:"ping"}.
Configuration#
Copy .env.example to .env. Key variables:
PORT/WS_PORT— HTTP and WebSocket listen ports (default 3000 / 3002).MONGODB_URI/MONGODB_TEST_URI— production and test connection strings.APP_ORIGIN— CORS allow-list (comma-separated); empty = permissive in dev.TRUST_PROXY— enable behind a reverse proxy (Traefik) for correct client IPs.DISCORD_CLIENT_ID/DISCORD_CLIENT_SECRET/DISCORD_REDIRECT_URI/DISCORD_ALLOWED_USERS— Discord OAuth (Bearer) auth for community tooling. Allow-listed users getadminscope; everyone else getsread.STARTGG,SPINSHARE_API_URL,SPINDATA_URL,SPINDATA_SUBSCRIBE_URL,SPINDATA_RELAY_URL— external service credentials/endpoints.
Rate limiting#
express-rate-limit applies a per-IP limit to browser/OAuth clients:
RATE_LIMIT_ENABLED(defaulttrue)RATE_LIMIT_MAX(default600per window) — headroom for the refapp SPA, which polls every ~5s; multiple open views push legit traffic well past the old100default.RATE_LIMIT_WINDOW_MS(default900000= 15 min)
Trusted service-to-service calls (Authorization: ApiKey …) and GET /v1/health are exempted so refbot/refapp are never throttled. Final
RATE_LIMIT_MAX should be tuned against real production traffic.
dist/ is gitignored and built during deployment (npm run build).
License#
ISC