feat(entry): admin bypass of the presence gate for faulty radar/camera
Build desktop / desktop (push) Successful in 4m12s
Build & push images / images (push) Successful in 2m50s
CI / check (push) Successful in 41s

The entry button (physical press AND the operator-issued mint) requires
radar/loop presence + camera detection to confirm a real vehicle. When one
of those devices is faulty, the gate blocks legitimate transient entry. Let
the ADMIN drop a specific signal as a requirement until support fixes the
hardware — the admin is not the adversary, but weakening an anti-fraud gate
stays attributed and auditable:

- Granular: bypass radar and camera independently (Setup → controller
  section). A dead camera drops only the camera check; a dead radar only
  radar. Both off = normal gate; both on = press-to-print.
- Signed: a DEDICATED endpoint (PUT /api/site-config/presence-bypass,
  site:update) appends a signed config_change {setting, value, prev,
  operator} per actually-changed signal — new ledger type. No-op toggles
  sign nothing; disabling signs too. Kept out of the generic site PUT.
- Flagged: every vehicle_entry issued (and every refusal anomaly) while
  bypassed carries presenceBypassed:[...] in its signed payload.
- Persists until turned off; amber warning in Setup while active. The
  booth entry light treats a bypassed signal as satisfied (server
  re-checks authoritatively). Physical-button path falls through to the
  cooldown backstop when radar is bypassed.
- Migration 0020: two boolean site_config columns (default off).

Fixes a latent bug surfaced by the tests: firstRelayByDirection returned no
presenceInput, so issueForOperator's radar gate always read "presence loop
unavailable" — operator-issue never actually gated on radar. The resolver
now attaches the presence input serving the relay (mirrors relayForButton).

10 new tests: 5 gate combinations (each bypass drops only its signal +
records it), 5 route tests (RBAC, signed transitions, no-op, validation).

Claude-Session: https://claude.ai/code/session_01Xcm6ikLgGoCxxHrxtjkk5V
This commit is contained in:
2026-07-04 16:52:34 +02:00
parent 8b65e199a3
commit 6505a4a73b
16 changed files with 497 additions and 20 deletions
+8
View File
@@ -218,6 +218,7 @@ export const en: Catalog = {
evtCashIn: "PAY-IN",
evtCashOut: "PAY-OUT",
evtCashReview: "REVIEW",
evtConfigChange: "CONFIG",
decision: { authorize: "authorized", deny: "denied" },
evtAnomaly: "ANOMALY",
evtRefused: "REFUSED",
@@ -477,6 +478,13 @@ export const en: Catalog = {
confirmRelayTest: "Pulse relay {{relay}} now? This physically opens the barrier and is recorded in the ledger as a test.",
relayTestOk: "✓ R{{relay}} pulsed — barrier opened",
relayTestFailed: "✗ R{{relay}} failed: {{detail}}",
// Entry presence-gate bypass (faulty radar/camera) — admin drops a signal as a requirement.
presenceGateTitle: "Entry presence gate",
presenceGateHint:
"The entry button normally needs both a radar/loop and a camera detection to confirm a real vehicle. If a device is faulty, bypass it so transients can enter until support fixes it. Each change is signed to the ledger, and tickets issued while bypassed are flagged.",
presenceBypassRadar: "Bypass radar / loop (faulty presence sensor)",
presenceBypassCamera: "Bypass camera (faulty vehicle detection)",
presenceBypassActive: "Presence bypass active — the entry gate is weakened. Turn off once the device is repaired.",
// Reveal/hide toggle for a secret field (e.g. the device web password).
revealSecret: "Show password",
hideSecret: "Hide password",
+8
View File
@@ -222,6 +222,7 @@ export const sq = {
evtCashIn: "ARKËTIM",
evtCashOut: "PAGESË",
evtCashReview: "SHQYRTIM",
evtConfigChange: "KONFIG",
decision: { authorize: "autorizuar", deny: "refuzuar" },
evtAnomaly: "ANOMALI",
evtRefused: "REFUZUAR",
@@ -487,6 +488,13 @@ export const sq = {
confirmRelayTest: "Ky veprim hap fizikisht barrierën dhe regjistrohet në ledger si provë.",
relayTestOk: "✓ R{{relay}} u pulsua — barriera u hap",
relayTestFailed: "✗ R{{relay}} dështoi: {{detail}}",
// Anashkalimi i portës së pranisë (radar/kamera me defekt) — admini heq një sinjal si kusht.
presenceGateTitle: "Porta e pranisë në hyrje",
presenceGateHint:
"Butoni i hyrjes normalisht kërkon edhe radarin/lakun edhe një zbulim nga kamera për të konfirmuar një automjet real. Nëse një pajisje ka defekt, anashkaloje që kalimtarët të mund të hyjnë derisa ta rregullojë ekipi i mbështetjes. Çdo ndryshim regjistrohet në ledger, dhe biletat e lëshuara gjatë anashkalimit shënohen.",
presenceBypassRadar: "Anashkalo radarin / lakun (sensor prania me defekt)",
presenceBypassCamera: "Anashkalo kamerën (zbulim automjeti me defekt)",
presenceBypassActive: "Anashkalimi i pranisë aktiv — porta e hyrjes është dobësuar. Fike sapo pajisja të rregullohet.",
// Reveal/hide toggle for a secret field (e.g. the device web password).
revealSecret: "Shfaq fjalëkalimin",
hideSecret: "Fshih fjalëkalimin",