Ottawa Defence Hackathon · AVSS Cryptographic Provenance Challenge
"Buy Canadian" procurement turns on whether a product is Product of Canada or Made in Canada — but those claims rest on unverifiable supplier self-reporting. This project makes provenance cryptographic: every supplier contribution is a signed attestation, attestations hash-link across tiers into a tamper-evident chain, and that chain is independently verified to compute the Canadian-content designation while detecting forgery, tampering, replay, and statistical anomalies.
The backend POST /verify is the automatically-scored deliverable. It scores
98.3% on the 1,000-chain training corpus, with 100% on every deterministic
attack family and zero false positives on clean chains. Everything else (the
two UIs, the design system, infra) supports the demo and judging.
Container architecture: the stateless /verify backend, the two UIs, and how a
request flows. Full diagram set — verification pipeline, anomaly taxonomy, crypto
contract — in docs/system-diagrams.md.
Grade the scored backend (exactly what the harness does — no profiles, no .env):
cd backend && docker compose up --build # serves POST /verify on :8000Run the full demo stack (both UIs + backend, from the repo root):
cp .env.example .env # optional: add OPENAI_API_KEY for the AI panel
docker compose --profile dev up --build # hot-reload dev → web :5173, backend :8000
docker compose --profile prod up --build # built demo → web :8080, backend :8000Then open the web app and try the Purchaser page — six sample products walk every verdict the verifier produces (see Demo guide).
| # | Deliverable | Where | Scored? |
|---|---|---|---|
| 1 | Verification backend — POST /verify over the signed attestation DAG |
backend/ |
✅ auto-graded |
| 2 | Supplier UI (issue a signed attestation) + Purchaser UI (scan/look up a product, see its provenance) | web/ |
judged in demo |
| 3 | Judge presentation — approach, architecture, attack detection, live UI walkthrough | docs/ |
judged |
Given a product's attestation chain, the backend:
- Builds the DAG from each attestation's
parents(order-independent). - Computes Canadian content — cost (
material_cad + labour_cost_cad) summed by each step'sperformed_in_country, as a % of total. - Finds the last substantial transformation — the qualifying
component_manufacture/subassembly/final_integration(≥ 4 labour hours) closest to the product leaf. - Decides the designation — last transformation in CA and ≥ 98 → Product of Canada; ≥ 51 → Made in Canada; else none.
- Runs two tiers of anomaly detection and returns the offending
attestation(s):
- Hard rules (deterministic): invalid / unknown-supplier signatures, parent-hash & unit mismatch, dangling parents, cycles, timestamp inversion, global mass-balance over-consumption, implausible transformations, extreme labour rates, duplicate ids, anchor-registry rewrite / cross-product reuse, plus structural-plausibility invariants that catch unnamed attack classes at zero cost to clean precision.
- Statistical (
t4_*): legal on every rule but anomalous versus how genuine chains look — non-canonical timestamps, labour/cost above the genuine ceiling, and foreign suppliers falsely claiming Canadian origin. Thresholds carry a safety margin above the genuine maximum because detection is F1-scored — precision is protected over recall.
Threat model: the kit ships every supplier private key, so an adversary can produce a correctly-signed malicious chain. A valid signature proves a message wasn't garbled — not that the claim is true. Our value is the forensic detection layer that reasons about whether a chain is internally consistent and economically plausible, not just whether it's signed.
Full write-ups: docs/backend-explained.md,
docs/system-diagrams.md,
docs/backend-technical-presentation.md.
| Path | What it is |
|---|---|
backend/ |
Scored component. Python 3.13 + FastAPI POST /verify (Gunicorn + Uvicorn workers). Stateless DAG verification, designation, two-tier detection, opt-in AI audit. Self-contained docker-compose.yml is the grader entry point. See backend/README.md. |
web/ |
Vite + React frontend — Supplier (issue) and Purchaser (verify) UIs. Talks to the live backend; six published demo chains under web/public/chains/. |
provenance-hackathon/ |
Official starter kit — specs, byte-exact reference_lib/, key/anchor registries, training_corpus.jsonl, self_test.py, worked example. |
design-system/ |
Design tokens, components, fonts, icons, motion. |
caddy/ |
TLS sidecar config (optional tls profile — HTTPS for phone-camera QR scans). |
docs/ |
Architecture & verification diagrams, backend teaching guide, presentation guide. |
docker-compose.yml |
Full-stack orchestration with dev / prod / tls profiles. |
.env.example |
Single annotated environment template — copy to .env. |
| Service | Port | URL | Profile |
|---|---|---|---|
| web (prod, nginx) | 8080 |
http://localhost:8080 | prod |
| web (dev, Vite HMR) | 5173 |
http://localhost:5173 | dev |
| backend (FastAPI) | 8000 |
http://localhost:8000 | dev / prod |
| Caddy TLS sidecar | 443 |
https://<LAN-IP>/ | tls |
The web app proxies /api → backend:8000 (nginx in prod, Vite in dev), so the
browser only ever talks same-origin.
# 1. Grade /verify against the official labels (backend running on :8000):
python3 provenance-hackathon/self_test.py http://localhost:8000/verify
# → overall: 98.3% (1000 cases)
# 2. Backend unit / API / corpus-guard suite:
cd backend && pip install -e ".[dev]" && pytest # 50 passed
# 3. Byte-exact crypto core (canonical serialization + Ed25519 golden vectors):
cd provenance-hackathon && pip install -r reference_lib/requirements.txt
python3 -m reference_lib.tests.test_golden # 5/5
# 4. Web suite:
cd web && npm install && npm test # 114 passedThe Purchaser page ships six published chains (web/public/chains/), each a real
signed chain verified live against /verify — covering every verdict:
| Product | Verdict | Demonstrates |
|---|---|---|
| Recovery-Capable ISR Drone | ✅ Made in Canada · 58.4% | the happy path (worked example) |
| Foam-lined Equipment Case | ✅ Product of Canada · 100% | the top designation |
| Body-Armor Plate Carrier Kit | ✅ Not Canadian · 0% | an honest foreign product |
| Man-Portable Solar Kit | ❌ Failed | tamper → mass-balance violation |
| Machined Mounting Bracket | ❌ Failed | forged signature (still claims 92% CA) |
| Gas-Turbine APU / Genset | ❌ Failed | subtle statistical origin anomaly |
The mounting-bracket + APU pair is the highlight: one looks 92% Canadian but its signature is forged; the other breaks no rule yet is statistically anomalous — showing both detection tiers.
All config lives in one root .env (copy from .env.example).
-
Public, browser-inlined: only
VITE_*values. Never give a provider secret aVITE_prefix. -
Backend-only secret — optional AI audit: the backend exposes an opt-in
POST /audit/root-causethat uses an LLM to group detected anomalies into root causes for an auditor. It's off by default and never touches the scored/verifypath. Enable it on the backend with:ENABLE_AI=true OPENAI_API_KEY=sk-... # backend-only; rotate if ever exposed OPENAI_AUDIT_MODEL=gpt-5.4 # any model your key can call OPENAI_AUDIT_TIMEOUT_SECONDS=15
Without these the endpoint returns a clean
503— the grader never enables it.
Total 33 points; only Technical Implementation (10) is auto-graded as
10 × (harness score ÷ 100) — our 98.3% maps to ≈ 9.8/10. The other 23
points (Entrepreneurial Drive, Algorithm Approach, Feasibility & Scalability,
Storytelling) come from the submission, demo, and presentation. See
provenance-hackathon/CHALLENGE.md.
Feasibility & scalability: the backend is stateless — every request carries the whole chain, no database. It scales vertically (Gunicorn master + N Uvicorn workers, CPU-bound Ed25519) and horizontally (N identical replicas behind a load balancer, no coordination). ~4 ms/chain; ~1,372 req/s on 8 workers.
- Commits: Conventional Commits, enforced by commitlint + Husky
(see
.claude/rules/committing.md). - Web lint/format: Prettier + ESLint + a design-token linter via lint-staged
on staged
web/files.
