# `Mailglass.Config`
[🔗](https://github.com/szTheory/mailglass/blob/v1.0.0/lib/mailglass/config.ex#L1)

Runtime configuration for mailglass, validated at boot via NimbleOptions.

**Only this module may call `Application.compile_env*`.** Every other module
reads configuration through `Application.get_env/2` (enforced by the
`LINT-08` Credo check in Phase 6).

The brand theme (D-19) is cached in `:persistent_term` after validation so
the render hot path reads it in O(1) without re-parsing the Application env
on every message.

## Options

* `:feedback_id` - Optional Feedback-ID prefix (RFC 8058/Deliverability). When set, auto-populates as `{sender_id}:{mailable}:{tenant_id}:{stream}`. The default value is `nil`.

* `:repo` - The adopter's Ecto.Repo module. Required from Phase 2+ onwards. The default value is `nil`.

* `:adapter` (`t:term/0`) - Adapter module or `{module, opts}` tuple. Default: the Fake adapter. The default value is `{Mailglass.Adapters.Fake, []}`.

* `:adapters` (list of `t:term/0`) - Optional named adapter registry for runtime route refs. Each entry is `{ref, module}` or `{ref, {module, opts}}`, where `ref` is an atom or string. The default value is `[]`.

* `:theme` (`t:keyword/0`) - Brand theme tokens. See `Mailglass.Components.Theme`. The default value is `[]`.

  * `:colors` (`t:map/0`) - Brand color map. Keys: `:ink`, `:glass`, `:ice`, `:mist`, `:paper`, `:slate`. The default value is `%{ink: "#0D1B2A", glass: "#277B96", ice: "#A6EAF2", mist: "#EAF6FB", paper: "#F8FBFD", slate: "#5C6B7A"}`.

  * `:fonts` (`t:map/0`) - Font-stack map. Keys: `:body`, `:display`, `:mono`. The default value is `%{display: "'Inter Tight', 'Inter', sans-serif", body: "'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif", mono: "'IBM Plex Mono', ui-monospace, monospace"}`.

* `:telemetry` (`t:keyword/0`) - Telemetry options. The default value is `[]`.

  * `:default_logger` (`t:boolean/0`) - Attach the default logger handler at boot. Default: `false`. The default value is `false`.

* `:renderer` (`t:keyword/0`) - Renderer options. The default value is `[]`.

  * `:css_inliner` - CSS inlining backend. Default: `:premailex`. The default value is `:premailex`.

  * `:plaintext` (`t:boolean/0`) - Auto-generate a plaintext body alongside the HTML body. Default: `true`. The default value is `true`.

* `:tenancy` - Module implementing `Mailglass.Tenancy`. Default: `nil` (single-tenant mode). The default value is `nil`.

* `:suppression_store` - Module implementing `Mailglass.SuppressionStore`. Default: `Mailglass.SuppressionStore.Ecto`. The default value is `Mailglass.SuppressionStore.Ecto`.

* `:async_adapter` - Async delivery adapter for `deliver_later/2`. `:oban` (default, durable) or `:task_supervisor` (non-durable fallback). Use `:task_supervisor` to silence the boot warning when Oban is deliberately not in deps. The default value is `:oban`.

* `:rate_limit` (`t:keyword/0`) - Rate-limiter configuration (RATE-01). The default value is `[]`.

  * `:tenant_recipient` (`t:keyword/0`) - Per-{tenant, domain} rate limits. The default value is `[]`.

    * `:default` (`t:keyword/0`) - Default per-{tenant, domain} bucket. The default value is `[capacity: 100, per_minute: 100]`.

    * `:overrides` (list of `t:term/0`) - Per-{tenant, domain} overrides as {{tenant_id, domain}, opts}. The default value is `[]`.

  * `:global_recipient` (`t:keyword/0`) - Global per-recipient domain rate limits. The default value is `[]`.

    * `:default` (`t:keyword/0`) - Default global per-domain bucket. The default value is `[capacity: 1000, per_minute: 1000]`.

    * `:overrides` (list of `t:term/0`) - Global per-domain overrides as {domain, opts}. The default value is `[]`.

  * `:sender_domain` (`t:keyword/0`) - Global per-sender domain rate limits. The default value is `[]`.

    * `:default` (`t:keyword/0`) - Default global per-sender domain bucket. The default value is `[capacity: 500, per_minute: 500]`.

    * `:overrides` (list of `t:term/0`) - Global per-sender domain overrides as {domain, opts}. The default value is `[]`.

* `:tracking` (`t:keyword/0`) - Open/click tracking configuration (TRACK-03). When any mailable enables opens or clicks, `:host` is REQUIRED or boot raises `%ConfigError{type: :tracking_host_missing}`. The default value is `[]`.

  * `:endpoint` - Phoenix.Token endpoint/secret override for open/click tracking. When nil, `Mailglass.Tracking.endpoint/0` falls back to `:adapter_endpoint`. The default value is `nil`.

  * `:host` - Tracking subdomain (e.g. `track.example.com`). Must be separate from the adopter's main app host. The default value is `nil`.

  * `:scheme` - URL scheme. `http` only for dev. The default value is `"https"`.

  * `:salts` (list of `t:String.t/0`) - Phoenix.Token salts. Head signs; all verify (rotation support). The default value is `[]`.

  * `:max_age` (`t:pos_integer/0`) - Token max age in seconds. Default: 2 years. The default value is `63072000`.

* `:compliance` (`t:keyword/0`) - RFC 8058 unsubscribe configuration. Phase 11 reads this subtree through `Mailglass.Config` accessors only so router/controller/token code avoids new direct compile-env lookups. The default value is `[]`.

  * `:endpoint` - Phoenix.Token endpoint/secret override for unsubscribe signing. When nil, `Mailglass.Config.compliance_endpoint/0` falls back to the tracking endpoint chain. The default value is `nil`.

  * `:host` - Canonical host used for unsubscribe URLs (for example `unsubscribe.example.com`). The default value is `nil`.

  * `:scheme` - Unsubscribe URL scheme. `http` is intended for local development only. The default value is `"https"`.

  * `:mount_path` (`t:String.t/0`) - Absolute path prefix used when generating unsubscribe URLs. The default value is `"/mailglass/unsubscribe"`.

  * `:previous_secrets` (list of `t:String.t/0`) - Raw prior `secret_key_base` values accepted during unsubscribe token verification after endpoint-secret rotation. The default value is `[]`.

  * `:redirect` - Optional GET unsubscribe redirect escape hatch (for example `/settings/unsubscribe`). The default value is `nil`.

  * `:max_age` (`t:pos_integer/0`) - Unsubscribe token max age in seconds. Default: 2 years. The default value is `63072000`.

  * `:lifecycle` (`t:atom/0`) - Module implementing `Mailglass.Lifecycle` for transaction-local unsubscribe side effects. The default value is `Mailglass.Lifecycle.Noop`.

* `:clock` - Module implementing `utc_now/0`. Default: `Mailglass.Clock.System`. Tests use `Mailglass.Clock.Frozen`-backed per-process freezing without overriding this key. The default value is `nil`.

* `:postmark` (`t:keyword/0`) - Postmark webhook configuration (HOOK-03). The default value is `[]`.

  * `:enabled` (`t:boolean/0`) - Enable the Postmark webhook route. Default: `true`. The default value is `true`.

  * `:basic_auth` - Basic Auth `{user, password}` tuple. Required for signature verification; omit only if the provider is disabled. The default value is `nil`.

  * `:ip_allowlist` (list of `t:String.t/0`) - Opt-in list of CIDR strings (e.g. `["50.31.156.0/24"]`). Off by default per D-04 — Postmark's origin IPs can change. The default value is `[]`.

* `:sendgrid` (`t:keyword/0`) - SendGrid webhook configuration (HOOK-04). The default value is `[]`.

  * `:enabled` (`t:boolean/0`) - Enable the SendGrid webhook route. Default: `true`. The default value is `true`.

  * `:public_key` - Base64-encoded SubjectPublicKeyInfo DER (NOT PEM — SendGrid's dashboard ships raw DER without `-----BEGIN PUBLIC KEY-----` framing). Required for signature verification; omit only if the provider is disabled. The default value is `nil`.

  * `:timestamp_tolerance_seconds` (`t:pos_integer/0`) - Replay tolerance window in seconds. Default: `300` (Stripe / Svix / Standard Webhooks consensus). The default value is `300`.

* `:mailgun` (`t:keyword/0`) - Mailgun webhook configuration. The default value is `[]`.

  * `:enabled` (`t:boolean/0`) - Enable the Mailgun webhook route when explicitly mounted. The default value is `true`.

  * `:signing_key` - Mailgun webhook signing key used for HMAC verification. Required for signature verification; omit only if the provider is disabled. The default value is `nil`.

  * `:timestamp_tolerance_seconds` (`t:pos_integer/0`) - Maximum accepted age for the Mailgun signature timestamp in seconds. Default: `28_800`. The default value is `28800`.

  * `:future_skew_seconds` (`t:pos_integer/0`) - Maximum accepted future skew for the Mailgun signature timestamp in seconds. Default: `300`. The default value is `300`.

  * `:replay_cache_ttl_seconds` (`t:pos_integer/0`) - Replay cache retention window for Mailgun tokens in seconds. Default: `28_800`. The default value is `28800`.

* `:ses` (`t:keyword/0`) - SES webhook configuration. The default value is `[]`.

  * `:enabled` (`t:boolean/0`) - Enable the SES webhook route when explicitly mounted. The default value is `true`.

  * `:cert_cache_ttl_seconds` (`t:pos_integer/0`) - TTL in seconds for cached SNS signing certificates. Default: `86_400`. The default value is `86400`.

* `:resend` (`t:keyword/0`) - Resend webhook configuration. The default value is `[]`.

  * `:enabled` (`t:boolean/0`) - Enable the Resend webhook route when explicitly mounted. The default value is `true`.

  * `:secret` - Svix webhook secret used for Resend signature verification. Required for verification; omit only if the provider is disabled. The default value is `nil`.

  * `:timestamp_tolerance_seconds` (`t:pos_integer/0`) - Maximum accepted age for the Svix timestamp in seconds. Default: `300`. The default value is `300`.

* `:webhook_retention` (`t:keyword/0`) - Retention policy for `mailglass_webhook_events` rows (HOOK-06). The default value is `[]`.

  * `:succeeded_days` - Days to retain `:succeeded` webhook_events before the Pruner deletes them. Set to `:infinity` to disable. Default: 14. The default value is `14`.

  * `:dead_days` - Days to retain `:dead` (terminal-after-retries) webhook_events before the Pruner deletes them. Set to `:infinity` to disable. Default: 90. The default value is `90`.

  * `:failed_days` - Days to retain `:failed` (investigatable) webhook_events. Default: `:infinity` (never prune). The default value is `:infinity`.

## Boot sequence

    # lib/mailglass/application.ex
    def start(_type, _args) do
      Mailglass.Config.validate_at_boot!()
      # ...
    end

Raises `NimbleOptions.ValidationError` on invalid configuration. Raising at
boot is intentional — a misconfigured mailer should never limp into
production serving half-rendered mail.

# `adapters`
*since 0.4.0* 

```elixir
@spec adapters() :: %{optional(atom() | String.t()) =&gt; {module(), keyword()}}
```

Returns the validated named adapter registry keyed by stable route ref.

# `compliance`
*since 0.1.0* 

```elixir
@spec compliance() :: keyword()
```

Returns the validated compliance subtree with defaults applied.

# `compliance_endpoint`
*since 0.1.0* 

```elixir
@spec compliance_endpoint() :: module() | binary()
```

Resolves the current unsubscribe signing endpoint.

Falls back to `Mailglass.Tracking.endpoint/0` so unsubscribe tokens reuse the
same endpoint-secret chain unless adopters opt into a compliance-specific
override.

# `compliance_host`
*since 0.1.0* 

```elixir
@spec compliance_host() :: String.t() | nil
```

# `compliance_lifecycle`
*since 0.1.0* 

```elixir
@spec compliance_lifecycle() :: module()
```

# `compliance_max_age`
*since 0.1.0* 

```elixir
@spec compliance_max_age() :: pos_integer()
```

# `compliance_mount_path`
*since 0.1.0* 

```elixir
@spec compliance_mount_path() :: String.t()
```

# `compliance_previous_secrets`
*since 0.1.0* 

```elixir
@spec compliance_previous_secrets() :: [String.t()]
```

# `compliance_redirect`
*since 0.1.0* 

```elixir
@spec compliance_redirect() :: String.t() | nil
```

# `compliance_scheme`
*since 0.1.0* 

```elixir
@spec compliance_scheme() :: String.t()
```

# `default_adapter`
*since 0.4.0* 

```elixir
@spec default_adapter() :: {module(), keyword()}
```

Returns the validated global default adapter as `{module, opts}`.

# `get_theme`
*since 0.1.0* 

```elixir
@spec get_theme() :: keyword()
```

Returns the cached brand theme keyword list.

Requires `validate_at_boot!/0` to have been called first. Returns an empty
list if the cache is unset (the caller is responsible for ensuring the boot
sequence has completed).

# `new!`
*since 0.1.0* 

```elixir
@spec new!(keyword()) :: keyword()
```

Validates and returns a keyword list of options.

Fills in defaults, raises `NimbleOptions.ValidationError` on unknown keys
or invalid values. Used primarily by `validate_at_boot!/0`; callers rarely
invoke this directly.

## Examples

    iex> config = Mailglass.Config.new!([])
    iex> Keyword.fetch!(config, :adapter)
    {Mailglass.Adapters.Fake, []}

# `resolve_adapter_ref`
*since 0.4.0* 

```elixir
@spec resolve_adapter_ref(atom() | String.t()) :: {module(), keyword()}
```

Resolves a named adapter ref into the normalized `{module, opts}` shape.

# `validate_at_boot!`
*since 0.1.0* 

```elixir
@spec validate_at_boot!() :: :ok
```

Reads the `:mailglass` Application env, validates it against the schema,
and caches the brand theme in `:persistent_term`.

Called from `Mailglass.Application.start/2`. Raises
`NimbleOptions.ValidationError` if the Application env is invalid.

When `[telemetry: [default_logger: true]]` is configured, the default
logger handler is attached here.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
