The Problem
Organizers need a web‑based tournament manager that can handle single‑elimination, round‑robin and Swiss formats, expose public dashboards, and let admins drag‑and‑drop matches. Existing SaaS tools are either locked‑in or lack the required scheduling flexibility, forcing teams to build ad‑hoc scripts.
What This Does
bracket delivers a full‑stack, self‑hosted system. The backend lives under backend/ and is an async FastAPI app (backend/bracket/app.py). It defines REST endpoints, authentication (backend/bracket/routes/auth.py), and a rich data model in backend/bracket/models/db/. The frontend under frontend/ is a Vite‑powered React app using Mantine components; the main entry point is frontend/src/main.tsx, which pulls the generated OpenAPI client from frontend/src/openapi/client/.
Key files:
backend/bracket/app.py– creates the FastAPIappand registers routers.backend/cli.py– CLI with commands likecreate-dev-db(callscreate_dev_db).backend/bracket/sql/*.py– thin SQL wrappers (e.g.,sql_get_tournament,sql_create_match).frontend/src/openapi/client/index.ts– typed client used by UI components.
How It Is Wired
Execution starts at backend/bracket/app.py:44 (lifespan event) which runs init_db_when_empty → create_admin_user → hash_password. The only external effect is a bcrypt hash (backend/bracket/utils/security.py).
The HTTP request flow:
- FastAPI receives a request (
/tournaments/{id}) → router inbackend/bracket/routes/tournaments.py. - Handler calls
sql_get_tournament(inbackend/bracket/sql/tournaments.py). sql_get_tournamentusesfetch_one_parsedfrombackend/bracket/utils/db.py→ executes a DB query viabackend/bracket/database.py.
The most widely used internal functions (by distinct callers) are:
send_tournament_request– 48 call sites.inserted_team– 36 call sites.SuccessResponse– 32 call sites.
These act as blast‑radius hubs; changes here ripple across many modules.
The frontend imports the OpenAPI client (frontend/src/openapi/client/index.ts) which ultimately calls fetch wrappers defined in frontend/src/services/adapter.tsx. That file is a hub (14 dependent modules) and therefore a stability target.
No circular import cycles were detected (0). The internal call graph contains 967 resolved edges, confirming that most business logic lives in the SQL wrapper layer and the scheduling builder (backend/bracket/logic/scheduling/builder.py).
How To Use It
# Clone the fork (use the exact URL required)
git clone https://github.com/moses-y/bracket.git
cd bracket
# Build and start all services (Docker Compose uses the Dockerfiles)
docker compose up -d
The compose file pulls a Postgres container and builds the backend (backend/Dockerfile) and frontend (Dockerfile).
Create the initial dev database:
docker exec bracket-backend uv run --no-dev ./cli.py create-dev-db
The backend reads configuration from .env files (backend/.env or backend/dev.env). The repository currently commits frontend/.env.development; this should be removed and replaced with an example file.
Run the UI at <http://localhost:3000>. The demo credentials are listed in the README.
Real‑World Use
A sports club can deploy the stack on an on‑premise server, point the FRONTEND_URL and DATABASE_URL environment variables to their internal network, and embed the public dashboard (/tournaments/{slug}) in their website via an iframe. Admins log in, create a tournament, and the scheduling service (backend/bracket/logic/scheduling/*.py) automatically generates Swiss rounds, which the UI updates in real time via the OpenAPI client.
Code Health & Issues
- Critical –
pyjwt@2.12.0has CVE‑2026‑48526. Upgrade to a fixed version. - High – Pin GitHub Actions to commit SHAs (
.github/workflows/*.yml). - High – Multiple dependencies with published advisories (e.g.,
aiohttp@3.13.4,python-multipart@0.0.26,starlette@1.0.0). Upgrade them. - High – Committed development
.env(frontend/.env.development). Remove,.gitignore, rotate secrets, add an example file. - Medium – Base images are mutable (
node:25-alpine,python:3.14-alpine3.22). Pin by digest. - Medium – No dependency‑vulnerability gate in CI. Add
dependency-review-actionorosv-scanner. - Medium – No pre‑commit secret scan. Add a hook (e.g.,
detect-secrets). - Medium – Checkout step keeps token; set
persist-credentials: falseindocs-build.yml. - Low – Jobs lack
timeout-minutes; add reasonable limits. - Low – Missing repo convention files (
.editorconfig, formatter config). Add them.
Additional measured findings:
- Oversized generated files (
frontend/src/openapi/types.gen.ts,sdk.gen.ts) exceed 2 k LOC – split by responsibility. - Hub module
frontend/src/services/adapter.tsxis imported by 14 modules – keep stable. - Repeated 6‑line blocks across 62 files – extract shared helpers.
- Deep nesting (max depth 8) in several backend utilities – refactor with early returns.
The Bottom Line
bracket provides a complete, async Python + React tournament platform that can be self‑hosted with a single docker compose up. The architecture is clear, but several high‑severity security gaps (pinned vulnerable dependencies, leaked env file) must be fixed before production use. Suitable for teams comfortable with Docker and FastAPI who need a customizable tournament engine.