LemonCore.Secrets.KeyProvider behaviour (lemon_core v0.1.0)

View Source

Behaviour for master key providers.

A provider knows how to read (and optionally write) the encoded master key from one storage location: the macOS Keychain, an environment variable, a file on disk, or anything a host application plugs in.

LemonCore.Secrets.MasterKey walks the configured providers in order and uses the first one that yields usable key material.

Configuration

config :lemon_core, LemonCore.Secrets,
  key_providers: [:keychain, :env, :file],
  key_file: "~/.lemon/secrets_master_key",
  env_var: "LEMON_SECRETS_MASTER_KEY"

:keychain, :env and :file are shorthands for the built-in providers in LemonCore.Secrets.KeyProvider.*; any other atom is treated as a module name implementing this behaviour. Every setting can also be overridden per call by passing the same key in the options given to LemonCore.Secrets functions.

Callbacks

fetch/1 returns the encoded key material (usually base64), not the raw key — decoding and validation happen in LemonCore.Secrets.MasterKey.

Providers signal "nothing stored here, try the next one" with {:error, :missing} and "this backend does not exist on this machine" with {:error, :unavailable}. Any other error is treated as a real failure and reported once the chain is exhausted.

Summary

Callbacks

Whether this provider can be used on this machine.

Reads the encoded master key.

Short name for the provider, used as the source in resolve results.

What to do when this provider holds unusable key material.

Writes the encoded master key. Used by LemonCore.Secrets.MasterKey.init/1.

Functions

Absolute path of the master key file, or nil when it cannot be determined.

Looks a setting up in opts, then in config :lemon_core, LemonCore.Secrets.

Returns the provider modules to try, in order.

Types

opts()

@type opts() :: keyword()

Callbacks

available?(opts)

(optional)
@callback available?(opts()) :: boolean()

Whether this provider can be used on this machine.

fetch(opts)

@callback fetch(opts()) :: {:ok, String.t()} | {:error, term()}

Reads the encoded master key.

name()

@callback name() :: atom()

Short name for the provider, used as the source in resolve results.

on_invalid()

(optional)
@callback on_invalid() :: :halt | :continue

What to do when this provider holds unusable key material.

:halt (the default) reports the error immediately — an explicitly configured but broken key is a user error worth surfacing. :continue moves on to the next provider, which is what the keychain does so that a stale entry cannot lock you out.

put(t, opts)

(optional)
@callback put(String.t(), opts()) :: :ok | {:error, term()}

Writes the encoded master key. Used by LemonCore.Secrets.MasterKey.init/1.

Functions

available?(provider, opts)

@spec available?(module(), opts()) :: boolean()

default_order()

@spec default_order() :: [atom()]

env_getter(opts)

@spec env_getter(opts()) :: (String.t() -> String.t() | nil)

env_var(opts \\ [])

@spec env_var(opts()) :: String.t()

exports?(module, fun, arity)

@spec exports?(module(), atom(), arity()) :: boolean()

home_dir(opts)

@spec home_dir(opts()) :: String.t() | nil

key_file(opts)

@spec key_file(opts()) :: Path.t() | nil

Absolute path of the master key file, or nil when it cannot be determined.

Defaults to ~/.lemon/secrets_master_key; :key_file (opts or app env) overrides it and may itself start with ~.

name(provider)

@spec name(module()) :: atom()

on_invalid(provider)

@spec on_invalid(module()) :: :halt | :continue

option(opts, key, default)

@spec option(opts(), atom(), term()) :: term()

Looks a setting up in opts, then in config :lemon_core, LemonCore.Secrets.

order(opts \\ [])

@spec order(opts()) :: [module()]

Returns the provider modules to try, in order.

Reads :key_providers from opts first, then from app env, falling back to the historical order (keychain, then env var, then key file).

supports_put?(provider)

@spec supports_put?(module()) :: boolean()