You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This is the canonical route reference for the project (the
architecture overview
links here rather than duplicating it, and the root README.md
points operators here). 76 routes total: 73/api/* routes dispatched from
the api handler in backend/src/handler.mjs (route bodies decomposed into
backend/src/routes/*.mjs factories), plus the 3/eval-ui/* pages served by
the separate proctor-eval entrypoint backend/src/eval-server.mjs (listed under
Evaluation below). Auth is timing-safe (safeEqual) and closed-by-default when
the secret is unset:
admin = x-admin-password vs ADMIN_PASSWORD
invig = x-invigilator-password vs the contest's invigilator_key OR
INVIGILATOR_PASSWORD (admin password also accepted)
api-key = x-api-key vs ALERTS_INGEST_API_KEY
sweep = x-api-key vs RETENTION_SWEEP_API_KEY (or admin)
session = knowing the session_id (no header) — the candidate write bearer
Any unmatched path → 404. Intentional 4xx echo a detail message; unexpected
errors return a generic 500 with no internal detail. CORS allows
GET,POST,OPTIONS (PUBLIC_APP_ORIGIN, default *).
Candidate / public
Method
Path
Auth
Purpose
GET
/api/exam-config
none
Public sanitized exam config for a contest/slug.
POST
/api/access-code
none
Resolve a typed 6-char access code → contest slug.
POST
/api/roster/lookup
none (rate-limited)
Verify a candidate's roster unique ID (person-mode may return a college picker).
POST
/api/session/start
time-window gate
Register/start a session, or idempotently replay an owned session_id. Serves problems[] + submissions_summary + submit_budget.
POST
/api/session/resume
session
Return an existing session verbatim after a reload (no re-collection).
POST
/api/upload-url
session (writable)
Mint a v4 signed write URL for a screen or camera chunk.
POST
/api/events
session (writable)
Append a JSONL event batch; raise sure-shot alerts for high-signal types.
Counts by status (live/locked/pending/finished/disconnected) + rooms.
GET
/api/admin/ip-report · attendance
IP clustering drill-down / roster taken–not-taken.
POST
/api/admin/health-check
Pre-test pre-flight canary: stands up an ephemeral namespaced contest+session and probes signing / chunk upload / recordings read / telemetry write / bundle hash-gate / Judge0 reachability with real auth, then tears it down (light skips metered Judge0; full adds 2 submissions).
POST
/api/admin/contest-exam-time
Live end-time control for the scoped contest (absolute / extend / force-end-now).
POST
/api/alerts
Ingest one alert or a batch ({alerts:[…]}, idempotent on alert.id).
GET
/api/admin/alerts
List alerts newest-first with filters + download_url from video_key.
The recording player path (used by recording review and alert deep-links)
resolves in both legacy and person-keyed modes. The distributed reviewer
queue (review-roster/review-next/review-verdict) is candidate-norm-keyed;
full person-mode queue serving is a roadmap item.
Evaluation (proctor-eval /eval-ui + routes)
Method
Path
Purpose
POST
/api/admin/contest-evaluate
Run the integrity + talent evaluation over a contest's sessions (batched).
The embedded Evaluation tab page + its browser app + the pure recommendation module.
Invigilator (backend/src/routes/invigilator.mjs)
Method
Path
Purpose
GET
/api/invigilator/overview
Which rooms exist, gate on/off.
GET
/api/invigilator/room
Room stats + session rows + shared alerts.
POST
/api/invigilator/release-code · open-room
Release the 6-digit room start code / open the whole room.
POST
/api/invigilator/exempt
Per-student enforcement exemption toggle.
POST
/api/invigilator/unlock-code · unlock
Mint a fullscreen unlock code / unlock a specific session.
Shared alert contract
Every producer and the backend agree on this shape (required on ingest: source,
type, severity, timestamp, hackerrank_username, title — the wire field
name hackerrank_username is frozen for back-compat; the candidate-facing
label is "Candidate ID"). The fuller field-by-field reference lives in
alert-taxonomy.md.
For the full alert catalog — the proctor alert types, default severities,
the per-type Share-with-invigilator flag, and the enforcement-violation lock ladder
— see alert-taxonomy.md.
{ "id": "<source>:<type>:<username_norm>:<contest_slug>:<dedupe>", // stable + idempotent "source": "proctor", // the only accepted source (the contest-eval poller was removed) "type": "<see alert taxonomy below>", "severity": "critical | warning | info", "timestamp": "<ISO 8601>", "contest_slug": "<optional>", "hackerrank_username": "<required (frozen wire name)>", "username_norm": "<lowercase/sanitized>", "person_id": "<optional; person-mode>", "session_id": "<optional>", "room": "<optional>", "title": "<headline>", "detail": "<optional explanation>", "data": { /* optional structured payload */ }, "video_key": "<optional GCS key; resolved to download_url on READ, never stored>", "verdict": { "status": "pending | real | false_positive | inconclusive" } }