Runtime configuration, read from AP_-prefixed environment variables.
The variable surface is defined by docs/audio-proxy-api-v1.md §6. Values are
parsed and validated exactly once, during AudioProxy.Application.start/2, and
stored in :persistent_term — reads on the request path are then a cheap
global lookup with no GenServer in the way.
Malformed values raise AudioProxy.Config.Error at boot, so a misconfigured
container fails immediately instead of serving traffic with surprising
defaults. Every error message names the offending variable.
Variables
| Variable | Type | Default |
|---|---|---|
AP_KEY | hex-encoded binary, ≥ 32 bytes decoded | unset (nil) |
AP_SALT | hex-encoded binary | unset (nil) |
AP_ALLOW_INSECURE | boolean | false |
AP_SOURCE_ALLOWLIST | comma-separated list | [] |
AP_LOCAL_ROOT | existing directory, not / | unset (nil) — local sources disabled |
AP_VARIANT_STORE | scheme-tagged URL (file:///path, s3://bucket) | unset (nil) — no variant cache |
AP_MAX_CONCURRENCY | positive integer | System.schedulers_online/0 |
AP_MAX_PROBE_CONCURRENCY | positive integer | 4 × AP_MAX_CONCURRENCY |
AP_QUEUE_SIZE | non-negative integer | 32 |
AP_READY_QUEUE_THRESHOLD | non-negative integer, ≤ AP_QUEUE_SIZE | half the queue, floored, min 1 (0 disables) |
AP_MAX_SRC_BYTES | positive integer | 2_000_000_000 |
AP_MAX_VARIANT_BYTES | positive integer | the effective AP_MAX_SRC_BYTES |
AP_RENDER_TIMEOUT | positive integer (seconds) | 300 |
AP_PROBE_TIMEOUT | positive integer (seconds) | 10 |
AP_SERVE_MODE | redirect | proxy | :redirect |
AP_PRESIGN_TTL | positive integer (seconds) | 300 |
AP_LOG_LEVEL | debug | info | warning | error | :info |
AP_METRICS_BIND | IP address literal | 127.0.0.1 |
AP_METRICS_PORT | positive integer | 9568 |
AP_S3_ENDPOINT | http(s)://host[:port] | unset (nil) — AWS proper |
The listener port is read from AP_PORT, falling back to PORT (which the
worktree workflow sets to the branch's hashed port), then to 4000.
The AWS variables
S3 credentials are the one thing here not AP_-prefixed:
AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN and
AWS_REGION (or AWS_DEFAULT_REGION). Every tool that produces credentials
already writes those names, and renaming them would mean an operator
translating a working environment into this proxy's dialect for no gain.
They are read here rather than left to ex_aws's own resolution, which
would otherwise reach into application env and, on EC2, into IMDS. Keeping
them in this map means the whole configuration surface is still one thing,
validated once, at boot — and that what the proxy will use is what the
operator set, not whatever a metadata endpoint happened to answer.
AudioProxy.S3 hands them to ex_aws per request.
Validated all-or-nothing: a half-configured client fails at its first request instead of at boot, which is the failure this module exists to prevent.
Summary
Functions
Returns the whole stored config.
Builds a validated config map from an environment map, without storing it.
Returns one stored config value.
Parses and validates the environment, then stores the result for all/0.
The levels AP_LOG_LEVEL accepts.
Replaces the stored config wholesale.
The addressing styles AP_S3_ADDRESSING accepts.
The serve modes AP_SERVE_MODE accepts.
Types
@type s3() :: %{ region: String.t() | nil, access_key_id: String.t() | nil, secret_access_key: String.t() | nil, session_token: String.t() | nil, endpoint: URI.t() | nil, addressing: :virtual | :path, ca_bundle: String.t() | nil }
The S3 settings, as one group.
All of region, access_key_id and secret_access_key are nil together
when S3 is not configured — the partial set is refused at boot, so anything
that finds one of them finds all three.
addressing and ca_bundle are independent of the credentials: both have a
usable value whether or not S3 is configured at all.
@type t() :: %{ port: pos_integer(), key: binary() | nil, salt: binary() | nil, allow_insecure: boolean(), source_allowlist: [String.t()], local_root: String.t() | nil, variant_store: AudioProxy.VariantStore.config() | nil, max_concurrency: pos_integer(), max_probe_concurrency: pos_integer(), queue_size: non_neg_integer(), ready_queue_threshold: non_neg_integer(), max_src_bytes: pos_integer(), max_variant_bytes: pos_integer(), render_timeout: pos_integer(), probe_timeout: pos_integer(), serve_mode: :redirect | :proxy, presign_ttl: pos_integer(), log_level: :debug | :info | :warning | :error, metrics_bind: :inet.ip_address(), metrics_port: pos_integer(), s3: s3() }
Functions
@spec all() :: t()
Returns the whole stored config.
Builds a validated config map from an environment map, without storing it.
This is the function tests exercise for parsing and validation. It writes
nothing to :persistent_term; it does touch the filesystem where a value
is a filesystem claim — AP_LOCAL_ROOT must be an existing directory,
and a file:// variant store is probed for writability.
It does not touch the network. An s3:// variant store is parsed and
shape-checked here and probed by load!/1, after the config is stored —
because the probe is an S3 request, and AudioProxy.S3 reads its
credentials, endpoint and addressing from the very map being built. The
asymmetry with the file:// probe is that one: a path is provable from
itself, a bucket is not.
Returns one stored config value.
Parses and validates the environment, then stores the result for all/0.
Called from AudioProxy.Application.start/2. Raises Error on the first
unusable value.
@spec log_levels() :: [atom()]
The levels AP_LOG_LEVEL accepts.
Replaces the stored config wholesale.
Used by load!/1 at boot and by AudioProxy.ConfigHelper in tests. Nothing on
the request path should call this.
@spec s3_addressing_styles() :: [atom()]
The addressing styles AP_S3_ADDRESSING accepts.
@spec serve_modes() :: [atom()]
The serve modes AP_SERVE_MODE accepts.