# `PhoenixKit.API.Config`
[🔗](https://github.com/BeamLabEU/phoenix_kit/blob/v2.64.1/lib/phoenix_kit/api/config.ex#L1)

Settings accessors for the machine API.

Every function reads a core setting through `PhoenixKit.Settings` and applies a
safe default. The API is **off by default and fails closed**: a switch is on
only when its setting reads exactly `"true"`, so a missing row, an unreadable
database, an unexpected value or a Settings error all count as off.

## Settings

| Key | Default |
|---|---|
| `api_admin_enabled` | `false` |
| `api_public_enabled` | `false` |
| `api_disabled_tools` | `""` (newline separated tool names) |
| `api_disabled_modules` | `""` (newline separated module keys) |
| `api_session_write_modules` | `""` (newline separated module keys) |
| `api_mcp_allowed_origins` | `""` (newline separated origins) |
| `api_admin_preauth_fail_per_min` | `20` |
| `api_admin_rate_per_min` | `600` |
| `api_public_token_rate_per_min` | `120` |
| `api_public_ip_rate_per_min` | `300` |
| `api_public_new_rows_per_ip_hour` | `120` |
| `api_public_new_rows_global_hour` | `2000` |

The three `api_public_token_*` and `api_public_new_rows_*` limits were renamed
when the API was made module-neutral. While the deprecation window lasts
(`PhoenixKit.API.Legacy`) a limit whose new key holds no valid number reads
its old key, then the default.

List settings are stored as newline-separated strings: `PhoenixKit.Settings`
has no list setter and a JSON setting cannot hold a bare list. Use
`encode_list/1` when writing one and `parse_list/1` when reading one.

# `rate_limits`

```elixir
@type rate_limits() :: %{
  preauth_fail_per_min: pos_integer(),
  admin_per_min: pos_integer(),
  public_token_per_min: pos_integer(),
  public_ip_per_min: pos_integer(),
  new_rows_ip_hour: pos_integer(),
  new_rows_global_hour: pos_integer()
}
```

# `admin_enabled?`

```elixir
@spec admin_enabled?() :: boolean()
```

Whether the admin API (`/api/admin/v1`) is switched on. Fails closed.

# `disabled_modules`

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

Module keys whose API tools the install has switched off (`api_disabled_modules`). For
display only, like `disabled_tools/0`: a list that cannot be read is `[]` here.

# `disabled_tools`

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

Tool names the install has hidden (`api_disabled_tools`). For display only: a list that
cannot be read is `[]` here, so request paths (`PhoenixKit.API.Registry`) use
`fetch_disabled_tools/0`, which does not treat it as "nothing is hidden".

# `encode_list`

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

Joins a list into the newline-separated form the list settings are stored in.

# `fetch_disabled_modules`

```elixir
@spec fetch_disabled_modules() :: {:ok, [String.t()]} | :unreadable
```

Module keys whose API tools the install has switched off (`api_disabled_modules`), telling
an empty list from one that could not be read at all. A module that is not in the list has
its API on, so every install starts with all of them on.

The tool registry fails closed on `:unreadable`, as with `fetch_disabled_tools/0`. This is
the one reader of the setting.

# `fetch_disabled_tools`

```elixir
@spec fetch_disabled_tools() :: {:ok, [String.t()]} | :unreadable
```

Tool names the install has hidden (`api_disabled_tools`), telling a list that is empty
from one that could not be read at all (a read that raises or exits).

The tool registry fails closed on `:unreadable`: a tool the admin switched off must not
come back because of a settings problem. This is the one reader of the setting.

# `fetch_session_write_modules`

```elixir
@spec fetch_session_write_modules() :: {:ok, [String.t()]} | :unreadable
```

Module keys whose write tools a person's live session may call
(`api_session_write_modules`), telling an empty list from one that could not be read.

An allow-list that fails closed: a module that is not listed has no write for a session,
so every install starts with none, and the executor treats `:unreadable` as an empty
list. API keys do not read it. This is the one reader of the setting.

# `int`

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

Reads an integer setting.

Returns `default` when the setting is missing, is not a whole number (`"12abc"` and
`"1.5"` are not read by their prefix), is zero or negative, or cannot be read. Every API
integer setting is a positive limit, and a zero limit would silently refuse every
request, so a non-positive value is treated as unset.

# `int`

```elixir
@spec int(String.t(), String.t() | nil, integer()) :: integer()
```

Reads an integer setting that was renamed: the value of `key` when it is a
valid limit (see `int/2`), else the value of `legacy_key` when that is, else
`default`. A `nil` `legacy_key` reads `key` alone.

# `max_body_bytes`

```elixir
@spec max_body_bytes() :: 2_097_152
```

Largest request body the API accepts, in bytes (2 MB).

# `mcp_allowed_origins`

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

Extra origins allowed to call the MCP endpoint (`api_mcp_allowed_origins`).

# `parse_list`

```elixir
@spec parse_list(String.t() | nil) :: [String.t()]
```

Splits a newline-separated setting value into a list.

Lines are trimmed; blank lines and duplicates are dropped. `nil` is `[]`.

# `public_enabled?`

```elixir
@spec public_enabled?() :: boolean()
```

Whether the public API (`/api/public/v1`, MCP) is switched on. Fails closed.

# `rate_limits`

```elixir
@spec rate_limits() :: rate_limits()
```

The rate-limit settings as one map.

`*_per_min` limits count over a 60 second window and `*_hour` limits over a
one hour window (see `PhoenixKit.API.RateLimit`).

# `rate_limits_without_fallback`

```elixir
@spec rate_limits_without_fallback() :: rate_limits()
```

The rate-limit settings as `rate_limits/0` gives them, but reading each key
alone: the deprecated key of a renamed limit is not consulted. The settings
form compares what it is given with this, so that its first save writes a
value that was in effect through a deprecated key under the new key. Goes
away with `PhoenixKit.API.Legacy`.

# `session_write_modules`

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

Module keys whose write tools a person's live session may call
(`api_session_write_modules`, the "Agent may act" boxes of Settings -> API). For display
only, like `disabled_modules/0`: a list that cannot be read is `[]` here.

---

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