LemonCore.Env (lemon_core v0.1.0)

View Source

Typed, declarative environment-variable registry for Lemon.

This module is the framework: typing, aliases, defaults, resolution and redaction-safe reporting. The declarations themselves belong to whichever app reads the variable — each app ships a LemonCore.Env.Registry module and the runtime lists them under :env_registries (see registries/0). all_declared/0 aggregates whatever is loaded, so a build with only some of the umbrella's apps reports exactly the variables its code can read.

It does not yet replace the scattered System.get_env/1 call sites across the umbrella -- callers migrate to LemonCore.Env.get/2 in a later phase. Until then, this registry is the living documentation: see docs/config-registry.md for the reference table.

Usage

# Look up a declared variable by name, applying its declared type,
# aliases, and default:
LemonCore.Env.get(:lemon_arena_poker_models)
#=> ["anthropic:claude-sonnet-4-20250514", "openai:gpt-5"]

LemonCore.Env.get(:lemon_web_port)
#=> 4080

# Raw (undeclared) typed reads, e.g. for one-off/local variables that
# don't warrant a registry entry:
LemonCore.Env.int("SOME_TIMEOUT_MS", 5_000)
LemonCore.Env.bool("SOME_FLAG", false)
LemonCore.Env.list("SOME_HOSTS")

# Every declared variable, for tooling (e.g. `mix lemon.doctor`) or
# generating docs:
LemonCore.Env.all_declared()

Declaration shape

Each declared variable is a map with:

  • :name - atom key used with get/2, e.g. :lemon_web_port
  • :env_var - the canonical environment variable name
  • :aliases - legacy/fallback environment variable names checked (in order) if :env_var is unset. Existing grandfathered non-conforming names live here rather than as the primary :env_var -- see the naming convention section of docs/config-registry.md.
  • :type - one of :string, :integer, :float, :boolean, :list, :bytes (parsed via LemonCore.Config.Helpers.get_env_bytes/2)
  • :default - value used when nothing resolves from the environment
  • :doc - one-line human-readable description
  • :secret? - whether the value should be redacted in any reporting surface (see LemonCore.Env.Resolved)
  • :required? - reserved for call-site opt-in; get/2 also accepts a per-call required: true option independent of this flag
  • :area - a coarse grouping used to organize docs/config-registry.md
  • :apps - umbrella app(s) that read this variable today

Type casting

Casting is delegated to LemonCore.Config.Helpers, the umbrella's existing env-parsing toolkit, so behavior (bool truthy/falsy spellings, duration/byte-size suffixes, list delimiters) stays consistent with every other config reader in the codebase.

Summary

Functions

Returns every declared environment variable (name, env var, type, default, doc, secret?/required? flags, area, and owning apps).

Gets a raw (undeclared) boolean environment variable.

Returns the declarations for a single :area (e.g. :agent, :gateway, :arena). See all_declared/0 for the full list of areas in use.

Returns the declaration for name, or nil if nothing is declared under that name.

Resolves a declared environment variable by its registry name.

Gets a raw (undeclared) integer environment variable.

Gets a raw (undeclared) list environment variable, split on delimiter.

The registry modules consulted by all_declared/0, in order.

Returns a redaction-safe snapshot of every declared variable's current resolved value, tagged with its resolution :source (:env, :alias, or :default). Secret-flagged values are redacted whenever the snapshot is inspected/logged (see LemonCore.Env.Resolved).

Gets an optional raw (undeclared) string environment variable.

Types

declaration()

@type declaration() :: %{
  name: atom(),
  env_var: String.t(),
  aliases: [String.t()],
  type: var_type(),
  default: term(),
  doc: String.t(),
  secret?: boolean(),
  required?: boolean(),
  area: atom(),
  apps: [atom()]
}

var_type()

@type var_type() :: :string | :integer | :float | :boolean | :list | :bytes

Functions

all_declared()

@spec all_declared() :: [declaration()]

Returns every declared environment variable (name, env var, type, default, doc, secret?/required? flags, area, and owning apps).

This is the source of truth behind docs/config-registry.md and is intended for tooling (e.g. a future mix lemon.doctor check) as well as interactive exploration.

bool(env_var, default \\ false)

@spec bool(String.t(), boolean()) :: boolean()

Gets a raw (undeclared) boolean environment variable.

by_area(area)

@spec by_area(atom()) :: [declaration()]

Returns the declarations for a single :area (e.g. :agent, :gateway, :arena). See all_declared/0 for the full list of areas in use.

describe(name)

@spec describe(atom()) :: declaration() | nil

Returns the declaration for name, or nil if nothing is declared under that name.

get(name, opts \\ [])

@spec get(
  atom(),
  keyword()
) :: term()

Resolves a declared environment variable by its registry name.

Resolution order: env_var -> each of aliases (in declared order) -> opts[:default] -> the declaration's own :default. The resolved value is cast according to the declaration's :type.

Options

  • :default - override the declaration's default for this call
  • :required - if true (or if the declaration has required?: true), raises ArgumentError when the value resolves to nil/""/[]

Examples

iex> LemonCore.Env.get(:lemon_web_port)
4080

iex> System.put_env("LEMON_ARENA_POKER_MODELS", "anthropic:claude-sonnet-4-20250514")
iex> LemonCore.Env.get(:lemon_arena_poker_models)
["anthropic:claude-sonnet-4-20250514"]

int(env_var, default \\ 0)

@spec int(String.t(), integer()) :: integer()

Gets a raw (undeclared) integer environment variable.

list(env_var, delimiter \\ ",")

@spec list(String.t(), String.t()) :: [String.t()]

Gets a raw (undeclared) list environment variable, split on delimiter.

registries()

@spec registries() :: [module()]

The registry modules consulted by all_declared/0, in order.

Defaults to lemon_core's own declarations so the library works unconfigured; the reference runtime lists every app's registry in config/config.exs. Modules that are not loaded are skipped rather than raising.

snapshot()

@spec snapshot() :: [LemonCore.Env.Resolved.t()]

Returns a redaction-safe snapshot of every declared variable's current resolved value, tagged with its resolution :source (:env, :alias, or :default). Secret-flagged values are redacted whenever the snapshot is inspected/logged (see LemonCore.Env.Resolved).

string(env_var, default \\ nil)

@spec string(String.t(), String.t() | nil) :: String.t() | nil

Gets an optional raw (undeclared) string environment variable.