reckon_db_integrity_key (reckon_db v2.2.2)

View Source

Per-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:

  1. Environment variable: {env_var, <<"VAR_NAME">>} - value is base64-decoded into the key bytes.
  2. 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

integrity_config/0

-type integrity_config() :: disabled | #{enabled := true, key_source := integrity_key_source()}.

integrity_key_source/0

-type integrity_key_source() :: {env_var, EnvName :: binary()} | {sealed_file, Path :: file:filename()}.

load_error/0

-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()}}.

store_config/0

-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

clear(StoreId)

-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.

get(StoreId)

-spec get(StoreId :: atom()) -> binary() | undefined.

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.

is_enabled(StoreId)

-spec is_enabled(StoreId :: atom()) -> boolean().

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.

load(Store_config)

-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.