Open competition infrastructure for any sport whose contests are a bracket, a group, or a tie.
CourtHive builds the rules engine, the data standard and the applications to run tournaments and leagues: draws of
any structure, entries, seeding, scheduling, scoring, results, rankings and publishing. It began in tennis, and the
engine still runs the USTA and ITA
Tournament Desk platforms, as it has since 2021. But nothing in it is tennis-shaped: draw structures and
matchUpFormat scoring codes describe pickleball, squash, badminton and team competition just as well.
→ courthive.com — what the platform does, and how it works.
Every draw, match, schedule and result is stored as a CODES record: CourtHive's canonical competition data standard, a superset of the ITF's Tennis Open Data Standards (TODS), which CourtHive's early work helped shape. CODES adds the first-class fields a real platform needs (tiers, organisation identity, scheduling, sanctioning, competition policies), and the engine writes it natively. It is the storage format itself, not an export.
That is what lets one record flow, unchanged, from ingest to mutation to storage to publication. A tournament is one document, not forty tables. TODS comes in; CODES comes out.
Competition Factory is the shared brain: pure, dependency-free
business logic for generating and managing competitions of arbitrary complexity. It runs unchanged in the browser, on
the server and in the ingest pipeline. Published on npm as
tods-competition-factory, documented at
courthive.github.io/competition-factory.
flowchart LR
subgraph Clients
TMX["TMX<br/>tournament management"]
EPX["epixodic<br/>point-by-point tracker"]
PUB["courthive-public<br/>published results"]
ARENA["courthive-arena<br/>venue scoreboards"]
end
subgraph Engine["Competition Factory (CODES)"]
F[rules engine]
end
TMX -- mutations --> CFS["competition-factory-server<br/>one mutation path"]
CFS --> F
TMX -. same engine, offline .-> F
EPX -. same engine, scoring .-> F
CFS --> DB[(CODES records)]
CFS -- projection outbox --> READ["courthive-query<br/>read side (CQRS)"]
INGEST["ingest-core<br/>TODS and other sources"] --> F
EPX -- frames --> RELAY["score relay<br/>built by epixodic"]
RELAY -- authoritative result --> CFS
RELAY --> ARENA
DB --> PUB
HIVE["HiveID<br/>identity provider"] -. verifies tokens .- CFS
PERSONS["courthive-persons<br/>one personId"] --- CFS
DB --> RANK["courthive-rankings"]
AMS["courthive-ams<br/>associations"] --- CFS
- One mutation path. A change travels request → run the engine → persist → respond. Because the engine is pure and portable, a client can be a thin server-backed page or a fully offline-capable app like TMX, which runs the same engine locally.
- Scoring off the core. Point-by-point scoring, captured in epixodic or any tracker, flows through the score relay, which fans out to scoreboards and viewers and writes the authoritative result back through the one mutation path.
- Reads, unlocked. A dedicated read side (courthive-query) answers the questions a document model was never shaped for, such as "everything I'm entered in", person, team and venue lookups, and participation history, as ordinary SQL joins kept current from a projection outbox.
- Identity, resolved once. Every person resolves to one
personId(courthive-persons), and HiveID is the single identity provider: the only service that signs tokens, which every other service verifies.
The full picture: courthive.com/#/architecture.
| Repository | What it is |
|---|---|
| competition-factory | The CODES rules engine (npm: tods-competition-factory) |
| TMX | Tournament management client: an offline-capable PWA |
| competition-factory-server | NestJS server: the mutation path, storage, real-time updates |
| courthive-components | UI components shared across CourtHive applications |
| courthive-public | Public viewer for published tournaments |
| courthive-arena | In-venue scoreboard and broadcast overlay display |
| ingest-core | Source-agnostic toolkit for bringing tournament data into CODES |
| provider-config | Canonical provider configuration types and validators |
| epixodic | Point-by-point match tracker with live visualizations; also builds the score relay (demo) |
| pdf-factory | Draw sheets, schedules, player lists and court cards as PDFs |
| scoringVisualizations | D3 visualizations of point-by-point match data |
The rest of the platform runs as services around the same engine and the same records; they are described on courthive.com and will open up over time.
| Service | Role |
|---|---|
| HiveID | Identity provider: the single token signer, verified everywhere else |
| courthive-persons | Canonical person identity: one personId across tournaments, federations and providers |
| courthive-facilities | Canonical facility registry: one facilityId for a physical place |
| courthive-declarations | What a person declares for themselves: availability and preferences |
| courthive-query | The read side: flattened SQL projections and cross-tournament queries |
| courthive-rankings | Ranking pipeline: point awards from CODES records, sliding-window lists |
| courthive-ams | Association management: membership, rosters, officials, sanctioning |
CourtHive started when a parent's interest in player development ran into poor-quality data and a fragmented market with little integration between providers. A focus on data visualization became a focus on good data, then a document-based tournament platform, then an open standard. The bet is still the same: when the record is right and open, everything built on it gets better.





