From 7de5c74500ca7030135373fddb108ed2d595915e Mon Sep 17 00:00:00 2001 From: Julian Cuni Date: Sun, 14 Jun 2026 07:45:14 +0200 Subject: [PATCH] wiki: record JWT key open question; defer ESP32 controller MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - open-questions #7: symmetric vs. asymmetric JWT signing key (from the commit security review). Prefer RS256/EdDSA so verifying hosts hold only a public key — mirrors the ATECC608 / challenge-response "public key only" property. Decide before multi-host/multi-lane deployment. - Mark esp32-custom-controller status: deferred per decision not to build device-level auth now; access control stays on UHPPOTE + network isolation (noted in open-questions #6). - local-jwt-auth: document hardened secret handling + 8h expiry and the asymmetric-key pointer. - Update index.md and append a log.md entry. --- wiki/decisions/open-questions.md | 19 +++++++++++++++---- wiki/entities/esp32-custom-controller.md | 8 +++++++- wiki/entities/local-jwt-auth.md | 10 ++++++++-- wiki/index.md | 2 +- wiki/log.md | 9 +++++++++ 5 files changed, 40 insertions(+), 8 deletions(-) diff --git a/wiki/decisions/open-questions.md b/wiki/decisions/open-questions.md index 8b8f7a2..afc5592 100644 --- a/wiki/decisions/open-questions.md +++ b/wiki/decisions/open-questions.md @@ -2,7 +2,7 @@ type: decision tags: [parking, decisions, open] sources: [parking-system-architecture] -updated: 2026-06-14 +updated: 2026-06-15 status: open --- @@ -25,6 +25,17 @@ status: open control. See [[reconciliation]]. 5. **Durability / backup.** Backup strategy for the [[sqlite]] database + recovery plan; "sync later" currently leaves a disk failure as **total revenue-history loss**. -6. **Secure-element integration.** Confirm [[atecc608]] wiring/usage on both the host (event - signing) and, if pursued, the custom controller (command authentication — see - [[esp32-custom-controller]]). +6. **Secure-element integration.** Confirm [[atecc608]] wiring/usage on the host (event + signing). The [[esp32-custom-controller]] command-authentication use is **deferred — not + being implemented for now** (access control stays on the [[uhppote-controller]] behind + [[network-isolation]]); revisit only if prevention-grade device auth becomes a requirement. +7. **JWT signing: symmetric vs. asymmetric key.** _(Raised by the commit security review, not the + source doc.)_ Auth currently uses a symmetric HMAC secret (`@fastify/jwt`, see + [[local-jwt-auth]]) — the same secret signs *and* verifies, so it must live on every host that + validates tokens. Consider rotating to an **asymmetric key (RS256 / EdDSA)** so the server + holds only the **public key** to verify; the private signing key can then live in the + [[atecc608]] or a key-management step. This mirrors the "store only the public key" property + already used for [[atecc608]] event signing and the [[challenge-response-auth]] scheme — + compromising a verifying host yields nothing that can forge a token. Decide before + multi-host / multi-lane deployment (see #1 lane topology), since that's when shared-secret + distribution becomes the liability. diff --git a/wiki/entities/esp32-custom-controller.md b/wiki/entities/esp32-custom-controller.md index 23dec5c..d7a7686 100644 --- a/wiki/entities/esp32-custom-controller.md +++ b/wiki/entities/esp32-custom-controller.md @@ -2,11 +2,17 @@ type: entity tags: [parking, hardware, access-control, upgrade-path] sources: [parking-system-architecture] -updated: 2026-06-14 +updated: 2026-06-15 +status: deferred --- # Custom ESP32 Controller (prevention alternative) +> **Status: deferred — not being implemented for now.** Access control stays on the +> [[uhppote-controller]] behind [[network-isolation]] (tamper-*evident*). This page is kept as +> the documented upgrade path; revisit only if prevention-grade device authentication becomes a +> requirement. See [[open-questions]] #6. + A small custom controller for **device-level authentication** — a control path that holds even against an attacker on the wire. The prevention-grade upgrade from the [[uhppote-controller]] (which is only tamper-*evident*). It moves the [[trust-boundary]] to the device. (See diff --git a/wiki/entities/local-jwt-auth.md b/wiki/entities/local-jwt-auth.md index 64cec9c..e658a9b 100644 --- a/wiki/entities/local-jwt-auth.md +++ b/wiki/entities/local-jwt-auth.md @@ -2,7 +2,7 @@ type: entity tags: [parking, stack, auth, offline-first] sources: [parking-system-architecture] -updated: 2026-06-14 +updated: 2026-06-15 --- # Local JWT Auth @@ -11,9 +11,15 @@ Authentication and authorization, kept **fully local** — a direct consequence [[offline-first]] (an air-gapped park cannot reach an external identity provider; see [[logto-zitadel-oidc]] for the rejected alternative). (See [[parking-system-architecture]] §2.) -- `@fastify/jwt` signs tokens with a **local secret**. +- `@fastify/jwt` signs tokens with a **local secret** (symmetric HMAC). The server **refuses to + start** without a strong `JWT_SECRET` (≥32 chars, no placeholder) — there is deliberately no + insecure default — and mints tokens with an **8h expiry** (bound to a shift). - A `users` table in [[sqlite]] holds **bcrypt** password hashes plus a **role** column. - Authorization = a simple `preHandler` role guard per route: **admin / operator / cashier / readonly**. No Casbin or full RBAC engine needed at this scale. +> **Open decision:** moving from the symmetric secret to an **asymmetric key (RS256/EdDSA)** so +> verifying hosts hold only a public key — [[open-questions]] #7. Relevant before any +> multi-host/multi-lane deployment. + Part of the [[technology-stack]]. License: MIT. diff --git a/wiki/index.md b/wiki/index.md index bd7d2a3..3825624 100644 --- a/wiki/index.md +++ b/wiki/index.md @@ -64,4 +64,4 @@ Counts: 1 source · 14 entities · 10 concepts · 2 decision records. ## Decisions - [[standing-decisions]] — settled decisions (stack, platform, integrity, access control, readers). -- [[open-questions]] — 6 unsettled items that drive procurement. +- [[open-questions]] — 7 open items (6 procurement + JWT key choice); ESP32 device auth deferred. diff --git a/wiki/log.md b/wiki/log.md index 54c80b6..5323285 100644 --- a/wiki/log.md +++ b/wiki/log.md @@ -9,3 +9,12 @@ overview.md, log.md). Created source summary, 14 entity pages, 9 concept pages, decision records (settled decisions + 6 open questions). Source is a dense design doc covering stack, threat model, device architecture, UHPPOTE access control, the custom ESP32 controller alternative, readers, and a reference BOM. + +## [2026-06-15] decision | JWT key choice + ESP32 deferred +From app work, not a new source. Added [[open-questions]] #7 (symmetric vs. +asymmetric JWT signing key — raised by the commit security review; prefer RS256/EdDSA +so verifying hosts hold only a public key, mirroring the ATECC608 / challenge-response +property). Marked [[esp32-custom-controller]] `status: deferred` per decision not to +implement device-level auth for now (access control stays on UHPPOTE + network +isolation); noted in [[open-questions]] #6. Updated [[local-jwt-auth]] (hardened secret +handling + 8h expiry, asymmetric-key pointer) and the index.