Skip to content

UC-011: Denied Card Escalation

Actor: Member with scheduled access (outside their time window) Priority: Should Status: Implemented (firmware denial tracking + escalation trigger + TOTP keypad)

Summary

A member whose card is denied (e.g., outside their scheduled time slot) can escalate by scanning their card repeatedly. After 3 denied scans within 1 minute, a guarantor is notified and can grant access by sharing their TOTP code over the phone. The member enters the guarantor's key-ID and code on the CONLAN M1200 keypad.

Preconditions

  • Member's NFC card is in the allow-list with SCHEDULED access
  • Current time is outside the member's allowed time slots
  • ESP32 has network connectivity (for sending the escalation notification)
  • At least one guarantor (role >= 0x40) has a TOTP entry in the allow-list

Main Flow

  1. Member arrives outside their scheduled time and scans their NFC card
  2. ESP32 denies access — red LED + denial buzzer
  3. Member scans card again within 1 minute — denied again (2nd scan)
  4. Member scans card a third time within the same 1-minute window
  5. ESP32 detects escalation threshold reached (3 scans within 60s)
  6. ESP32 queues escalation event: {"event": "escalation_triggered", "uid": "...", "timestamp": ...}
  7. LED blinks yellow to indicate "waiting for approval"
  8. Member calls a guarantor they know (phone number known from community)
  9. Member explains the situation; guarantor decides whether to approve
  10. Guarantor opens their authenticator app and reads the current TOTP code
  11. Guarantor tells the member their key-ID and the 6-digit code over the phone
  12. Member types <guarantor_key_id>#<code># on the CONLAN M1200 keypad
  13. ESP32 looks up the guarantor's TOTP entry, verifies the code locally (wide window T-10 to T+1)
  14. Code valid — door unlocks, green LED + confirmation beep
  15. ESP32 clears the denial record for this UID

Alternative Flows

A1: Guarantor denies the request

  1. Member calls a guarantor who decides not to approve
  2. Guarantor does not share a TOTP code
  3. Member cannot enter a valid code — access remains denied
  4. Member's subsequent scans restart the escalation cycle if desired

A2: Member stops scanning

  1. After 1 or 2 denied scans, the member gives up and leaves
  2. No escalation is triggered (threshold not reached)
  3. Denial record expires when the 1-minute window passes

A3: ESP32 has no network connectivity

  1. Member scans card 3 times, escalation threshold reached
  2. ESP32 has no network connectivity — escalation event is queued for the next successful sync
  3. Member calls a guarantor directly (TOTP verification is purely local, no network needed)
  4. Guarantor shares their key-ID and TOTP code by phone — access granted as normal

A4: Guarantor uses HOTP backup code

  1. Guarantor does not have an authenticator app available
  2. Guarantor reads an HOTP backup code from a printed list
  3. Member types the backup code on the keypad
  4. ESP32 verifies against the HOTP counter — access granted

Error Flows

E1: Wrong code entered

  1. Member enters an incorrect code on the keypad
  2. ESP32 rejects — red LED flash
  3. Member can retry (up to 3 attempts before keypad lockout)

E2: Keypad locked out

  1. After 3 failed code entries within 5 minutes, keypad is locked for 5 minutes
  2. NFC card access still works during keypad lockout
  3. Lockout resets after the 5-minute period

Postconditions

  • If approved: member gains one-time access via guarantor's TOTP code, door unlocks
  • No allow-list modification needed — TOTP grants immediate, one-time access
  • Escalation event is logged with the guarantor's key-ID for audit trail
  • TOTP counter updated to prevent replay

Access Rule

  • Escalation works for any card in the allow-list that is denied due to time restrictions
  • The mechanism does NOT work for unknown cards (UID not in allow-list)
  • Escalation parameters are configurable in config.h:
  • ESCALATION_SCAN_COUNT — number of denied scans to trigger (default: 3)
  • ESCALATION_SCAN_WINDOW_MS — time window for repeated scans (default: 60s)
  • TOTP/HOTP parameters in config.h:
  • TOTP_TIME_STEP — TOTP time step in seconds (default: 30)
  • TOTP_WINDOW_GUARANTOR_BACK — guarantor code back window (default: 10 = 5 min)
  • TOTP_WINDOW_GUARANTOR_FORWARD — guarantor code forward window (default: 1)
  • KEYPAD_MAX_ATTEMPTS — max failed entries before lockout (default: 3)
  • KEYPAD_LOCKOUT_MS — base lockout duration, doubles each time (default: 5 min)

Sequence Diagram

Member          NFC Terminal     ESP32              Backend       Guarantor
  |                 |              |                    |              |
  |--- scan 1 ----->|--- UID ---->|                    |              |
  |                 |              |-- DENIED (1/3) --->|              |
  |<--- red LED ----|              |                    |              |
  |                 |              |                    |              |
  |--- scan 2 ----->|--- UID ---->|                    |              |
  |                 |              |-- DENIED (2/3) --->|              |
  |<--- red LED ----|              |                    |              |
  |                 |              |                    |              |
  |--- scan 3 ----->|--- UID ---->|                    |              |
  |                 |              |-- ESCALATION! ---->|              |
  |<--- yellow LED -|              |   (queued event)   |              |
  |                 |              |                    |              |
  |--- calls guarantor by phone ---------------------------------------->|
  |                 |              |                    |              |
  |<-- guarantor tells key-ID + TOTP code by phone ------------------|
  |                 |              |                    |              |
  |--- keypad: 5#287082# -->|---->|                    |              |
  |                 |              |-- lookup key 5     |              |
  |                 |              |-- TOTP verify      |              |
  |                 |              |-- GRANTED          |              |
  |<--- green LED --|              |                    |              |

Notes

  • The member initiates contact — they call a guarantor they know. There is no intercom at the door, so the guarantor cannot contact the member.
  • TOTP verification is purely local — no network round-trip needed. Works even without network connectivity.
  • TOTP secrets are per-user — the guarantor's key-ID identifies which secret to check. The event log records which guarantor authorized entry.
  • The guarantor code uses a wide acceptance window (T-10 to T+1 = 5 min back) to account for phone communication delay.
  • Only cards already in the allow-list can trigger escalation. Unknown cards are ignored to prevent abuse.
  • The ESP32 tracks up to 4 UIDs simultaneously (MAX_DENIAL_TRACKS). If all slots are full, the oldest record is evicted.
  • Future: The ESP32 queues escalation events for backend sync. The backend could forward these to a messenger group channel (e.g., Signal, Telegram) as an additional notification — potentially with smart scheduling (different recipients depending on day of week/month). This is secondary to the direct phone call flow and not yet implemented.
  • See ADR-008 for the full TOTP/HOTP design rationale.