Files
parking_solution/wiki/decisions/event-streams-split.md
T
julian 9a4c7ee27b wiki: split signed business ledger from device telemetry
Correction before schema work: the events table conflated the anti-fraud
business ledger with device telemetry. Decision: ledger_events (signed,
chained, reconciled) holds only business facts; device_events (unsigned,
prunable) holds relay/printer/camera/reader/input telemetry. A raw button
press is telemetry; the entry flow mints a signed vehicle_entry. Drops
input_received-as-signed-event.

New: decisions/event-streams-split, concepts/device-events; updated
append-only-event-chain, index, log.
2026-06-15 18:08:56 +02:00

2.7 KiB

type, tags, sources, updated, status
type tags sources updated status
decision
parking
decisions
integrity
devices
schema
2026-06-15 open

Decision: Split the signed business ledger from device telemetry

Taken 2026-06-15, at the start of the business-layer schema work.

The problem

The existing events table (signed, hash-chained — append-only-event-chain) had grown to carry two unrelated concerns: the financial/accountability ledger and raw device telemetry (button pushes recorded as input_received). They have opposite requirements — the ledger must be small, signed, and reconciled; telemetry is high-volume, churny, and disposable.

Decision — two tables

  • ledger_events — the signed, hash-chained, atecc608-signed business ledger (rename of events). Holds only business/accountability facts: vehicle_entry, vehicle_exit, payment, void, shift_z_report, and the witness-grade barrier_open_command / barrier_open_observed / anomaly. reconciliation runs against this; sessions/tariff/occupancy are projections of it.
  • device_events — unsigned operational telemetry (see device-events): relay fired, printer paper-out, camera offline, reader read, raw input edges. May rotate/prune. Never signed, never reconciled.

A raw button press is telemetry → device_events. The entry flow then mints a signed vehicle_entry in the ledger once a ticket prints + the barrier is commanded. So input_received-as-a-signed-event is dropped (it was transitional).

Why

  • Keeps the signed ledger small and high-value — fewer rows to sign, hash, verify, reconcile, and export; signal isn't drowned in device noise.
  • Right durability semantics per stream: the ledger is precious + append-only forever; telemetry can age out.
  • Clean separation matches the device-adapter-pattern philosophy — device chatter stays on the device side of the boundary.

Consequences / migration (no production data yet)

  • No .sqlite with real chain data exists, so renaming + restructuring is safe now (no signatures to invalidate). This is the moment to do it.
  • Code: rename events → ledger_events; EventLog/canonicalize/verifyChain and the /api/events routes follow the rename; add an unsigned device_events writer; move the Dingtian input-push handler to emit device_events (+ the entry flow signs vehicle_entry).
  • ParkingEventType in packages/shared splits into ledger types vs. a device-event type set.

Open

  • device_events retention/rotation policy.
  • Which device facts (if any) are witness-grade enough to also warrant a signed ledger entry (e.g. barrier_open_observed from a loop sensor) — see append-only-event-chain witness gap.