UC-009: Allow-List Sync¶
Actor: System (ESP32) Priority: Must Status: Implemented (protocol definition)
Summary¶
The ESP32 periodically syncs with the backend to fetch the latest allow-list and report queued events.
Preconditions¶
- ESP32 has network credentials stored in NVS (WiFi SSID/password or Ethernet configured)
- Backend is reachable via HTTPS
- ESP32 has the Ed25519 public key for signature verification
- 5-minute sync interval has elapsed since last sync
Main Flow¶
- Sync timer fires (every 5 minutes)
- ESP32 pauses BLE (NUKI temporarily unreachable)
- ESP32 connects to the network
- ESP32 sends
GET /api/v1/device/allowlist?current_version=N - Backend responds with 200 + signed allow-list payload (if newer version exists)
- ESP32 verifies Ed25519 signature over header + entries
- ESP32 checks version is strictly greater than current version (replay protection)
- ESP32 stores new allow-list in LittleFS and updates RAM cache
- ESP32 sends queued events:
POST /api/v1/device/events - ESP32 sends heartbeat:
POST /api/v1/device/heartbeat - ESP32 disconnects from the network, resumes BLE (WiFi only)
Alternative Flows¶
A1: No update available (304)¶
- At step 5, backend responds with 304 Not Modified
- ESP32 skips steps 6–8, proceeds to send events and heartbeat
- Network disconnects (WiFi only), BLE resumes
A2: First boot (no existing allow-list)¶
- ESP32 has no stored allow-list
- Sends
GET /api/v1/device/allowlist?current_version=0 - Backend always responds with the latest allow-list
- Normal verification and storage proceeds
Error Flows¶
E1: Network connection fails¶
- At step 3, network connection times out (10 seconds)
- ESP32 keeps the existing allow-list (if any)
- BLE resumes, retry at next sync interval
- All access decisions use the cached allow-list
E2: Signature verification fails¶
- At step 6, Ed25519 signature does not match
- New allow-list is rejected
- Existing allow-list remains active
- Error event logged for next successful sync
E3: Version rollback detected¶
- At step 7, new version <= current version
- Payload rejected (possible replay attack)
- Existing allow-list remains active
E4: Backend unreachable¶
- HTTP request fails or times out
- Same as E1 — cached allow-list continues to work
- If allow-list has been stale for >24 hours, SCHEDULED entries start being denied
Postconditions¶
- ESP32 has the latest allow-list (or keeps the previous valid one)
- Queued events have been delivered to the backend
- Backend knows the device is alive (heartbeat)
Access Rule¶
- N/A (system process, not tied to an NFC card)
Notes¶
- The network connection is only active for ~5 seconds per sync cycle. When using WiFi, BLE (NUKI) is unavailable during this window. With Ethernet, BLE remains available at all times.
- The 5-minute interval is configurable via
SYNC_INTERVAL_MSin config.h. - If the allow-list becomes stale (valid_until passed), UNRESTRICTED and EMERGENCY entries still work, but SCHEDULED and CONDITIONAL entries are denied for safety.
- The monotonic version number prevents replay attacks — an attacker cannot feed an older list to the device.