docs(wiki): booth bring-up fixes — relay password, secret re-merge, lamp concurrency

- dingtian-relay: the "offline despite ping" gotcha (relay_pw in every binary frame,
  missing form field → Test connection sent 0 → timeout) + the identity-gated secret
  re-merge that stops a redirected probe exfiltrating the password.
- button-light-indicator: serialized desired-state worker (UDP is unordered → the lamp
  stuck on/off) and hot-reload of the lamp config (no restart).
- log entry for the three fixes (commits 420542c / fd15988 / 830993b).

Claude-Session: https://claude.ai/code/session_01Xcm6ikLgGoCxxHrxtjkk5V
This commit is contained in:
2026-06-24 19:11:49 +02:00
parent 830993bcb8
commit 5a5fedf4f4
3 changed files with 57 additions and 4 deletions
+22
View File
@@ -157,6 +157,28 @@ On assign the driver runs `harden()` (the [[device-registry|HardenableDevice]] c
> drop connections (ECONNRESET), locking out the API the driver depends on — recoverable only by
> factory reset. `harden()` deliberately never touches it.
## `relayPassword` field + the "offline despite ping" gotcha (2026-06-24)
`relay_pw` is in **every** binary frame — control AND the status read `healthCheck()` uses. With a
wrong/missing value the device **silently drops the packet** (no NAK), so the probe **times out →
the controller shows "offline" even though it pings** (ping is ICMP and never touches the binary
protocol). This bit a real bring-up: the driver read `config.relayPassword` but there was **no form
field** for it, so Test connection sent `0` → timeout → "offline", while `relay_pw` was actually a
non-zero value the harden flow had set. Diagnostic: a raw UDP status frame
(`FF AA <s> 00 <pwLo> <pwHi>`) replies *only* with the right password — `pw=N` → `ffaa…`, `pw=0` →
timeout — and binding the WSL socket to the device-facing NIC (`localAddress`) also broke the reply
(leave it unbound on WSL). Fix: a **"Relay control password"** config field (a **secret**; blank =
keep the stored value).
> 🔒 **Secret re-merge is identity-gated (don't let a redirected probe exfiltrate it).** Because
> `relayPassword`/`pushPassword` are redacted from the client ([[first-run-setup]]), the edit form
> can't resend them, so `/api/setup/test` re-merges the stored secret by device **id** — but ONLY
> when the submitted config addresses the **same device**: matching `driverId` and every
> connection-identity field it sets (`host`/`port`/`binaryPort`/`httpPort`/`serial`). A redirected
> host/port or mismatched driver returns NO secret, so an authenticated admin can't point a test at
> an attacker host and have the password sent there (the booth operator is the [[threat-model]]
> adversary). Save already merged from the stored row; this closes the same gap on test.
## Status — VERIFIED on hardware (DT-R004, sw V3.1.5461A, 10.0.10.172)
- ✅ status read (`0000:1111:4`), relay pulse, input press/release events (active-LOW, idle HIGH).