reckon_db_integrity_key (reckon_db v2.3.1)
View SourcePer-store HMAC key loader for the tamper-resistance layer.
Loads the integrity HMAC key for a store at startup, validates it, and stores it under a persistent_term key so the write/read paths can access it without process-state coupling. Refuses to start the store if integrity is configured but the key cannot be loaded.
Key sources
Two sources supported in 2.1.0, in priority order:
- Environment variable: {env_var, <<"VAR_NAME">>} - value is base64-decoded into the key bytes.
- Sealed file: {sealed_file, "/path/to/key"} - file mode must be 0600 (refused otherwise); content read raw (no encoding).
A future 2.2.0 release will add Vault/KMS providers and per-store keyrings for rotation; the {env_var, _} and {sealed_file, _} sources will remain supported.
Validation
The key must be exactly 32 bytes (256 bits) after decoding. Anything else is a configuration error and the load fails. This matches the standard HMAC-SHA256 key size.
Storage
The loaded key is placed in persistent_term under the key {reckon_db, integrity_key, StoreId}. The integrity_enabled flag is placed under {reckon_db, integrity_enabled, StoreId} as a boolean. Callers should NOT read the key directly via persistent_term - they should use get/1 to keep the lookup pattern stable.
The key never appears in logs, error tuples, telemetry events, or process state dumps. This is enforced by convention; reviewers should reject any code that violates it.
Summary
Functions
Remove all integrity state for a store from persistent_term.
Retrieve the loaded HMAC key for a store.
Whether integrity is enabled for a store.
Load the integrity key for a store at startup.
Types
-type integrity_config() :: disabled | #{enabled := true, key_source := integrity_key_source()}.
-type integrity_key_source() :: {env_var, EnvName :: binary()} | {sealed_file, Path :: file:filename()}.
-type load_error() :: {error, {integrity_key_invalid_size, ActualBytes :: non_neg_integer()}} | {error, {integrity_key_env_var_not_set, EnvName :: binary()}} | {error, {integrity_key_env_var_not_base64, EnvName :: binary()}} | {error, {integrity_key_file_not_readable, Path :: file:filename(), Reason :: term()}} | {error, {integrity_key_file_insecure_mode, Path :: file:filename(), Mode :: integer()}}.
-type store_config() :: #store_config{store_id :: atom(), data_dir :: string(), mode :: single | cluster, timeout :: pos_integer(), writer_pool_size :: pos_integer(), reader_pool_size :: pos_integer(), gateway_pool_size :: pos_integer(), options :: map(), integrity :: integrity_config()}.
Functions
-spec clear(StoreId :: atom()) -> ok.
Remove all integrity state for a store from persistent_term.
Intended for store shutdown and for test isolation. Production code should not call this in the hot path.
Retrieve the loaded HMAC key for a store.
Returns the key binary if integrity is enabled and the key is loaded; returns 'undefined' if integrity is disabled for the store or the key has not been loaded (which should never happen if startup ordering is correct).
Callers in the write/read path call this on every operation; the persistent_term lookup is sub-microsecond.
Whether integrity is enabled for a store.
Reads a separate persistent_term flag so callers can quickly short-circuit on the disabled-store case without inspecting the (sensitive) key binary.
-spec load(store_config()) -> ok | load_error().
Load the integrity key for a store at startup.
Reads the store config, and if integrity is enabled, fetches the key from the configured source, validates it, and installs it in persistent_term. Idempotent: re-loading a store overwrites the previously-loaded key.
Returns 'ok' on success (including the case where integrity is disabled - nothing to do), or a structured error.