Espex.PskStore behaviour (espex v0.6.0)

Copy Markdown View Source

Behaviour for persisting the Noise pre-shared key when Home Assistant provisions or rotates it at runtime via NoiseEncryptionSetKeyRequest.

Espex itself holds the active PSK in Espex.Server and applies a new key to the next connection automatically — but it has no opinion on durability. Without a store, a provisioned key is lost on restart (espex logs a warning and applies it anyway). Configure a :psk_store adapter to own persistence: write the 32 raw bytes wherever your host app keeps secrets (a file, NVS, a database row, a secrets manager), and load them back into :psk / device_config on boot.

Callback

store_psk/1 is invoked with the new 32-byte key before espex applies it. Return :ok to proceed (espex then updates the running server and replies success: true to the client). Return {:error, reason} to abort — espex skips the server update and replies success: false, so a failed write never silently drops the key.

Example

defmodule MyApp.FilePskStore do
  @behaviour Espex.PskStore

  @path "/data/espex_psk.bin"

  @impl Espex.PskStore
  def store_psk(<<_::256>> = psk) do
    File.write(@path, psk)
  end

  @doc "Call on boot to seed device_config from the persisted key."
  def load_psk do
    case File.read(@path) do
      {:ok, <<_::256>> = psk} -> psk
      _ -> nil
    end
  end
end

Wiring

Pass the module as the :psk_store adapter, and seed device_config from it on boot so a previously provisioned key survives restart:

psk = MyApp.FilePskStore.load_psk()

children = [
  {Espex,
   name: MyApp.EspexSup,
   server_name: MyApp.EspexServer,
   device_config: [name: "my-device", psk: psk, accepts_key_provisioning: psk == nil],
   psk_store: MyApp.FilePskStore}
]

Here accepts_key_provisioning is opened only while still keyless, so the plaintext bootstrap path is available exactly until a key exists.

Summary

Callbacks

Persist the newly provisioned 32-byte Noise PSK.

Callbacks

store_psk(psk)

@callback store_psk(psk :: <<_::256>>) :: :ok | {:error, term()}

Persist the newly provisioned 32-byte Noise PSK.

Called before espex applies the key to the running server. Return :ok to proceed or {:error, reason} to abort the update.