Security model
Why we don't run Lean on the web server.
ProofGarden Lean Judge follows a one-line rule: the web server never executes user-supplied Lean. All real type-checking happens inside GitHub Actions on GitHub-hosted runners, in a clean container, with a pinned lean-toolchain.
Defence in depth
- Static filter.A first-pass scan rejects bodies that contain tokens that don't belong in a tactic block (
sorry,admit,axiom,def,theorem,#eval,native_decide, …). - Server-generated file. The user only submits the proof body. The Lean file (imports, namespace, theorem header,
#print axiomsline, terminator) is rendered server-side from challenge metadata. - Branch isolation. The generated file lands on a fresh
submission/<id>branch. The judge repository'smainis protected; the workflow filters on a specific path glob, so user-pushed branches can't bypass it. - Pinned toolchain. The judge repo carries the only allowed
lean-toolchainandlakefile.lean. Users cannot edit them. - Commit-SHA verification. Both the webhook and the polling refresh refuse to accept a run whose
head_shadiffers from the SHA we recorded when we pushed the file. - Append-only ledger. Every Seed change writes a
LedgerEntryand increments the balance in a single Prisma transaction. Awards for a given submission are idempotent.
Full threat model: docs/SECURITY.md.