Player browser
Steam sign-in, tournament UI, bracket, and private join actions.
- HTTPS requests
- Authenticated SSE updates
System explainer8 minute read
MatchFound separates the internet-facing tournament website from the machine that controls CS2. The website decides what should happen, PostgreSQL remembers it, and a private Windows or Linux runner makes it real.
01 / Big picture
Control messages and game traffic take different routes. That separation is the most important idea in the design.
Steam sign-in, tournament UI, bracket, and private join actions.
Node.js and TypeScript own rules, authorization, scheduling, and APIs.
A least-privileged agent claims allowlisted jobs over outbound HTTPS.
Isolated match profiles use unique game and private RCON ports.
The source of truth for users, tournaments, brackets, jobs, events, and audits.
It can request an allowed action. The control plane validates the session, role, CSRF token, request shape, and current tournament state before recording anything.
Production builds contain no game-host shell scripts, CS2 process controller, log monitor, or RCON client. Only the runner on the game host has those capabilities.
The runner sends a heartbeat and claims a time-limited job over HTTPS. Nothing on the public internet opens an inbound control connection to the game host.
02 / End-to-end
The same path works for a 1v1 or a 5v5. The format changes the entry shape, score target, and server profile—not the safety model.
The organizer chooses 1v1 or 5v5, number of players, start time, score to win, and event rules.
Steam OpenID proves identity. MatchFound stores a revocable server-side session; the browser receives only an opaque cookie.
The host assigns familiar CS ranks. Rank-aware seeding creates balanced opening pairs while database constraints prevent duplicate entries.
A ready match reserves one compatible instance and creates an
idempotent start_match job in the same transaction.
An eligible runner claims a lease, verifies the exact profile and port, starts or reuses the intended process, applies the score target, and reports the result.
Only the two competitors—or two teams—and the organizer can see the IP,
password, and one-click steam://connect link.
The runner turns CS2 output into normalized, sequenced events. Replays and forged events are rejected before they can change the bracket.
The result, winner, bracket advancement, and cleanup job commit atomically. The runner removes players, rotates the password, and closes the dynamic instance.
03 / Concurrency
MatchFound starts and stops configured instances as brackets become ready. It does not promise infinite servers: the runner advertises real capacity, and excess matches wait safely in the queue.
Each profile owns a unique game port, RCON port, log path, and process identity.
The scheduler chooses only profiles compatible with the match format and required map.
When capacity is full, a ready match stays visible and queued. Cleanup releases the instance, then the scheduler assigns the next compatible match without a port collision.
04 / Trust boundaries
Each component receives the minimum authority it needs. A compromise at one boundary should not automatically unlock the next one.
| Sensitive value | Browser | Control plane | PostgreSQL | Runner |
|---|---|---|---|---|
| Steam Web API key | Never | Secret manager | Never | Never |
| Session token | Opaque cookie | Hash only | Hash only | Never |
| Runner credential | Never | Hash only | Hash only | Own host only |
| RCON password | Never | Rejected | Never | Local only |
| Match password | Own match only | Authorized response | Protected state | Configures CS2 |
Steam identity, server-side sessions, CSRF defense, strict request schemas, and a centralized role matrix protect every mutation.
Transactions, unique constraints, event sequence numbers, idempotency keys, and optimistic versions prevent double starts and double winners.
HTTPS serves the control plane. Only allocated CS2 game ports are public; PostgreSQL and RCON stay private.
Audit events, structured redacted logs, metrics, and traces connect a user action to its tournament, match, job, host, and result.
05 / Failure handling
The database preserves desired state. After a restart, each side compares that desired state with actual CS2 processes instead of blindly repeating the last command.
PostgreSQL retains matches, leases, jobs, events, sessions, and bracket state.
It reports inventory, replays unacknowledged results, and resumes valid leases.
The runner reports an actionable failure; bounded retries or organizer recovery follow.
The same idempotency key or event sequence returns the stored result instead of acting twice.
The rule: never infer truth from a process still running. Read durable state, inspect the real instance, and converge the two.
06 / Today versus target
The app on this PC keeps a development path for LAN testing. Production deliberately removes those shortcuts from the public process.
http://localhost:431007 / Plain-language glossary
The website and API that decide tournament state. It does not directly control RCON in production.
A small agent on a supported Windows or Linux CS2 host that performs a narrow allowlist of privileged game-server actions.
A time-limited claim on a job. If the runner disappears, the lease expires and recovery can proceed safely.
The same request can be retried without starting a second server or advancing the same winner twice.
Comparing recorded desired state with the real processes and ports, then safely making them agree.
Server-Sent Events: a reconnectable stream that pushes bracket and server-status changes to an authorized browser.
Keep this sentence