--- type: concept tags: [parking, security, foundational] sources: [parking-system-architecture] updated: 2026-06-20 --- # 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|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]]). > **Worked example — "store the price" ≠ "account for the sale" (found + fixed 2026-06-20).** Every > money-taking action must append a signed `payment` event, or it is invisible to > [[reconciliation]]. A concrete miss: selling a [[subscription]] wrote only the mutable > `subscriptions` master row (the agreed *price*) and **appended nothing to the ledger**, so the cash > the operator collected showed up in the feed/drawer/Z-report **nowhere** — a clean off-book channel > (three real sales, 27,000 ALL, untraceable). The fix is the textbook control: append a signed > `payment` (`subscriptionSale: true`) at sale time so it folds into the [[shift]] like any taking. > **The lesson generalises:** whenever a feature records *an amount* in a mutable table, ask "where is > the signed event that says money changed hands?" — a price in master data is not an accountable > transaction. See [[subscription]] "Collecting the fee". > **Direction shift:** the system is heading toward **fully unmanned operation** — no operator, no > booth ([[autonomous-direction]]). That removes the booth-operator as the *primary* adversary, but > swaps in **unattended-machine threats** (tailgating, plate spoofing, physical tampering, forced > entry). The append-only signed log + reconciliation controls carry over; the emphasis moves from > "catch the cashier" to "trust the automated record and detect tampering."