# DDR-0019: E27 Provisioning vs Runtime Responsibilities and Module Boundaries

Status: Approved
Date: 2025-12-22
Applies to: elkm1/elke27_lib (E27 backend)
Related ADRs: ADR-0003 (E27 API Message Layer Architecture), ADR-0006 (E27 Transport and Link Layer Separation)
Related DDRs: DDR-0016 (Cleartext Messages Not Link-Framed), DDR-0017 (Ack/Head Byte Before JSON)

## Context

The E27 protocol has two distinct lifecycles:

1) Provisioning lifecycle (one-time, user-interactive)
   - The `api_link` exchange associates an application instance with a panel,
     analogous to pairing a keypad/device to the panel.
   - `api_link` requires user secrets (access code and passphrase).
   - Project policy: access code and passphrase are NOT persisted.

2) Runtime lifecycle (recurring, non-interactive)
   - Each new TCP connection requires `hello` to establish per-session keys.
   - Subsequent commands are sent using schema-0 encryption and link-layer framing.

Because access code/passphrase are not stored, provisioning must be initiated
only when the caller (Home Assistant) can prompt the user and supply those secrets.

## Decision

Module boundaries are defined as follows:

- `linking.py` owns the `api_link` protocol mechanics:
  - Parsing discovery nonce required for key derivation
  - Deriving `pass` and `tempkey` from (access code, passphrase, nonce, identity)
  - Building and sending the `api_link` request (cleartext, unframed)
  - Receiving the `api_link` response (encrypted schema-0 framed), decrypting,
    stripping the ack/head byte, and extracting:
      - `linkkey`
      - `linkhmac`
  - `linking.py` may define a `LinkCredentials` structure (or it may be placed
    in a small `credentials.py` module).

- `provisioning.py` (if present) is orchestration glue only:
  - It wires together discovery + `linking.py` functions into a single callable
    that returns `LinkCredentials`, given user-provided secrets.
  - It has NO Home Assistant dependencies and performs NO prompting.

- `session.py` owns runtime connection behavior and never prompts for secrets:
  - Requires `LinkCredentials` to be provided for normal operation
  - Performs per-connection `hello` and decrypts session keys using `linkkey`
  - Executes encrypted JSON commands over the schema-0 channel
  - If link credentials are missing, `session.py` MUST raise a structured error
    (e.g., `E27ProvisioningRequired`) that the caller can map to an interactive
    provisioning flow.

Home Assistant is responsible for:
- Prompting the user for access code and passphrase during provisioning/reauth
- Calling into `provisioning.py` / `linking.py` to obtain link credentials
- Persisting only link credentials (and other allowed settings), not secrets

## Consequences

- The library remains HA-agnostic and testable.
- Provisioning and runtime paths are clearly separated and easier to reason about.
- Errors caused by missing or invalid link credentials can be mapped cleanly to
  Home Assistant reauth/provision flows without embedding HA logic in the library.
- Developer tools (e.g., smoketest) may optionally run provisioning by accepting
  credentials from environment variables, but this remains a tooling concern.

## Notes

This DDR intentionally prevents `session.py` from embedding any callback/UI
mechanism to request access code/passphrase, preserving clean layering and
reusability outside of Home Assistant.
