feat(logs): app log store — backend pino DB sink + frontend error collection
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
This commit is contained in:
@@ -50,8 +50,16 @@ A raw button press is **telemetry** → `device_events`. The entry flow then min
|
||||
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.
|
||||
|
||||
## A third stream followed (2026-06-19)
|
||||
|
||||
The same separation logic produced a **third** stream: **`app_logs`** — operational/diagnostic logs
|
||||
(backend warn+ via a pino sink, plus frontend errors). They're neither business facts (ledger) nor
|
||||
hardware telemetry (device_events), so they get their own unsigned, prunable table. See
|
||||
[[app-logs]]. The principle generalizes: *one stream per durability/meaning class*.
|
||||
|
||||
## Open
|
||||
|
||||
- `device_events` retention/rotation policy.
|
||||
- `device_events` retention/rotation policy. (Resolved for `app_logs`: age + row cap — see
|
||||
[[app-logs]]; the same policy is a candidate for `device_events`.)
|
||||
- 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.
|
||||
Reference in New Issue
Block a user