1efa77bf56
A parking lot is one pool of spaces with a flexible set of entry/exit
points — no "lane". Direction is a property of each RELAY inside an access
controller; readers/cameras bind to a controller relay and inherit it.
Schema:
- drop `lane` from ledger_events, device_events, sessions
- rename lane_devices -> devices (no lane/direction columns)
- access config.relays=[{relay,direction,button?}]; reader/camera
config.controllerId+relay binding
- fresh 0000_baseline migration (history reset; dev data was throwaway)
Signed ledger:
- remove `lane` from canonicalize(); bump signer keyId sw-hmac-v1 -> v2
(v1 events won't verify under v2 — intentional, gated per-event by keyId)
Server:
- new device-resolve.ts (replaces lane-map.ts): relayForButton,
relayForDevice, firstRelayByDirection, devicesByDirection
- entry-flow: button terminal -> its relay; exit/permit: reader's bound
relay; dispatcher resolves the bound relay + inherited direction
- camera snapshots fire by direction site-wide, async, never block open
- DeviceConfig widened to nested JSON for relays[]
Web:
- wizard: no lane selector; add controllers (relay map + entry-button
terminal) first, then bind readers/cameras/printers to a controller relay
Wiki: new entry-exit-points.md (replaces lane-direction); reworked
entry-exit-readers, parking-session, first-run-setup, device-registry,
append-only-event-chain, device-events; removed stale lane/LaneMap mentions.
4.6 KiB
4.6 KiB
type, tags, sources, updated, status
| type | tags | sources | updated | status | ||||
|---|---|---|---|---|---|---|---|---|
| decision |
|
|
2026-06-15 | open |
Open Questions / Next Steps
Not yet decided, and they drive everything else — settle before procurement. (See parking-system-architecture §10.)
- Lane topology. One host per lane, or one central host driving networked devices in each lane? Decides how many controllers, printers, UPSs, and sqlite instances exist, and the failure blast radius. (A single central host is a single point of failure for all lanes.)
- Failure modes. Define per direction what happens to barriers on host/power/network loss — particularly fail-open on exit for egress safety. Currently unaddressed. See fail-state-safety.
- Payment subsystem. Manned booth (P2PE terminal + cash drawer) vs unmanned pay station; confirm PCI scope is kept out of the application via a standalone certified terminal (see bom).
- Reconciliation channel. Even if "offline," establish some periodic path (USB, hotspot, manager visit) to reconcile the signed log against an external authority — the real anti-fraud control. See reconciliation.
- Durability / backup. Backup strategy for the sqlite database + recovery plan; "sync
later" currently leaves a disk failure as total revenue-history loss. (Confirmed in-scope
to design, 2026-06-15.) Because the DB is the signed append-only-event-chain, a backup must
preserve the chain intact (a restored copy must still
verifyChain); options include SQLite WAL/online-backup snapshots to a second disk/USB + the periodic external export that doubles as the reconciliation channel (#4). Encryption at rest already applies (disk-os-hardening). Design TBD. - Secure-element integration. Confirm atecc608 wiring/usage on the host (event signing). The esp32-custom-controller command-authentication use is deferred — not being implemented for now (access control is the dingtian-relay behind network-isolation); revisit only if prevention-grade device auth becomes a requirement.
- JWT signing: symmetric vs. asymmetric key. (Raised by the commit security review, not the
source doc.) Auth currently uses a symmetric HMAC secret (
@fastify/jwt, see local-jwt-auth) — the same secret signs and verifies, so it must live on every host that validates tokens. Consider rotating to an asymmetric key (RS256 / EdDSA) so the server holds only the public key to verify; the private signing key can then live in the atecc608 or a key-management step. This mirrors the "store only the public key" property already used for atecc608 event signing and the challenge-response-auth scheme — compromising a verifying host yields nothing that can forge a token. Decide before multi-host / multi-lane deployment (see #1 lane topology), since that's when shared-secret distribution becomes the liability. - Exchange-rate (FX) system. (Raised by the tariff design, 2026-06-15.) Currency is
selectable per tariff version and the money model is FX-ready (
paymentstores currency + a reservedfxRate), but no conversion is built. If multi-currency pricing/charging is ever needed, it requires an offline rate source (rates can't depend on the network — offline-first), a base currency, and a rounding policy. Deferred; nothing blocks adding it later without migrating stored amounts. - Pay-station money corners — receipts & refunds/change. (Raised by the scope sweep,
2026-06-15; deferred until pay-station hardware is chosen.) Not yet designed: receipts / VAT
invoices (fiscal receipt with tax number + sequential numbering may be legally required — could
change what the
paymentevent must store) and refunds / overpayment / change (cash change, "exact change only", a refund as a signed reversal event). Both depend on the unmanned-vs-manned payment subsystem (#3) and the note/coin/card acceptor hardware. Revisit at procurement. - Snapshot retention. (Raised by the entry-exit-points camera-snapshot build, 2026-06-16.)
Entry/exit snapshots are stored as BLOBs in the sqlite
snapshotstable. This grows the single DB file fast (~100–300 KB per image × every entry and exit), and SQLite doesn't reclaim deleted-blob pages withoutVACUUM. Undecided: pruning policy (age-based vs. total-size cap), VACUUM cadence, and how this interacts with the #5 backup strategy (blobs bloat every backup). Until decided, snapshots accumulate unbounded. See entry-exit-points.