wiki: design the business layer (session, tariff, permit, vision, shift, ops)

Pivot from the hardware/integrity layer to the parking operation. All
wiki-only; no code yet. Core principle throughout: business entities are
projections over the signed append-only event log, never mutable tables.

New concepts: parking-session, tariff (composable/versioned, FX-ready),
shift (manned-only Z-report), capacity-occupancy, validation-discounts,
reporting-analytics, clock-integrity, ticket-encoding, anti-passback.
New entities: permit, opencv-anpr-service, blocklist.
Decisions: session-model, vision-service (host-side ANPR + vehicle
verification; scoped AGPL exception for the isolated service).

Updates: append-only-event-chain (new event types + vision witness),
local-jwt-auth (drop 8h expiry -> until logout; code change pending),
lpr-camera (host-side recognition supersedes edge-AI), standing-decisions
(AGPL exception), open-questions (+FX, +pay-station money corners, backup).

Deferred + flagged: intercom/help-call, receipts/refunds/change, FX engine,
lane topology (#1).
This commit is contained in:
2026-06-15 17:41:38 +02:00
parent 2ab5a39a57
commit 8a8e74561d
21 changed files with 1173 additions and 12 deletions
+55
View File
@@ -0,0 +1,55 @@
---
type: decision
tags: [parking, decisions, domain, business]
sources: []
updated: 2026-06-15
status: open
---
# Decision: Parking Session Model
The starting decision for the **business layer**, taken 2026-06-15 as the project pivots from the
(now hardware-verified) device/integrity layer to the parking *operation*.
## Decisions
1. **A session is a projection over the signed event log, not a mutable table.** The
[[append-only-event-chain]] `events` table stays the only source of truth; a
[[parking-session]] is folded from `vehicle_entry` / `vehicle_exit` / `payment` / `void`
events. A cache table is allowed for query speed but is always rebuildable and never
authoritative.
2. **Transient-first, mixed site.** Model the casual pay-for-duration session + [[tariff]] first;
layer [[permit]] holders on top as a second identity source that short-circuits payment
([[entry-exit-readers]]).
3. **Pay-on-foot / pay station.** Payment is **decoupled from exit**: the customer pays at a
central station; the exit lane only validates the session is paid and within the walk-back
grace window before opening ([[parking-session]] lifecycle). Matches the
[[autonomous-direction|unmanned]] roadmap and sharpens [[open-questions]] #3 toward an unmanned
pay station (PCI scope still kept out of the app via a certified terminal).
4. **New signed event types:** `vehicle_entry`, `vehicle_exit`, `payment`, `void` — extend the
existing `input_received`. Recorded in [[append-only-event-chain]].
## Why (rejected alternative)
A **mutable `sessions` table** carrying `amountOwed` / `paidStatus` as the source of truth was
rejected: it reopens the exact fraud vector the system exists to close ([[threat-model]] — the
insider edits the row, marks it paid, pockets the cash). Making "paid" a **signed `payment`
event** means it can't be forged and can't be silently deleted (a deletion breaks the chain). The
projection approach costs a fold/cache but keeps the anti-fraud guarantee intact end-to-end.
## What this unblocks
Closes the dangling thread from [[device-input-flow]] ("the entry flow itself is the next
build"): `input_received` → signed `vehicle_entry` → ticket print → `pulseOpen`, then the
pay-station and exit-validation flows. Schema (`packages/db`) + shared types follow the
[[parking-session]] + [[tariff]] design pages.
## Open / next
- Rate card, currency, grace windows, caps — operator/procurement input ([[tariff]]).
- Tariff versioning (effective-dated) for historical repricing.
- [[permit]] data model + lapsed-mid-stay handling.
- Wire payment capture to a concrete pay-station terminal ([[open-questions]] #3) — kept abstract
(payment = an independent signed event referencing a session) until procurement settles.
- Reconciliation of sessions/payments against an external authority remains [[open-questions]] #4
+ the unbuilt witness/reconciliation gap in [[append-only-event-chain]].