> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tai42.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Config and secrets

> Swappable config and secret providers.

Configuration and secrets back a server's environment and settings. A built-in
file provider ships by default, and the store is swappable behind one contract, so
a server can read its config from a file, from Kubernetes, or from another backend
without any change to the code that consumes it.

## The provider seam

Config is a pluggable-provider feature. The skeleton ships the selection seam — a
config **mode**, its settings, and a factory that maps a mode to a provider module
— and the default file provider. The active mode is chosen with `TAI_CONFIG_MODE`
(`file` by default). The factory loads the selected provider by dynamic import and
builds its manager:

```python theme={null}
from tai42_skeleton.config import ConfigManagerFactory

manager = ConfigManagerFactory.create()
```

Other providers ship as separately installed plugins that expose the same
`build_config_manager()` convention. The factory needs nothing but the mode and the
module; the consuming code never knows which provider is behind the manager.

## Secrets are referenced, not embedded

Sensitive values are referenced by the **name** of the environment variable that
holds them, not by value. A `secret_env` field carries an env var name; the runtime
reads the secret from the environment at use time. So a manifest, a hook, or a
webhook verifier binding names `secret_env` and never contains the credential
itself — keeping secrets out of files that are edited, versioned, or displayed.

## How a config change is written

Every change to the manifest or env — a tool reload, a manifest replace, a live
env edit — runs one [mutation pipeline](/concepts/live-operations#the-mutation-pipeline):
validate the resulting config, persist it transactionally, reload the calling
process, then broadcast on the [worker bus](/concepts/worker-bus). Persisting
before reloading is why the persisted config and the live config cannot drift
apart through a supported path — the running state is always what is on disk.

Two guarantees hold across every provider:

* **Transactional writes, in both config modes.** A write holds exclusive access
  across the whole read-modify-write span, so a concurrent writer cannot
  interleave and lose an update — the `file` provider takes a lock, the `k8s`
  provider retries on an optimistic-concurrency conflict.
* **Comment preservation.** A manifest edit keeps the surrounding comments,
  key order, and formatting of the untouched document. An automated edit does not
  reflow a file a human maintains by hand.

## The plugin model

Config providers are one member of a broader pattern. A **plugin** is any
separately-shipped package that registers through the `tai42_app` contract handle —
connectors, storage backends, config providers, [backends](/concepts/backends),
monitoring, and webhook verifiers. A plugin is loaded because the
[manifest](/concepts/manifest) names its module, and no plugin imports the
skeleton, which is what keeps the core provider-free and the ecosystem open-ended.

Change environment values and read the active mode from the CLI:

```bash theme={null}
tai config mode
tai config env set KEY=value
```

## Settings reference

Every setting below is read from the environment (or the managed `.env` store) at
use time. These are hand-maintained here — there is no generated reference for the
skeleton, toolbox, and agents settings, so this page is their canonical home.

### Core environment

| Variable                        | Default             | What it does                                                                                                                                              |
| ------------------------------- | ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `TAI_CONFIG_MODE`               | `file`              | Selects the config provider. `file` (default) reads from disk; `k8s` loads the Kubernetes config provider plugin. Any other value is **rejected loudly**. |
| `TAI_CONFIG_DIR_PATH`           | `/app`              | Directory the `file` provider anchors `.env`, `manifest.yml`, and templates under.                                                                        |
| `TAI_MANIFEST_PATH`             | —                   | Manifest path; also the default for `--manifest-path`.                                                                                                    |
| `TAI_RUN_MODE`                  | unset               | Unset/empty runs the normal multi-worker server; `debug` runs a single in-process server (ignoring `--workers`). Any other value is **rejected loudly**.  |
| `TAI_LOG_LEVEL`                 | `INFO`              | Root log level. A config reload re-applies it under `tai serve`.                                                                                          |
| `TAI_BODY_LIMIT_MAX_BODY_BYTES` | `10485760` (10 MiB) | App-wide request-body cap; oversized bodies get a `413`.                                                                                                  |
| `SUB_MCP_REDIS_URL`             | unset               | Durable sub-MCP registration store. **Required** when running more than one worker (see [Deploy](/guides/deploy#scale-workers)); in-memory when unset.    |

### Worker bus

The [worker bus](/concepts/worker-bus) is app-owned internal infrastructure — a
plain-Redis fan-out primitive, not a plugin. It is configured entirely through
these `TAI_BUS_*` variables; no manifest field selects a bus.

| Variable                | Default | What it does                                                                                                                                                                                                                                                                              |
| ----------------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `TAI_BUS_REDIS_URL`     | unset   | Redis URL that enables the bus. Unset = bus off; the process runs on a no-op local bus (valid only for a single worker, `file` mode, no backend). **Required** for a multi-worker server, a registered backend, or `TAI_CONFIG_MODE=k8s` — the boot rules refuse those shapes without it. |
| `TAI_BUS_NAMESPACE`     | `tai`   | Prefixes the control channel, every reply channel, and every presence key. Redis pub/sub is server-global, so give each stack sharing one Redis a unique value.                                                                                                                           |
| `TAI_BUS_ACK_TIMEOUT`   | `2.0`   | Seconds the collector waits for the fast liveness acknowledgements before budgeting the rest of its wait toward the apply deadline.                                                                                                                                                       |
| `TAI_BUS_APPLY_TIMEOUT` | `30.0`  | Seconds a mutation waits for every worker to finish applying — the report cut. Budgets a worst-case reload plus the post-apply hooks.                                                                                                                                                     |
| `TAI_BUS_HEARTBEAT_TTL` | `15.0`  | Presence-key TTL. A worker refreshes it at a third of this, so a frozen or killed worker leaves the census within one TTL.                                                                                                                                                                |

### Connector token encryption

Connector OAuth token blobs are encrypted at rest. Supply the key by env var name —
the value is never embedded in a manifest.

| Variable                  | What it does                                                                                                                                                               |
| ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `CONNECTORS_KEK`          | Base64 32-byte AES-GCM key encrypting connector token blobs. Required once connectors store credentials.                                                                   |
| `CONNECTORS_KEK_PREVIOUS` | Optional base64 32-byte previous KEK, retained across a rotation so blobs written under the old key still decrypt (decrypt-only; drop it once every blob is re-encrypted). |

### Proxy pool (tai42-toolbox)

| Variable                  | Default | What it does                                                                                                               |
| ------------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------- |
| `PROXY_POOL`              | empty   | The operator's proxy-URL pool the `proxy` tool extension rotates over.                                                     |
| `PROXY_ALLOW_CALLER_URLS` | `false` | When `false`, a caller-supplied proxy URL not already in the pool is refused; set `true` to opt into caller-supplied URLs. |

### Timeouts and limits

Guardrails on downstream calls, concurrency, and payload sizes. All have safe
defaults; raise a timeout only when a legitimately longer operation needs it.

| Variable                                | Default | Bounds                                                                                                            |
| --------------------------------------- | ------- | ----------------------------------------------------------------------------------------------------------------- |
| `TAI_MCP_CALL_TIMEOUT_SECONDS`          | `300`   | Wall-clock budget for one downstream MCP tool call.                                                               |
| `TAI_MCP_SCHEMA_MAX_DEPTH`              | `50`    | Max JSON-schema nesting depth accepted when adapting an advertised MCP tool; a deeper schema skips that one tool. |
| `MCP_CLIENT_CONNECT_TIMEOUT_SECONDS`    | `30`    | Budget for an MCP client connect + init.                                                                          |
| `INTERACTIONS_BLOCKING_GRACE_SECONDS`   | `5`     | Outer bound a blocking `ask_user` waits past its own deadline.                                                    |
| `TAI_TOOL_RUNS_MAX_CONCURRENT_RUNS`     | `32`    | Per-worker cap on in-flight background tool runs; at capacity the submit door answers `503`.                      |
| `TAI_TOOL_RUNS_SHUTDOWN_DRAIN_SECONDS`  | `10`    | How long shutdown waits for cancelled tool-run supervisors to write their terminal records.                       |
| `TAI_AGENTS_MAX_VOTERS`                 | `16`    | Max voters a `voting_agent` invocation accepts.                                                                   |
| `TAI_AGENTS_VOTER_CONCURRENCY`          | `8`     | Concurrent voters run per `voting_agent` invocation.                                                              |
| `TAI_AGENTS_EMBEDDING_DIMS_CACHE_SIZE`  | `64`    | Size of the retrieval agent's embedding-dimension cache.                                                          |
| `BATCH_DEFAULT_MAX_CONCURRENT`          | `5`     | Default concurrency for the `batch` tool extension.                                                               |
| `BATCH_MAX_BATCH_SIZE`                  | `100`   | Max param sets a `batch` call accepts.                                                                            |
| `TAI_SAMPLING_MAX_TOKENS_PER_CALL`      | `32768` | Cap on tokens requested in one sampling call.                                                                     |
| `TAI_SAMPLING_MAX_CALLS_PER_INVOCATION` | `20`    | Cap on sampling calls per tool invocation.                                                                        |
| `JQ_TIMEOUT_SECONDS`                    | `10`    | Budget for one `jq` expression evaluation.                                                                        |

**Per-feature connection timeouts.** Each Redis- or Postgres-backed feature carries
its own connection tuning under that feature's env prefix. Postgres connections
expose `<PREFIX>PG_CONNECT_TIMEOUT` (default `10`) and
`<PREFIX>PG_STATEMENT_TIMEOUT_SECONDS` (default `60`) — for example
`ACCESS_CONTROL_STORE_PG_CONNECT_TIMEOUT`, `VERSIONING_STORE_PG_STATEMENT_TIMEOUT_SECONDS`.
Redis connections expose `<PREFIX>SOCKET_TIMEOUT` and `<PREFIX>SOCKET_CONNECT_TIMEOUT`
— for example `ACCESS_CONTROL_SOCKET_TIMEOUT`, `TAI_TOOL_RUNS_SOCKET_CONNECT_TIMEOUT`.

<Note>
  A specific config provider's setup — for example which secret store and config
  source it reads — lives in that provider package's own repository, reachable
  from the [catalog](/reference/catalog/index). The platform documents the provider
  contract, not any one provider.
</Note>

See the [config-provider author guide](/guides/authors/config-provider) for
writing a provider and the [CLI reference](/reference/cli/index) for `tai config`.
