Architecture Decision Records¶
Key decisions behind football-trackers, with context and rationale. Captured from the original requirements discussion (June 2026).
| ADR | Decision |
|---|---|
| 0001 | Build a DIY system instead of using a commercial tracker |
| 0002 | WiFi (not BLE) as the field transport |
| 0003 | Real-time streaming (not store-and-sync) |
| 0004 | ESP32 (WEMOS Lite) + NEO-M8N wearable baseline |
| 0005 | Leg IMU and/or camera CV for technical metrics |
| 0006 | Local core + cloud relay; devices never face the internet |
| 0007 | Per-device MQTT identity + topic ACLs; plaintext on an isolated LAN |
| 0008 | Auth: argon2 + session cookies, roles, mTLS for the field↔relay link |
| 0009 | Caddy as the TLS-terminating edge (auto-HTTPS, only :443 public) |
| 0010 | 30-day local retention + erasure; cloud holds aggregates only |
| 0011 | Keep bun:sqlite; swap to TimescaleDB only on a trigger |
| 0012 | Overlay network (WireGuard/Tailscale) for Profile-B remote access; defer the bespoke relay |
| 0013 | One dedicated, internet-isolated WPA2 AP for match day; broker TLS-PSK deferred behind triggers |
| 0014 | Per-device firmware secrets in NVS, set by a serial enrollment console (one shared image) |
| 0015 | FE auth: cookie-on-upgrade + principal-bound session authz; reject the bundle-baked token |
| 0016 | Player names via an authenticated per-session roster; location stores stay pseudonymous |
| 0017 | Post-match review/replay: one renderer, history read off the live loop |
| 0018 | Live position smoothing with a strict interpolation honesty rule (no fabricated positions) |
| 0019 | Youth age-banded speed zones from a per-session server config store (single source of truth) |
| 0020 | Tactical event detection from GPS (Track A: movement-derived phases); ball events (passes/shots) deferred (Track B) |
| 0021 | Local dev stack: Docker Compose backend + host-run coach view |
| 0022 | Dynamic provisioning: NVS broker host + mDNS (name.local) + captive-portal Wi-Fi setup |
| 0023 | Offline camera/CV match analysis (vision/, build-spec form): realises Track B / Path 2; prototyped on public footage, youth-footage gate deferred |
| 0024 | The coach view reports its own failures: a four-value client beacon, no free text (no error/RUM SDK) |
| 0025 | Operability: ordered graceful shutdown on a signal-deliverable PID 1, user_version migrations, verified VACUUM INTO backups inside ADR-0010's retention |
Format: Context → Decision → Consequences → Alternatives considered.
ADR-0006–0014 realise the target architecture (security + dual
deployment profile) derived from the architecture brief and the
architecture board review. 0012 (overlay)
amends 0006; 0013 was decided by a field-network expert panel; 0014 closes 0013's hard gate.
ADR-0015–0018 come from the FE improvement panel (six experts + adversarial
verification): 0015 implements 0008 for the browser; 0016 extends 0010's pseudonymity to name display; 0017
reuses the 30-day raw store (0010) for review without stalling live ingest; 0018 governs honest motion.
ADR-0019 (Phase-4 coaching metrics) sources youth zone thresholds per session; 0020 adds GPS-only tactical event
detection (Track A) and scopes the ball-event sensing (Track B); 0021 is the local Docker dev/bench stack used for
the first real-device bring-up (local-bench-runbook).
ADR-0023 realises 0020's deferred Track B / 0005's camera Path 2 as a standalone offline vision/ CV subproject
(Veo/Trace-style player+ball tracking → top-down radar + stats), prototyped on public footage only; it
inherits, does not discharge, 0020 §6's child-video DPIA — the real-youth-footage gate stays deferred to a future ADR.
ADR-0024 (audit Phase 5) adds the first browser→server write: a four-value client beacon so a coach whose view
has gone dark is visible from /metrics at all — deliberately not an error/RUM SDK, whose default payload
(stack traces, URLs, DOM context) is exactly what 0010/0016 exist to keep children out of. The same phase
amends 0019, which now also stores each session's four measured pitch corners (they were compiled into
the client bundle, pointing at a bench in Belgrade).
ADR-0025 (audit Phase 6) makes the box operable: an ordered graceful teardown on a process that can
actually receive SIGTERM (docker stop went from exit 137 in 1.3 s to exit 0 in 0.1 s), a user_version
migration ladder that refuses a store newer than the build, and VACUUM INTO backups that inherit
0010's retention window and are reached by 0010's erasure CLI — because a backup is a complete copy
of the same children's location. It also closes the item Phase 5 deferred (server-side scan cancellation)
and stores session tokens as sha256 verifiers, which is what makes carrying coaches' logins across a
restart safe. TLS for the field box is explicitly still open.