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
Credo check).
The brand theme 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. The :schema name is cached the same way — warmed at boot
by validate_at_boot!/0, with a self-healing backfill (schema/0) that
re-reads and re-validates from the Application env for boot-skipped contexts.
Options
:feedback_id- Optional Feedback-ID prefix (RFC 8058/Deliverability). When set, auto-populates as{sender_id}:{mailable}:{tenant_id}:{stream}. The default value isnil.:repo- The adopter's Ecto.Repo module. Required from + onwards. The default value isnil.:schema(String.t/0) - Names the Postgres SCHEMA holding all mailglass domain tables."public"is the explicit pre-2.0 opt-out. Must be a valid unquoted Postgres identifier. The default value is"mailglass".:adapter(term/0) - Adapter module or{module, opts}tuple. Default: the Fake adapter. The default value is{Mailglass.Adapters.Fake, []}.:adapters(list ofterm/0) - Optional named adapter registry for runtime route refs. Each entry is{ref, module}or{ref, {module, opts}}, whererefis an atom or string. The default value is[].:theme(keyword/0) - Brand theme tokens. SeeMailglass.Components.Theme. The default value is[].:colors(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(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(keyword/0) - Telemetry options. The default value is[].:default_logger(boolean/0) - Attach the default logger handler at boot. Default:false. The default value isfalse.
:renderer(keyword/0) - Renderer options. The default value is[].:css_inliner- CSS inlining backend. Default::premailex. The default value is:premailex.:plaintext(boolean/0) - Auto-generate a plaintext body alongside the HTML body. Default:true. The default value istrue.
:tenancy- Module implementingMailglass.Tenancy. Default:nil(single-tenant mode). The default value isnil.:suppression_store- Module implementingMailglass.SuppressionStore. Default:Mailglass.SuppressionStore.Ecto. The default value isMailglass.SuppressionStore.Ecto.:async_adapter- Async delivery adapter fordeliver_later/2.:obanis the default durable selection and fails closed with a typed error when it is unavailable or unready; it never changes adapters.:task_supervisoris explicitly selected, non-durable development/test behavior and is rejected byproduction_readiness/0. The default value is:oban.:rate_limit(keyword/0) - Rate-limiter configuration. The default value is[].:tracking(keyword/0) - Open/click tracking configuration (TRACK-03). When any mailable enables opens or clicks,:hostis 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/0falls back to:adapter_endpoint. The default value isnil.:host- Tracking subdomain (e.g.track.example.com). Must be separate from the adopter's main app host. The default value isnil.:scheme- URL scheme.httponly for dev. The default value is"https".:salts(list ofString.t/0) - Phoenix.Token salts. Head signs; all verify (rotation support). The default value is[].:max_age(pos_integer/0) - Token max age in seconds. Default: 2 years. The default value is63072000.
:compliance(keyword/0) - RFC 8058 unsubscribe configuration. reads this subtree throughMailglass.Configaccessors 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/0falls back to the tracking endpoint chain. The default value isnil.:host- Canonical host used for unsubscribe URLs (for exampleunsubscribe.example.com). The default value isnil.:scheme- Unsubscribe URL scheme.httpis intended for local development only. The default value is"https".:mount_path(String.t/0) - Absolute path prefix used when generating unsubscribe URLs. The default value is"/mailglass/unsubscribe".:previous_secrets(list ofString.t/0) - Raw priorsecret_key_basevalues 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 isnil.:max_age(pos_integer/0) - Unsubscribe token max age in seconds. Default: 2 years. The default value is63072000.:lifecycle(atom/0) - Module implementingMailglass.Lifecycle. For one-click unsubscribe, Mailglass invokes the compatible callback after the primary convergence commits and runs its returned Multi as a separate, best-effort transaction; its failure cannot change the committed empty-200 result. The default value isMailglass.Lifecycle.Noop.
:clock- Module implementingutc_now/0. Default:Mailglass.Clock.System. Tests useMailglass.Clock.Frozen-backed per-process freezing without overriding this key. The default value isnil.:postmark(keyword/0) - Postmark webhook configuration. The default value is[].:enabled(boolean/0) - Enable the Postmark webhook route. Default:true. The default value istrue.:basic_auth- Basic Auth{user, password}tuple. Required for signature verification; omit only if the provider is disabled. The default value isnil.:ip_allowlist(list ofString.t/0) - Opt-in list of CIDR strings (e.g.["50.31.156.0/24"]). Off by default per — Postmark's origin IPs can change. The default value is[].
:sendgrid(keyword/0) - SendGrid webhook configuration. The default value is[].:enabled(boolean/0) - Enable the SendGrid webhook route. Default:true. The default value istrue.: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 isnil.:timestamp_tolerance_seconds(pos_integer/0) - Replay tolerance window in seconds. Default:300(Stripe / Svix / Standard Webhooks consensus). The default value is300.
:mailgun(keyword/0) - Mailgun webhook configuration. The default value is[].:enabled(boolean/0) - Enable the Mailgun webhook route when explicitly mounted. The default value istrue.:signing_key- Mailgun webhook signing key used for HMAC verification. Required for signature verification; omit only if the provider is disabled. The default value isnil.:timestamp_tolerance_seconds(pos_integer/0) - Maximum accepted age for the Mailgun signature timestamp in seconds. Default:28_800. The default value is28800.:future_skew_seconds(pos_integer/0) - Maximum accepted future skew for the Mailgun signature timestamp in seconds. Default:300. The default value is300.:replay_cache_ttl_seconds(pos_integer/0) - Replay cache retention window for Mailgun tokens in seconds. Default:28_800. The default value is28800.
:ses(keyword/0) - SES webhook configuration. The default value is[].:enabled(boolean/0) - Enable the SES webhook route when explicitly mounted. The default value istrue.:cert_cache_ttl_seconds(pos_integer/0) - TTL in seconds for cached SNS signing certificates. Default:86_400. The default value is86400.
:resend(keyword/0) - Resend webhook configuration. The default value is[].:enabled(boolean/0) - Enable the Resend webhook route when explicitly mounted. The default value istrue.:secret- Svix webhook secret used for Resend signature verification. Required for verification; omit only if the provider is disabled. The default value isnil.:timestamp_tolerance_seconds(pos_integer/0) - Maximum accepted age for the Svix timestamp in seconds. Default:300. The default value is300.
:webhook_retention(keyword/0) - Retention policy formailglass_webhook_eventsrows. The default value is[].:succeeded_days- Days to retain:succeededwebhook_events before the Pruner deletes them. Set to:infinityto disable. Default: 14. The default value is14.:dead_days- Days to retain:dead(terminal-after-retries) webhook_events before the Pruner deletes them. Set to:infinityto disable. Default: 90. The default value is90.:failed_days- Days to retain:failed(investigatable) webhook_events. Default::infinity(never prune). The default value is:infinity.
:outbound_payload_retention(keyword/0) - Finite retention policy for private outbound payload content. Expired payloads retain a contentless audit tombstone. The default value is[].:terminal_days(pos_integer/0) - Days to retain terminal, discarded, and abandoned payload content. Default:14. The default value is14.:uncertain_days(pos_integer/0) - Days to retain acceptance-uncertain payload content for reconciliation. Default:30. The default value is30.:legacy_days(pos_integer/0) - Days to retain recognizable legacy queued payload content. Default:14. The default value is14.:prune_batch_size(pos_integer/0) - Maximum payload tombstones transitioned by one explicit tenant prune call. Default:500. The default value is500.
:outbound_payload_maintenance- Production payload-maintenance mode. Set:scheduledwhen a host scheduler enqueues tenant-explicitMailglass.Outbound.PayloadPrunerWorkerjobs, or:manualwhen an operator runsmix mailglass.outbound.payloads.prune --tenant TENANT_IDfrom system cron. The default value is:none.:operator(keyword/0) - The default value is[].:auth- Adopter-ownedMailglassAdmin.Authcallback used by the production operator router mount. The default value isnil.
Boot sequence
# lib/mailglass/application.ex
def start(_type, _args) do
Mailglass.Config.validate_at_boot!()
# ...
endRaises NimbleOptions.ValidationError on invalid configuration. Raising at
boot is intentional — a misconfigured mailer should never limp into
production serving half-rendered mail.
Raise on invalid configuration so misconfigured mailers fail fast at boot.
Summary
Functions
Returns the validated named adapter registry keyed by stable route ref.
Returns the validated compliance subtree with defaults applied.
Resolves the current unsubscribe signing endpoint.
Returns the validated global default adapter as {module, opts}.
Returns the cached brand theme keyword list.
Validates and returns a keyword list of options.
Returns the adopter-owned production operator authorization callback, if
one was configured. The callback must implement authorize/2 when the
production preflight is run.
Returns the declared payload-maintenance operating mode.
Verifies that the explicitly selected async adapter is suitable for a durable production deployment.
Returns the validated renderer subtree with defaults applied.
Resolves a named adapter ref into the normalized {module, opts} shape.
Returns the configured Postgres schema name (default "mailglass").
Reads the :mailglass Application env, validates it against the schema,
and caches the brand theme in :persistent_term.
Functions
Returns the validated named adapter registry keyed by stable route ref.
@spec compliance() :: keyword()
Returns the validated compliance subtree with defaults applied.
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.
@spec compliance_host() :: String.t() | nil
@spec compliance_lifecycle() :: module()
@spec compliance_max_age() :: pos_integer()
@spec compliance_mount_path() :: String.t()
@spec compliance_previous_secrets() :: [String.t()]
@spec compliance_redirect() :: String.t() | nil
@spec compliance_scheme() :: String.t()
Returns the validated global default adapter as {module, opts}.
@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).
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, []}
@spec operator_auth() :: module() | nil
Returns the adopter-owned production operator authorization callback, if
one was configured. The callback must implement authorize/2 when the
production preflight is run.
@spec outbound_payload_maintenance() :: :scheduled | :manual | :none
Returns the declared payload-maintenance operating mode.
Production readiness requires either :scheduled (the host schedules
tenant-explicit pruner jobs) or :manual (the documented bounded Mix task
is owned by system cron or an equivalent operator runbook). :none is the
safe default for ordinary application boot and fails production preflight.
@spec production_readiness() :: :ok | {:error, Mailglass.ConfigError.t()}
Verifies that the explicitly selected async adapter is suitable for a durable production deployment.
This is an operator-invoked preflight, deliberately separate from
validate_at_boot!/0: development and test applications may explicitly use
the non-durable :task_supervisor adapter.
@spec renderer() :: keyword()
Returns the validated renderer subtree with defaults applied.
Resolves a named adapter ref into the normalized {module, opts} shape.
@spec schema() :: String.t()
Returns the configured Postgres schema name (default "mailglass").
Serves from :persistent_term in O(1) on the hot path. Warmed at boot by
validate_at_boot!/0; on a cold-cache miss (a boot-skipped context) it
self-heals by re-reading and re-validating the :schema Application env,
then caching the validated value. "public" is the explicit pre-2.0 opt-out.
Unlike get_theme/0, this never returns a silent default on a miss — the
value is always validated once at the cache-write boundary so an adopter's
schema: "public" opt-out can never be masked.
@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.