Event log: resolve input_received lane from the firing device

Replace the hardcoded lane: 0 on input_received events with a real
device->lane lookup. A new LaneMap caches lane_devices.id -> lane,
built at startup and refreshed by the setup routes on assign/unassign.
An unmapped device logs lane: -1 + a warning (0 is a real lane) and is
still recorded faithfully (append-only chain).

source stays null for raw inputs by design: it's an IdentitySource
(how a vehicle was identified), not a device field; device provenance
remains in identity. Documented both in the wiki.
This commit is contained in:
2026-06-15 12:51:21 +02:00
parent f5fd61984a
commit 59bfe2013f
5 changed files with 83 additions and 7 deletions
+13 -3
View File
@@ -2,7 +2,7 @@
type: concept
tags: [parking, security, integrity]
sources: [parking-system-architecture]
updated: 2026-06-14
updated: 2026-06-15
---
# Append-Only Event Chain
@@ -62,8 +62,18 @@ so old events stay verifiable.
Dingtian **input (button) pushes** → bus → `input_received` events (see [[device-input-flow]],
[[dingtian-relay]]). These are recorded faithfully as raw inputs, **not** as `vehicle_entry` —
the richer entry event waits for the entry flow (ticket print + barrier command). Device→lane
mapping is still a TODO (logged with `lane: 0`).
the richer entry event waits for the entry flow (ticket print + barrier command).
- **`lane`** is now resolved from the firing device. A `LaneMap` (`apps/server/src/lane-map.ts`)
caches `lane_devices.id → lane`, built at startup and refreshed by the setup routes on every
assign/unassign. Device events carry the device instance id, not a lane; the handler looks it
up. A device with no mapping (assigned without a lane, or a stale id) logs **`lane: -1`** and a
warning — never `0`, which is a real lane — and is still recorded (the chain is append-only;
nothing is dropped).
- **`source` stays `null`** for `input_received`, and deliberately so: `source` is an
`IdentitySource` (`wiegand | lpr | qr | ticket | manual`) — *how a vehicle was identified* — not
a device/IP field. A raw button push has no vehicle identity. The device provenance lives in
**`identity`** (e.g. `dingtian:<id> input:1/on`).
### ⚠️ Limitation: the log captures HOST-ORIGINATED actions only