The rule
GET /api/judge/scores as judge_a returns 200. The same URL with ?judge=judge_a as judge_b must return 401/403. As participant, 401/403. No exceptions, no frontend hiding — the check lives in the FastAPI dependency.
Judges requesting anything but their own id get a 403; organizers must pass an explicit judge_id; everyone else is denied. Verified live across seven role/endpoint combinations in ARCHITECTURE.md.
if current_user.role == UserRole.JUDGE:
if judge_id and judge_id != current_user.id:
raise HTTPException(403, "Cannot view other judge's scores")Defense in depth
Self-review is blocked at POST /api/judge/scores/{id} (403 for team members), declared conflicts block scoring per project, and every scoring mutation appends an audit_events row.
Playwright judge-isolation spec plus run.py prove it on every boot. Residual risk: none known — the one cell of the threat model we are fully green on.
Run it yourself
Every post maps to a real route or file in this repo. Start with the gallery, then the docs.
Keep reading
View allOct 3, 2026 · Engineering
Run the whole platform with one command: setup guide (main branch)
Clone main, docker compose up, seed fixtures, test accounts, and the 7/7 acceptance checker — everything to go from zero to judging in minutes.
Sep 28, 2026 · Engineering
Fair ranking is not an average: z-scores, Bradley-Terry and Borda
Raw averages reward lucky judge draws. How per-judge calibration plus two extra ranking views produce a ROBUST/FRAGILE verdict organizers can defend.
Oct 3, 2026 · Company
Build the platform that will judge you: how we built DOGFOOD 2026
FastAPI + Next.js + Postgres with backend-enforced judge isolation, z-score calibration and one-command Docker — our Write-Up Quest build log.