bfb6ab0b36
Add a third data stream (app_logs), distinct from the signed ledger and device telemetry, for operational/diagnostic logs — an offline appliance has no Sentry to ship to, so the host is the log store. Backend: a pino stream tees warn/error/fatal into app_logs (info/debug stay stdout-only) with no call-site change; the DB is built before Fastify so the logger has its sink. Frontend (lib/logger.ts): ships failed API requests (minus 401 churn), window.onerror, unhandledrejection, and a top-level React ErrorBoundary; console warn/error forwarded only at debug/trace. Batched/throttled POST, sendBeacon on pagehide, loop-safe (never logs the /api/logs call), best-effort everywhere. POST /api/logs (any signed-in user, CSRF, tolerant) + GET /api/logs gated by a new log:read permission (new `log` RBAC resource; admin holds it). Retention: pruned by age + row cap, hourly + at startup. UI: a Logs screen under /setup (filter level/source/since, expand to context+stack), sq+en. Migration 0009_app_logs. Verified end-to-end via app.inject: login -> POST 204 -> GET 200 with the record; backend warn/error persisted, info dropped; non-admin GET 403 / POST 204. Claude-Session: https://claude.ai/code/session_01Xcm6ikLgGoCxxHrxtjkk5V
51 lines
2.3 KiB
Markdown
51 lines
2.3 KiB
Markdown
---
|
|
type: concept
|
|
tags: [parking, devices, monitoring, telemetry]
|
|
sources: []
|
|
updated: 2026-06-15
|
|
status: open
|
|
---
|
|
|
|
# Device Events (telemetry)
|
|
|
|
The **unsigned** operational record of what the hardware did and reported — distinct from the
|
|
signed business [[append-only-event-chain|ledger]] (see [[event-streams-split]]). For monitoring,
|
|
diagnostics, and live booth status — **not** anti-fraud.
|
|
|
|
## What lands here
|
|
|
|
- **Relays/barriers:** relay fired/released, pulseOpen issued (the *device-side* echo; the
|
|
authoritative `barrier_open_command` is a signed ledger event).
|
|
- **Printers:** paper-out / near-end / cover-open / cutter / offline (already polled —
|
|
[[printer-status-monitoring]]).
|
|
- **Cameras:** reachable/offline, snapshot success/failure ([[lpr-camera]]).
|
|
- **Readers / inputs:** a raw read, raw input edges (Dingtian button `input N on/off` —
|
|
[[device-input-flow]]).
|
|
|
|
## Properties
|
|
|
|
- **Unsigned, not chained** — no `prevHash`/`signature`. It's telemetry, so it carries none of the
|
|
ledger's integrity machinery.
|
|
- **Disposable** — high-volume and churny; **may rotate/prune** on a retention policy (the ledger
|
|
never does).
|
|
- **Device-keyed** — references the `devices` instance (raw device provenance). No `lane`
|
|
(pool-of-spaces model — see [[entry-exit-points]]).
|
|
|
|
> **Not to be confused with [[app-logs]].** `device_events` is **hardware telemetry** (a relay fired,
|
|
> a camera failed). Diagnostic/application logs (a failed API request, an uncaught frontend error,
|
|
> a backend warning) are a **separate third stream** in `app_logs` — don't route app errors here, nor
|
|
> hardware telemetry there. Both are unsigned + prunable; the distinction is *what produced it*.
|
|
|
|
## The boundary that matters
|
|
|
|
A device event is *evidence the host saw something happen*; it does **not** by itself authorize or
|
|
record a business fact. A button press here becomes a **signed `vehicle_entry`** in the ledger only
|
|
after the entry flow runs (ticket + barrier command). This keeps device chatter on the device side
|
|
of the [[device-adapter-pattern|adapter boundary]] and the signed ledger focused on money/access.
|
|
|
|
## Open
|
|
|
|
- Retention/rotation policy (size- or age-based).
|
|
- Whether any witness-grade device fact (e.g. a loop-sensor `barrier_open_observed`) should *also*
|
|
write a signed ledger entry for [[reconciliation]] — see [[append-only-event-chain]].
|