# `Ankusa.Config`
[🔗](https://github.com/jamescarr/ankusa/blob/ankusa-v0.7.0/packages/ankusa/lib/ankusa/config.ex#L1)

The configuration struct passed down the supervision tree at start. No
`Application.get_env/2` buried in call sites — instance-scoped config falls
out of this for free.

# `t`

```elixir
@type t() :: %Ankusa.Config{
  admin: term(),
  archive: term(),
  audit: term(),
  backup: term(),
  batcher: term(),
  claim_check: term(),
  data_dir: term(),
  dedupe: term(),
  direct_publish_timeout_ms: term(),
  dispatch: term(),
  egress: term(),
  fleet: term(),
  instance: term(),
  intake: term(),
  lifecycle: %{sinks: [{module(), keyword()}]},
  listener: term(),
  max_body_bytes: term(),
  metrics: term(),
  port: term(),
  quarantine: term(),
  queue: term(),
  rate_limits: term(),
  roles: term(),
  route_resolver: term(),
  routes: term(),
  secrets: term(),
  source_store: term(),
  storage: term(),
  store_headers: term(),
  tenants: term(),
  wal: term()
}
```

# `listen_ip!`

```elixir
@spec listen_ip!(term(), String.t()) :: :inet.ip_address()
```

The address a listener binds: a strict IPv4/IPv6 literal string, or an `:inet` address tuple.

# `new`

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

Build a `%Ankusa.Config{}` from a keyword list, deep-merging the map-valued
sections (`:listener`, `:batcher`, `:dispatch`, `:storage`, `:claim_check`,
`:backup`, `:admin`, `:metrics`, `:routes`, `:rate_limits`, `:dedupe`, `:fleet`,
`:quarantine`, `:tenants`, `:lifecycle`, `:egress`, `:secrets`) over the defaults (`egress.pools` is merged one level down). Every `tls` block (`listener.tls`,
`admin.tls`, `routes.admin.tls`, `claim_check.tls`, `metrics.tls`) is
validated and normalised by `Ankusa.Listener.normalize_tls!/2`, and
`listener.num_acceptors: nil` becomes two per scheduler.

`:routes` is nested one level deeper than the rest (`:routes` has its own
`:cache`, `:ip_rules`, and `:admin` sections), so `put_routes/2` merges those
too — `routes.cache.max_size` keeps the other cache keys.

`wal: :none` is normalized last: the roles are settled before `normalize_wal/1`
drops the queue's readers (`:dispatch`, `:storage`) from them.

# `parse_roles!`

```elixir
@spec parse_roles!(String.t()) :: [atom()]
```

Parse a comma-separated `ANKUSA_ROLES`-shaped string into role atoms.
Never calls `String.to_atom/1` — each part must name one of the fixed
roles (`edge`, `dispatch`, `storage`, `claim_check`).

# `path`

```elixir
@spec path(t(), Path.t()) :: Path.t()
```

Absolute path for an instance-scoped data sub-directory.

# `role?`

```elixir
@spec role?(t(), atom()) :: boolean()
```

Is a role enabled for this node?

---

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