DDR-0027: Session Lifecycle and Framed vs Unframed Traffic Routing

Status: Accepted

Context
E27 communication alternates between unframed cleartext traffic and framed
encrypted traffic depending on session phase. Mixing these concerns leads
to fragile code and difficult testing.

Decision
The session layer enforces explicit routing based on lifecycle state:

1. Unframed Traffic
   - Discovery (ELKWC2017)
   - api_link
   - hello
   - Routed directly as JSON bytes

2. Framed Traffic
   - authenticate
   - all subsequent application messages
   - Routed through:
       framing → decryption → JSON dispatch

3. Session Responsibilities
   - Maintain lifecycle state machine
   - Select framed vs unframed receive path
   - Emit events instead of printing:
       - on_authenticated
       - on_message
       - on_provisioning_required
       - on_error / timeout

4. Explicit Non-Responsibilities
   - No Home Assistant calls
   - No credential storage
   - No protocol encoding details

Consequences
- Clear separation of concerns
- Deterministic behavior across reconnects
- Safe refactoring with high test coverage
## Clarification: api_link framing asymmetry (TX unframed, RX framed+encrypted)

Decision
- The E27 `api_link` exchange is asymmetric:
  - Client -> Panel: send `api_link` as clear JSON, UNFRAMED (no 0x7E framing, no CRC).
  - Panel -> Client: response is FRAMED (0x7E envelope with CRC16) and the payload is SCHEMA-0 ENCRYPTED.

Observed behavior / constraints
- The decrypted `api_link` response plaintext begins with an ACK/NACK byte (0x00 for ACK) followed by UTF-8 JSON.
- The client must therefore:
  - deframe first (verify CRC; keep scanning after CRC failures),
  - then decrypt schema-0,
  - then strip the leading ack/nack byte before JSON decode.

Rationale
- Matches captured traffic from live panel tests (api_link request unframed; response framed+encrypted).
- Aligns with Node-RED implementation behavior and the current smoketest/session driver behavior.

Implications
- Session/linking implementation must treat api_link as a special-case routing rule:
  - unframed send path
  - framed receive path
- Unit tests must cover:
  - unframed TX formatting for api_link
  - framed RX deframe+decrypt+ack-strip+json-decode for api_link

Cross-references
- DDR-0024: Session lifecycle & framed vs unframed traffic routing
- DDR-0020: Silent failure behavior when credentials are incorrect (no response)
- ADR-010x (future): E27 framing / schema-0 semantics (protocol truth)
## Key material and transition semantics during api_link and session establishment

Decision
- The E27 protocol uses three distinct cryptographic key contexts, each with a strictly limited scope:
  1. Provisioning credentials (access code + pass phrase)
  2. Link keys (link_key, link_hmac)
  3. Session keys (session_key, session_hmac)

Key usage by phase

1. Discovery
- No cryptographic keys are used.
- Traffic is clear, unframed JSON.
- Output material: panel nonce (used only to derive link keys).

2. api_link (provisioning / association)
- Client inputs:
  - access_code
  - pass_phrase
  - discovery nonce
- These are used ONLY to derive:
  - link_key
  - link_hmac
- Transmission semantics:
  - Request: unframed, clear JSON (credentials in payload).
  - Response: framed and schema-0 encrypted using derived link_key.
- Security boundary:
  - access_code and pass_phrase MUST NOT be stored after link_key derivation.
  - link_key/link_hmac represent long-lived association state.

3. hello (session bootstrap)
- Inputs:
  - link_key
  - link_hmac
- Transmission semantics:
  - Request: unframed, clear JSON.
  - Response: unframed clear JSON containing encrypted fields.
- Output material:
  - session_key
  - session_hmac
  - session_id
- Security boundary:
  - session keys supersede link keys for all subsequent traffic.

4. Authenticated session traffic
- Inputs:
  - session_key
  - session_hmac
- Transmission semantics:
  - All application messages are framed (0x7E envelope) and schema-0 encrypted.
- link_key/link_hmac MUST NOT be used beyond hello.

Rationale
- Separating provisioning, association, and session keys:
  - limits blast radius of credential exposure,
  - allows long-lived pairing with short-lived sessions,
  - aligns with observed panel behavior and Node-RED reference logic.

Implications
- session.py MUST:
  - never persist access_code or pass_phrase,
  - treat absence or failure of link_key derivation as a provisioning event,
  - invalidate session_key/session_hmac on disconnect.
- provisioning.py owns credential acquisition only, not key storage.
- Unit tests must assert:
  - correct key usage per phase,
  - no session traffic encrypted with link keys,
  - no api_link traffic encrypted with session keys.

Cross-references
- DDR-0022: Cryptographic primitives and schema-0 envelope rules
- DDR-0024: Session lifecycle and framed vs unframed routing
- ADR-010x: E27 crypto and framing architecture (to be formalized)
