Turborepo (pnpm workspaces) with all dependencies pinned to latest mutually-compatible versions: turbo 2.9, TypeScript 6, Fastify 5, React 19, Vite 8, better-sqlite3 12 + Drizzle ORM 0.45. Layout: - apps/server Fastify backend (local JWT auth + role guard, /health) - apps/web React 19 + Vite 8 operator SPA - packages/db Drizzle schema on SQLite/WAL; append-only events + users - packages/devices reader/printer/relay adapter interfaces (intent-only relay) - packages/shared shared domain types Architecture constraints from the design wiki are encoded in the scaffold: append-only hash-chained + signed event log, device-agnostic adapters, "a barrier is not a door" (relay expresses intent only), fully-local offline-first auth. wiki/ is an LLM-maintained Obsidian knowledge base (28 pages) ingested from the architecture & design notes, with its own maintenance schema. Verified: pnpm install, full turbo build (5/5), server boots and serves /health, drizzle-kit generates the initial migration.
1.7 KiB
type, tags, sources, updated
| type | tags | sources | updated | ||||
|---|---|---|---|---|---|---|---|
| concept |
|
|
2026-06-14 |
Threat Model
The second foundational force (with offline-first). The central insight is a reframing of who the adversary is. (See parking-system-architecture §3.)
The key reframing
Early thinking focused on protecting the database at rest — SQLCipher, LUKS, BitLocker, TPM-sealed keys. All of that defends against an outsider who steals the machine or boots from external media.
That is the wrong primary threat. The most likely adversary is the legitimate operator at the booth. While the app runs, the database is decrypted in memory and the operator has full authorised access through the app. Encryption does nothing against the classic parking fraud: take the cash, then void/delete the entry/exit record so the books balance.
Consequences
The controls that actually address insider/operator fraud are different in kind:
- append-only-event-chain — events appended, never edited/deleted; a "void" is itself a recorded event, hash-chained, and atecc608-signed (unforgeable).
- reconciliation against an authority the operator can't alter — this is what remote sync really is: a fraud-control mechanism, not just a backup.
- disk-os-hardening still worthwhile (defeats boot-from-USB) but not the main event; with LUKS in place, SQLCipher is optional defence-in-depth.
The same reframing recurs at the device layer: the uhppote-controller's real problem is unauthenticated commands (uhppote-udp-protocol), addressed by detection (event-log-ingestion) or prevention (esp32-custom-controller).