- lpr-camera.md: "503 Device Busy" can be PERSISTENT (main-stream saturation on
the G3H) — the real fix is sub-stream selection, not just retry.
- device-status-monitoring.md: QR reader health was false-healthy (hardcoded
"ready") until the ICMP-ping fix; document the push-device monitoring model.
- log entries for both 2026-06-26 sessions.
Claude-Session: https://claude.ai/code/session_01Xcm6ikLgGoCxxHrxtjkk5V
Wire the lane camera's vehicle event into the gated subscription flow: on a
vehicle/active push from an opt-in (config.anpr) camera, AnprBridge pulls a fresh
snapshot, runs ANPR, applies a stricter entry confidence floor, debounces, and —
matching the plate to a subscription BEFORE emitting — emits a kind:"plate" read.
The existing ReadDispatcher -> SubscriptionFlow then signs the entry/exit and opens
the barrier. A plate is never the sole authority: it routes through the same gate
(active/window/blocklist/car-count) as any credential. Fail-soft, fire-and-forget,
subscriber-only by construction. Field-verified end to end (plate AA504LX opened the
entry barrier and appended a signed vehicle_entry).
Add an admin master switch (site_config.anpr_entry_enabled, default ON) in Site
Settings that disables ONLY the barrier-driving bridge; advisory snapshot-ANPR and
lane busy/free are unaffected. Read live per event, so toggling takes effect with no
restart. Migration 0013 (additive ALTER ADD COLUMN, default 1).
- New: apps/server/src/anpr-entry.ts (AnprBridge) + tests (9)
- hikvision-alarm.ts hands vehicle detections to the bridge (fire-and-forget) + wiring tests (3)
- server.ts reorders the read flows above the hik-alarm registration
- snapshot.ts exports buildCamera for reuse
- env: VISION_ENTRY_MIN_CONFIDENCE (0.85), ANPR_DEBOUNCE_MS (12000)
- site route + SiteSettings checkbox + i18n (sq/en parity)
- wiki: lane-presence-and-anpr-entry / lpr-camera / index / log -> BUILT
Claude-Session: https://claude.ai/code/session_01Xcm6ikLgGoCxxHrxtjkk5V
Captures this session's back-and-forth as a new concept page
[[lane-presence-and-anpr-entry]] and cross-links it:
- BUILT: advisory lane busy/free booth lights (LaneStatus + WS), with the
measured camera limits behind the 30s timeout (no leave signal; movement-
driven re-fire; notificationRecurrence locked to "beginning" — ISAPI flip
silently reverts).
- PLANNED: the ANPR "bridge" — explicitly a small apps/server HANDLER (~40
lines), NOT a new service/container. On a camera vehicle event: snapshot ->
ANPR -> high-confidence match -> debounce -> emitRead{kind:"plate"}, then
the existing subscription match/dispatch/gate admits the subscriber. Both
directions, opt-in (config.anpr), plate never the sole authority.
- Records the decisions (high confidence floor, debounce-for-correctness)
and the REJECTED ideas (continuous livestream / per-car queue tracking /
make-model) with why, plus the open hardware question (booth-PC test).
Updates subscription.md (plate matching is built; the live source is this
bridge) and lpr-camera.md (the two consumers of the vehicle event).
Claude-Session: https://claude.ai/code/session_01Xcm6ikLgGoCxxHrxtjkk5V
The Hik DS-2CD1043G2-LIU was NOT defective. An earlier wiki entry wrongly
concluded it needed RMA (dead event engine) based on a silent alertStream
+ diskfull/EventScribe:except + dead RTC surviving a full factory reset.
Real cause: no detection AREA was drawn on the frame. With no region, the
camera detects nothing -> generates no event -> posts nothing. The instant
an area was drawn, the first vehicle produced a clean POST.
- Flag "draw the detection area" as the FIRST thing to check.
- Document the confirmed real payload: multipart/form-data (MoveDetection.xml),
EventNotificationAlert with eventType=VMD, eventState=active,
targetType=vehicle (vehicle/human classified on-device), targetRect bbox.
Note the dateTime is garbage (dead RTC) -> use our own receive time.
- Reframe the SSH diagnostics: diskfull/EventScribe/RTC are RED HERRINGS,
not proof of a dead camera; don't escalate to hardware fault while a basic
config precondition is unmet.
- Append a log correction (append-only) superseding the earlier conclusion.
Claude-Session: https://claude.ai/code/session_01Xcm6ikLgGoCxxHrxtjkk5V
Captures the hard-won findings from the field session: the WSL source-IP
rewrite + skipSourceIpCheck fix, the boolean-as-string setup bug, the
unreliable "Test" button, the latching httpBroken flag, and Notify-
Surveillance-Center vs HTTP-Alarm-Server.
Adds a "diagnose a non-pushing camera from its OWN state" runbook
(alertStream heartbeat silence, SSH showStatus EventScribe:except, dmesg
RTC/UBIFS, netstat outbound watch) and documents the verified-dead
DS-2CD1043G2-LIU unit (defective event engine, survives factory reset ->
RMA), with the pull+vision fallback.
Claude-Session: https://claude.ai/code/session_01Xcm6ikLgGoCxxHrxtjkk5V
Newer Hik firmware can PUSH events to us: Event -> Smart/VCA with
"Detection Target: Human/Vehicle" + Notify Surveillance Center + Alarm
Settings -> Alarm Server makes the camera HTTP-POST an
EventNotificationAlert on each detection.
- New POST /api/devices/hikvision/:deviceId/event (routes/hikvision-alarm.ts):
same machine-push pattern as the Dingtian Input Link — source-IP guarded
+ optional HTTP Digest, not behind the SPA cookie/CSRF.
- Discovery-first / permissive: a wildcard content-type parser accepts ANY
body as raw bytes (event XML, multipart+JPEG, or JSON — Hik varies by
firmware), records it verbatim as a kind:"alarm" device_event, and
best-effort extracts eventType/target/plate/dateTime/channelID for the
summary + a loud log line. The point is to SEE exactly what a camera
sends before wiring it further.
- hikvision driver gains alarmPushEnabled + pushUser/pushPassword config and
pushesToBackend:true (setup offers the backend push IP).
- NOT yet a barrier trigger / DeviceReadEvent — records only. A plate read
is advisory, never the sole reason a barrier opens; the read-bus/ANPR
wiring is a deliberate next step once the real payload is known.
Tests: hikvision-alarm.test.ts (6: vehicle XML summary, ANPR plate, raw
JSON, wrong-IP 404, disabled 404, unknown-device 404). server 109/109;
build+lint 14/14. Wiki: lpr-camera.md + log.
Claude-Session: https://claude.ai/code/session_01Xcm6ikLgGoCxxHrxtjkk5V
Replace the camera stub with HttpCamera: Hikvision ISAPI and Dahua CGI
snapshots over client-side HTTP Digest (new drivers/http-digest.ts).
healthCheck() now pulls a real frame instead of returning ready/stub.
Snapshot carries bytes (driver fetches); storage/imageRef is the caller's
job, keeping the adapter free of storage deps.
Fix the cosmetic Backend-push-IP field: add pushesToBackend to DeviceDriver
(only Dingtian sets it), expose as pushCapable in the catalog, and gate the
wizard's backend-IP fetch + field on it so pull-only devices hide it.
Verified on hardware (Hikvision 10.0.10.121): healthCheck ready,
captureSnapshot returns a valid JPEG.
Turborepo (pnpm workspaces) with all dependencies pinned to latest
mutually-compatible versions: turbo 2.9, TypeScript 6, Fastify 5,
React 19, Vite 8, better-sqlite3 12 + Drizzle ORM 0.45.
Layout:
- apps/server Fastify backend (local JWT auth + role guard, /health)
- apps/web React 19 + Vite 8 operator SPA
- packages/db Drizzle schema on SQLite/WAL; append-only events + users
- packages/devices reader/printer/relay adapter interfaces (intent-only relay)
- packages/shared shared domain types
Architecture constraints from the design wiki are encoded in the scaffold:
append-only hash-chained + signed event log, device-agnostic adapters,
"a barrier is not a door" (relay expresses intent only), fully-local
offline-first auth.
wiki/ is an LLM-maintained Obsidian knowledge base (28 pages) ingested
from the architecture & design notes, with its own maintenance schema.
Verified: pnpm install, full turbo build (5/5), server boots and serves
/health, drizzle-kit generates the initial migration.