---
title: "Configuration reference"
description: "Describe how you want the work done. Auto builds a working playbook you can test, change and run again."
url: "https://on.auto/docs/self-host/configuration"
last_updated: "2026-10-17T10:18:42Z"
---

# Configuration reference

This page lists every setting of the runtime, the configuration file, and the tool servers functions may call. The runtime reads its settings once, when it starts. Change a setting, then restart the server.

## How settings are read

*   Every setting is an environment variable. Keys and other secrets go in the environment alone.
*   A container takes its variables from its platform, or from a file of them with `docker run --env-file`.
*   `npx @on.auto/runtime` also reads `.env` in the folder it starts in. A variable set in the shell wins over `.env`.
*   Settings that are lists or maps can also go in a YAML configuration file: `model_gateways`, `model_aliases`, `declared_models`, `allowed_models`, `tool_servers`, `connections`, `api_keys` and `allowed_origins`.
*   `CONFIG_FILE` names that file. `npx @on.auto/runtime` reads `auto-runtime.yaml` in the folder it starts in when it is there, unless `.env` or the shell sets `CONFIG_FILE`. A container reads a file only when `CONFIG_FILE` names it, as [Use a configuration file](https://on.auto/docs/self-host/docker#use-a-configuration-file) shows.
*   Each key of the file stands for the environment variable of the same name in upper case. A variable that is set wins over the key, whole: `MODEL_GATEWAYS` replaces the file's `model_gateways`, and the two are not merged.
*   Precedence, highest first: the shell, `.env` (with `npx`), the configuration file, the default.
*   At start, the server logs which settings it read from the file, and which of them the environment set too.
*   A value the server cannot read stops it at start. The message names the setting and what it expects, and never the value.

### Write the configuration file

Copy [`auto-runtime.example.yaml`](https://github.com/BeOnAuto/runtime/blob/main/auto-runtime.example.yaml) to start one. [`auto-runtime.schema.json`](https://github.com/BeOnAuto/runtime/blob/main/auto-runtime.schema.json) is the file's JSON Schema, so an editor with the YAML language server completes and checks it.

```yaml
# yaml-language-server: $schema=https://raw.githubusercontent.com/BeOnAuto/runtime/main/auto-runtime.schema.json
model_gateways:
  - name: gateway
    base_url: https://gateway.example.com/v1
    api_key: ${GATEWAY_API_KEY}
model_aliases:
  anthropic/*: gateway/anthropic/*
```

A secret is never written in the file. The file refers to the variable that holds it:

*   `${NAME}` takes the value of the variable `NAME`;
*   `${NAME:-default}` takes `default` when `NAME` is not set;
*   `$$` stands for a literal `$`.

The server stops at start with one line that names the file, the line, the column and the key, never a value, when:

*   it cannot use the file;
*   the file holds a key the server does not know;
*   a value is one its setting refuses;
*   a reference names a variable that is not set;
*   a credential is written out instead of referenced.

A value counts as a credential written out when:

*   its key names a credential, such as `api_key`, `Authorization`, `client_secret`, `password` or `token`, and it holds no reference. `credential_kinds` of the `connections` section names sorts of credential and not one, so its values are judged by their shape alone;
*   the text written out in it has the shape of one, such as `Bearer ...`, an `sk-...` provider key or an `abk_...` API key;
*   it is a URL with a password, whose user or query value has that shape, or whose query names a parameter as a key that names a credential does, such as `?api_key=...`, and holds no reference there;
*   it is an argument whose part after its `=` has that shape.

Inside an argument, only the value is judged, never the name the argument gives it.

## What to set for what

| To use | Set | See |
| --- | --- | --- |
| A provider's own API | `ANTHROPIC_API_KEY`, `OPENAI_API_KEY` or `GOOGLE_GENERATIVE_AI_API_KEY` | [Direct API keys](https://on.auto/docs/self-host/models#direct-api-keys) |
| An OpenAI-compatible gateway | `model_gateways` in the file, a list whose `name` becomes the prefix, `gateway/<model>` | [A gateway](https://on.auto/docs/self-host/models#an-internal-openai-compatible-gateway-with-a-custom-header) |
| Amazon Bedrock | `AWS_REGION`, with the AWS default credential chain | [Bedrock](https://on.auto/docs/self-host/models#amazon-bedrock-with-an-iam-role) |
| Azure OpenAI | `AZURE_RESOURCE_NAME` and `AZURE_API_KEY` | [Azure](https://on.auto/docs/self-host/models#azure-openai-with-an-api-key) |
| Google Vertex AI | `GOOGLE_VERTEX_PROJECT` and `GOOGLE_VERTEX_LOCATION` | [Vertex](https://on.auto/docs/self-host/models#google-vertex-ai-with-workload-identity) |
| Your own names for models | `model_aliases` in the file | [Model aliases](https://on.auto/docs/reference/model-settings#model-aliases) |
| Another provider's models, through your gateway | `model_aliases: {anthropic/*: gateway/anthropic/*}` in the file | [Model aliases](https://on.auto/docs/reference/model-settings#model-aliases) |
| The models listed for Bedrock, Azure or Vertex | `declared_models` in the file, a list of model ids for each provider prefix | [Declared models](https://on.auto/docs/reference/model-settings#declared-models) |
| Only some models | `allowed_models` in the file, such as `[anthropic/*, gateway/llama-3.3-70b]` | [Allowed models](https://on.auto/docs/reference/model-settings#allowed-models) |
| An outbound proxy or a private certificate authority | `NODE_USE_ENV_PROXY=1`, `HTTPS_PROXY` and `NODE_EXTRA_CA_CERTS` | [Proxy and CA](https://on.auto/docs/self-host/models#behind-an-outbound-proxy-with-a-private-certificate-authority) |
| The ledger in Amazon RDS without a database password | `DATABASE_URL` without a password and `DATABASE_CREDENTIALS=aws-iam` | [Without a password](https://on.auto/docs/self-host/postgresql#without-a-password-on-amazon-rds) |
| Secrets sealed under a key of your own | `SECRETS_KEY`, and `SECRETS_KEY_PREVIOUS` while it replaces another | [Secrets at rest](https://on.auto/docs/self-host/security#secrets-at-rest) |
| Content kept in an object store instead of the ledger | `CONTENT_STORE_URL`, an `s3://bucket/prefix` URL | [Object storage](https://on.auto/docs/self-host/object-storage) |
| Tools that functions may call | `tool_servers` in the file, each entry's `allowed` to allow only some of its tools | [Tool servers](https://on.auto/docs/self-host/configuration#tool-servers) |
| People who sign in through the Auto identity service | `PUBLIC_URL`, which turns sign-in on, and `IDENTITY_ISSUER` for a development instance of the service | [Let people sign in](https://on.auto/docs/self-host/security#let-people-sign-in) |
| Machines and pipelines that call the API | `API_KEYS`, with an entry the key command prints for each | [API keys](https://on.auto/docs/self-host/security#api-keys) |
| The requests interaction functions may keep open | `INTERACTION_OPEN_REQUESTS` | [Interaction functions](https://on.auto/docs/self-host/configuration#interaction-functions) |
| The model processes and the builds of prediction functions | `PREDICTION_PROCESSES`, `PREDICTION_PROCESS_MEMORY`, `PREDICTION_BUILDS_AT_ONCE`, `PREDICTION_BUILDS_PER_DAY` and `PREDICTION_MAX_FUNCTIONS` | [Prediction functions](https://on.auto/docs/self-host/configuration#prediction-functions) |
| The folder prediction functions keep model files in | `PREDICTION_CACHE_DIR` | [Prediction functions](https://on.auto/docs/self-host/configuration#prediction-functions) |

`list_models` (`GET /v1/orgs/{org}/models`, and the MCP tool of the same name) shows the models the server can call, leaving out what `allowed_models` does not allow. [Listing the models](https://on.auto/docs/reference/model-settings#listing-the-models) sets out how it builds the list, and [Allowed models](https://on.auto/docs/reference/model-settings#allowed-models) what a definition that names a model outside `allowed_models` returns.

## Settings

Every setting is an environment variable. The tables below group them by area.

### Server and logs

| Variable | Default | Purpose |
| --- | --- | --- |
| `PORT` | `5555` | The port the server listens on |
| `HOST` | `0.0.0.0` in the image, `127.0.0.1` with `npx` | The interface the server binds to. On a loopback address with neither `PUBLIC_URL` nor `API_KEYS`, the server is in [local mode](https://on.auto/docs/self-host/security#local-mode). On any other address it needs one of them to start |
| `CONFIG_FILE` | none; `auto-runtime.yaml` with `npx` when it is in the folder | The configuration file, absolute or relative to the working directory |
| `LOG_FORMAT` | `json`, `console` with `npx` | `json`: one JSON object per line on stderr. `pretty`: lines of text for a person. `console`: a time, a mark for the level and the message, in color at a terminal |
| `LOG_LEVEL` | `info`, `error` with `npx` | The least level logged: `debug`, `info`, `warn` or `error` |

### Sign-in and access

[Authentication and security](https://on.auto/docs/self-host/security) sets out how these work together.

| Variable | Default | Purpose |
| --- | --- | --- |
| `PUBLIC_URL` | none | The URL at which callers reach the runtime. Set, it turns sign-in through the Auto identity service on, and every access token names it as its audience. [Let people sign in](https://on.auto/docs/self-host/security#let-people-sign-in) gives the URLs it accepts |
| `IDENTITY_ISSUER` | `https://clerk.on.auto` | The identity service that people sign in through, an `https` URL with no path, query or fragment. A development runtime names the development instance of the service |
| `API_KEYS` | none | The API keys the server accepts, as a compact JSON array of the entries the key command prints; `api_keys` in the file. See [API keys](https://on.auto/docs/self-host/security#api-keys) |
| `ALLOWED_ORIGINS` | none | Comma-separated origins a browser page may call the API from, besides Auto Studio's; `allowed_origins` in the file. See [Calling from a browser](https://on.auto/docs/self-host/security#calling-from-a-browser) |

### Storage

| Variable | Default | Purpose |
| --- | --- | --- |
| `LEDGER_FILE` | `data/ledger.db`, which in the image is on the `/data` volume; `~/.auto-runtime/ledger.db` with `npx` | The SQLite file of the ledger, absolute or relative to the working directory. Its folder is created when missing |
| `DATABASE_URL` | none | A PostgreSQL URL. When set, the ledger is kept in that database instead, and setting `LEDGER_FILE` too stops the server at start. Environment only, since it holds a password. See [Use PostgreSQL](https://on.auto/docs/self-host/postgresql) |
| `DATABASE_CREDENTIALS` | `url` | How the server signs in to that database: `url`, with the password the URL holds, or `aws-iam`, with a token signed for each connection. See [Without a password, on Amazon RDS](https://on.auto/docs/self-host/postgresql#without-a-password-on-amazon-rds) |
| `SECRETS_KEY` | `secrets.key` beside a SQLite ledger file, made at the first start | The key the secrets kept in the ledger's database are sealed under: the base64 of 32 random bytes, as `openssl rand -base64 32` prints it. Without it, a SQLite ledger keeps a key in that file, which only its owner may read. A ledger in PostgreSQL needs it, and a container sets it. The server also derives from it the key that signs the state an MCP client sends back when it retries a call, so every server that shares it answers that retry. Environment only. See [Secrets at rest](https://on.auto/docs/self-host/security#secrets-at-rest) |
| `SECRETS_KEY_PREVIOUS` | none | The key that `SECRETS_KEY` replaces, in the same form and only beside it. At start the server reads with it what it sealed and seals that again under `SECRETS_KEY`, after which it can go. While it is set, the server also answers the retry of an MCP call whose state a server signed under the key it replaces. See [Rotate the secrets key](https://on.auto/docs/self-host/production#rotate-the-secrets-key) |
| `CONTENT_STORE_URL` | none | An `s3://bucket/prefix` URL. When set, content is kept in that bucket instead of the ledger's database. Content kept before a change is not moved; see [Keep content on object storage](https://on.auto/docs/self-host/object-storage#switch-a-deployment-that-already-has-content) |

### Models

Reasoning functions call language models with these settings, all optional. [Set up a model provider](https://on.auto/docs/self-host/models) says which to set for what, and how to check them in the start-up line. A provider whose settings are absent is not configured, and a definition that names it is rejected as `unavailable` when it runs. The [model settings reference](https://on.auto/docs/reference/model-settings) holds the rules for the lists and maps below.

| Variable | Purpose |
| --- | --- |
| `ANTHROPIC_API_KEY` or `ANTHROPIC_AUTH_TOKEN`, `ANTHROPIC_BASE_URL` | Anthropic (`anthropic/...`) |
| `OPENAI_API_KEY`, `OPENAI_BASE_URL`, `OPENAI_API` | OpenAI (`openai/...`). `OPENAI_API=chat_completions` for endpoints without the Responses API |
| `GOOGLE_GENERATIVE_AI_API_KEY` | Gemini API (`google/...`) |
| `AWS_REGION`, `AWS_BEARER_TOKEN_BEDROCK`, `AWS_ENDPOINT_URL_BEDROCK_RUNTIME`, `AWS_ENDPOINT_URL` | Amazon Bedrock (`bedrock/...`, `bedrock-anthropic/...`), with the AWS default credential chain |
| `AZURE_RESOURCE_NAME` or `AZURE_BASE_URL`, `AZURE_API_KEY`, `AZURE_API_VERSION` | Azure OpenAI (`azure/...`). Without a key, Microsoft Entra ID, in an image built with `--build-arg AZURE_IDENTITY=true` |
| `GOOGLE_VERTEX_PROJECT`, `GOOGLE_VERTEX_LOCATION` | Vertex AI (`vertex/...`, `vertex-anthropic/...`), with Google application default credentials |
| `MODEL_GATEWAYS` | A list of OpenAI-compatible gateways, each its own provider prefix; `model_gateways` in the file |
| `MODEL_ALIASES` | A map from one model reference to another. A trailing `*` on both sides covers every model of a provider; `model_aliases` in the file |
| `DECLARED_MODELS` | The models `list_models` shows for a provider that does not list its own, and for a gateway whose list cannot be read, by provider prefix; `declared_models` in the file |
| `ALLOWED_MODELS` | The only model references a definition may give, by name or through an alias, and that `list_models` shows; every model when not set; `allowed_models` in the file |
| `NODE_USE_ENV_PROXY`, `HTTPS_PROXY`, `NO_PROXY`, `NODE_EXTRA_CA_CERTS` | Node's own switches for an outbound proxy and a private certificate authority |
| `REASONING_MAX_INPUT_TOKENS` | The most input tokens the model calls of one run of a reasoning function may add up to, from 10,000 to 9,999,999, 2,000,000 unless set. Past it, the model replies from what it has, without its tools |

### Workflows

[Workflow storage and failover](https://on.auto/docs/self-host/workflows#starting-stopping-and-restarting) sets out how the server uses these.

| Variable | Default | Purpose |
| --- | --- | --- |
| `WORKFLOW_MAX_DURATION` | `P30D` | The most a run may last, an ISO 8601 duration from `PT2H` to `P365D` |
| `WORKFLOW_NESTED_RUNS` | `32` | How many calls the server runs at once, from 1 to 1000, shared by every org |
| `WORKFLOW_MAX_OPEN_CALLS` | `1000` | How many calls may wait under one run at the top of a tree, from 1 to 9999 |
| `WORKFLOW_SWEEP_INTERVAL` | `PT1S` | How often the server checks its waiting runs (a sweep), an ISO 8601 duration from `PT0.01S` to `PT1M` |

### Telemetry

`OTEL_EXPORTER_OTLP_ENDPOINT` turns telemetry on, and `TELEMETRY_CONTENT=true` records content. [Send telemetry](https://on.auto/docs/self-host/telemetry#settings) lists these and the other `OTEL_` settings the server reads. Each is read from the environment only.

### Computation and recall functions

Each run of a computation function, and each run and view of a recall function, runs in a worker thread the server keeps between runs.

*   The server never keeps more workers alive than `COMPUTATION_WORKERS`.
*   It folds the views of recall functions in at most half of the workers at once, and at least one.
*   It ends a worker after a run that ran past its time, used too much memory, was cancelled or broke.
*   It also ends a worker after its thousandth run, and after a minute without one.
*   A worker has a stack of 64 MiB and is stopped when its heap grows past 256 MiB. [Plan memory](https://on.auto/docs/self-host/production#plan-memory) sizes them.

| Variable | Default | Purpose |
| --- | --- | --- |
| `COMPUTATION_WORKERS` | `4` | The most worker threads the server keeps, and so the most runs it takes at once, from 1 to 64. A run that finds none free within 10 seconds is `unavailable` |
| `RECALL_MAX_FUNCTIONS` | `32` | How many active recall functions a playbook may keep, from 1 to 1000. Lowering it below what a playbook already keeps refuses only that playbook's saves |
| `RECALL_MAX_REBUILDS` | `4` | How many views of one playbook are built at once, from 1 to 64. The others wait in the order they were saved |
| `RECALL_PLAYBOOKS_AT_ONCE` | `4` | How many playbooks the server folds at once, from 1 to 64 |

### Interaction functions

An interaction function needs nothing configured beyond its [tool server](https://on.auto/docs/self-host/configuration#tool-servers) in `tool_servers`. It does one of these:

*   calls the tool it names under `call`, and returns its result;
*   sends its request through the tool it names under `deliver`;
*   leaves its request in the playbook's inbox.

It may also read the replies to what it sent through another tool of that server.

*   The entry's `allowed` names the tools its functions may use. It is checked again at every call, every attempt and every read.
*   The entry's `record_content`, on unless the entry turns it off, keeps what they send and what the tool returns.

| Variable | Default | Purpose |
| --- | --- | --- |
| `INTERACTION_OPEN_REQUESTS` | `10000` | The most requests a playbook may have open at once, from 1 to 1,000,000. A run past it is `unavailable` with the kind `requests_full` |

### Prediction functions

A prediction function's content is kept by the playbook: its uploaded datasets, the rows and the brief of each build, and the model file each version holds. That content is in the ledger's database or, with `CONTENT_STORE_URL`, in the object store, where a build's candidates are also staged until the build ends.

Its model processes load models and read a build's rows from files the server keeps on its own disk. The server writes a file again from the playbook's content whenever one is missing, so the folder needs no volume and loses nothing when it is emptied.

Each setting is read with its range and logged at start. A value outside its range stops the server at start, naming the setting. [Plan memory](https://on.auto/docs/self-host/production#plan-memory) sets out how much the processes take.

| Variable | Default | Purpose |
| --- | --- | --- |
| `PREDICTION_MAX_FUNCTIONS` | `32` | The most active prediction functions a playbook may keep, from 1 to 1,000. A save past it is refused |
| `PREDICTION_PROCESSES` | `4` | The most model processes that run at once, from 2 to 64 |
| `PREDICTION_PROCESS_MEMORY` | `1280` | The data each model process may reserve, in MiB, from 768 to 16,384. A process that reaches it is ended, and its job ends out of memory |
| `PREDICTION_BUILDS_AT_ONCE` | `2` | The most builds the server scores at once, from 1 to 63, and below `PREDICTION_PROCESSES` so that a question always finds a process. A build past it is `unavailable` with the kind `builds_full` |
| `PREDICTION_BUILDS_PER_DAY` | `4` | The most builds a prediction function may ask in the trailing 24 hours, from 1 to 100. A build past it is `unavailable` with the kind `builds_full` and the `because` field `builds_of_the_day` |
| `PREDICTION_CACHE_DIR` | `/var/tmp/auto-runtime` | The absolute path of the folder the model files are kept in |

The folder of `PREDICTION_CACHE_DIR` follows these rules:

*   At start, the server creates the folder when it is missing, readable only by its own user, and does the same for each folder within it.
*   It refuses to start, naming the setting, when the folder cannot be made, belongs to another user, or other users may write to it. A folder another user may write to would let that user change a model before it is loaded.
*   The files found in the folder at start are read again, and kept only when their bytes match the digest in their name.
*   Each server keeps at most 1 GiB of model files and 1 GiB of rows, by its own count. Give two servers on one machine a folder each.

## Tool servers

A tool server gives functions tools to call. It comes from one of two places: an entry under `tool_servers` in the configuration file, which this section describes, or a [connection](https://on.auto/docs/self-host/configuration#connections) a person added to an org. Add an entry under `tool_servers`; `TOOL_SERVERS`, the same list as JSON, wins over it. [Add a tool server](https://on.auto/docs/self-host/tool-servers) walks through adding one and checking it.

*   A reasoning function that lists `tools` calls the tools of these servers.
*   An interaction function calls the tool it names in `call`, or sends its request through the tool it names in `deliver`.
*   The key of an entry is the name a function writes in `server/tool`. Nothing in a function says which kind of server it names.

Each entry is one of three types. When `type` is left out, it is taken from the field that names the server:

| Type | Named by | What it is |
| --- | --- | --- |
| `mcp` | `url` | A remote MCP server, spoken to over Streamable HTTP |
| `stdio` | `command` | An MCP server the runtime runs as a process |
| `openapi` | `document` | An HTTP API described by an OpenAPI document, 3.0 or 3.1, read once at start. Each of its operations is one tool |

```yaml
tool_servers:
  graph:
    url: https://gateway.example.com/mcp
    headers:
      Authorization: Bearer ${GRAPH_API_KEY}
    org: acme
    playbooks: [sales, support]
    allowed: [search, introspect, validate, dry_run, execute]
  notes:
    command: /usr/local/bin/notes-mcp-server
    args: [--read-only]
    env:
      PATH: /usr/local/bin:/usr/bin
      NOTES_API_KEY: ${NOTES_API_KEY}
    org: acme
  billing:
    document: https://billing.example.com/openapi.json
    base_url: https://billing.example.com/v2
    security:
      apiKeyAuth: ${BILLING_API_KEY} # the scheme the document declares under components.securitySchemes
    headers:
      X-Tenant: acme-eu # outside the document's schemes
    org: [acme, globex]
    allowed: [listInvoices, getInvoice]
```

### Fields of an entry

| Field | For | What it holds |
| --- | --- | --- |
| `type` | every type | `mcp`, `stdio` or `openapi`. Taken from `url`, `command` or `document` when left out |
| `url` | `mcp` | The server, spoken to over Streamable HTTP |
| `headers` | `mcp`, `openapi` | Headers sent with every request. For an `mcp` server, its credential. For an `openapi` server, only what its document's security schemes do not place, such as a tenant. The value of a reference is a secret; a value written out is not |
| `auth` | `mcp`, `openapi` | OAuth client credentials instead of a header: `issuer`, `client_id`, `client_secret` or `private_key` with `algorithm`, and `scope`. For an `openapi` server, what satisfies the document's `oauth2` and `openIdConnect` schemes; refused when it declares neither. See [Sign in to a server with OAuth](https://on.auto/docs/self-host/configuration#sign-in-to-a-server-with-oauth) |
| `command` | `stdio` | An installed, pinned command, started when a run or a listing first needs it and stopped with the server |
| `args` | `stdio` | Its arguments |
| `env` | `stdio` | Its whole environment. The value of a reference is a secret; a value written out is not. It inherits nothing else from the server, so give `PATH` or an absolute command |
| `document` | `openapi` | The OpenAPI document of the API, 3.0 or 3.1, in JSON or YAML: an https URL, an http URL on a loopback address, or a path relative to the directory of this file that stays inside it |
| `base_url` | `openapi` | The URL the calls go to, in place of the first server the document names. Required when the document names none or names it relative to a file. An https URL, or an http URL on a loopback address |
| `query_params` | `openapi` | Query parameters sent with every call that the document's security schemes do not place, such as a tenant. The value of a reference is a secret; a value written out is not |
| `security` | `openapi` | The credential for each of the document's security schemes that the entry gives, by the scheme's name, placed where the scheme says. Always a reference, such as `${BILLING_API_KEY}` or `ada:${BILLING_PASSWORD}`. See [The credentials of an HTTP API](https://on.auto/docs/self-host/configuration#the-credentials-of-an-http-api) |
| `org` | every type | Required. The org whose functions may use the server, a list of orgs, or `*` for every org of the runtime. One entry serves each org under one name, with one credential and one connection, and each org sees the same tools |
| `playbooks` | every type | The playbooks that may use it within each org it names, by id, the same list in each. Every playbook of each org when left out. Refused beside `*`, since a playbook id belongs to one org |
| `allowed` | every type | The tools of the server that a function may name and that a call made by hand with `execute` may call, each named as the server lists it, without the server's name and without `*`. Every tool of the server when left out |
| `record_content` | every type | Whether the arguments and results of calls are kept whole, scrubbed of secrets, in the playbook's ledger, where anyone who may read the playbook reads them. `true` when left out; `false` keeps their sizes alone |
| `request_id` | every type | The response header, or for an MCP server the key of a result's metadata, in which the server returns its own id of a request, recorded with each call |

### Keep secrets out of the file

Only `headers`, `query_params`, `security`, `env` and `auth` may hold a reference. A reference in `url`, `command`, `args`, `document` or `base_url` stops the server at start: an argument shows in the machine's list of processes, and the URL of a server or of a document is no place for a credential.

A secret is one of these, and nothing else:

*   a value a `${NAME}` reference takes from the environment;
*   a credential of an `auth` block;
*   a token minted from one.

A value of `security`, which always holds a reference, is a secret as a whole, and so is the value of its reference. A header whose reference takes a scheme and a credential, such as `Authorization: ${GRAPH_HEADER}` with `GRAPH_HEADER` set to `Bearer <key>`, is a secret whole, and so is the credential after its scheme, `<key>`, so it is scrubbed where a server echoes the key alone. Each secret counts once, however many places hold it. A header written out, such as `X-Region: production-eu`, is not a secret, so it is never scrubbed.

The server scrubs every secret from every message, event and result:

*   as written;
*   inside JSON, where a quote, a backslash or a line break in it is escaped;
*   as an address carries it, form-encoded in a query and percent-encoded in a path;
*   in base64, as a Basic credential carries it.

There is one exception: a value shorter than 8 characters is not scrubbed, since scrubbing it would erase ordinary words. Give a server a key at least that long.

One set of secrets serves all the tool servers, for runs and for calls made by hand with `execute` alike, so a token minted for a run is scrubbed from a call made by hand too. A minted token is scrubbed until the token after the one that replaces it is minted, since a call in flight as a token is renewed may still carry the one before it in a result recorded a moment later.

### Run a server as a process

*   Write `command` as an installed program. Never use a package downloaded at start, such as `npx -y`, which would run whatever that package is the day the process starts. The MCP entry of the [quick start](https://on.auto/docs/get-started/local#connect-your-agent) is the one exception: it is Auto Runtime itself, pinned to one release, and you choose when to move it to another.
*   The process runs under the server's user.
*   `env` is the process's whole environment, so give it `PATH` or write `command` as an absolute path.

### Sign in to a server with OAuth

An `auth` block gives an `mcp` or `openapi` server OAuth client credentials instead of a header.

*   The server mints tokens from the client credentials, once for every run that needs one.
*   It renews them before they expire, and mints once more when the server responds with `401`.
*   It sends the credentials only to an authorization server whose metadata names the `issuer`.
*   An `mcp` server's authorization server is found through the server's own protected-resource metadata.
*   An `openapi` server's authorization server is the `issuer` itself. Its metadata is read by OAuth authorization server metadata or OpenID Connect discovery, since an HTTP API publishes none of its own.

### The credentials of an HTTP API

Give an HTTP API's credential under `security`, by the name of the security scheme its document declares. The server places it as the scheme says.

An OpenAPI document declares its security schemes under `components.securitySchemes`, each saying where its credential goes. Each operation names the schemes it requires under `security`, or takes the document's own `security` when it names none.

| Scheme | What `security` gives | What a call sends |
| --- | --- | --- |
| `apiKey` in a header | the key | the scheme's header |
| `apiKey` in the query | the key | the scheme's query parameter |
| `http` with the scheme `bearer` | the token | `Authorization: Bearer <token>` |
| `http` with the scheme `basic` | `user:password` | `Authorization: Basic` and their base64 |
| `oauth2` or `openIdConnect` | nothing: the entry's `auth` block | the token the block mints, as a bearer |

Use `security` for what the document's schemes describe, and `headers` and `query_params` for what they do not, such as a tenant.

Which credentials a call sends:

*   A requirement is a list of alternatives. Each alternative is a set of schemes that must all be given.
*   An operation is offered as a tool when the entry gives one of its alternatives.
*   A call sends the credentials of the first alternative the entry gives, in the document's order, and no other.
*   An operation whose requirement is empty, or of a document that declares none, sends none.
*   An operation for which the entry gives none of the alternatives is not offered as a tool. At start, the server logs a warning that names the schemes it needs.
*   The scopes an `oauth2` requirement names are the operation's. The `auth` block asks for its own `scope`, so set it to cover what the operations require.

The server stops at start, naming the entry, when:

*   a name under `security` is not a scheme the document declares;
*   a name under `security` is an `apiKey` the document places in a cookie;
*   a name under `security` is an `http` scheme other than `bearer` and `basic`;
*   a name under `security` is a `mutualTLS` scheme;
*   a name under `security` is an `oauth2` or `openIdConnect` scheme with a value, since the credential for those schemes comes from the `auth` block;
*   a `basic` value is not `user:password`;
*   `headers` or `query_params` holds a header or query parameter that a scheme given under `security` also places;
*   an entry has an `auth` block, and its document declares no `oauth2` or `openIdConnect` scheme.

### How an HTTP API becomes tools

At start the server reads each `openapi` entry's document: at most 4 MiB and 500 operations, within 10 seconds. It stops, naming the entry, when:

*   it cannot read the document;
*   the document's URL responds with a redirect, which the server does not follow;
*   the document is Swagger 2.0, or of another version than 3.0 or 3.1;
*   a part it reads is not of the shape the specification gives it (the message names the place in the document);
*   two operations would share a tool name;
*   a path does not begin with `/`, which would join the host or the port of `base_url`;
*   a path holds a `.` or `..` segment, a `?` or a `#`;
*   its tools take more than 16 MiB when listed;
*   a reference does not resolve inside the document. This includes a reference to an http, https or file address, which the server never fetches;
*   its calls would have nowhere to go: no `base_url` and no server, or a server that is neither https nor on a loopback address.

Trailing slashes on `base_url` or on the server's URL are dropped.

Each operation becomes one tool:

*   Its name is the `operationId`, with any character a tool name cannot hold as `_`. Without one, it is the method and the path in lower case, such as `get_invoices_id` for `GET /invoices/{id}`.
*   Its description is the operation's `summary` and `description`.
*   Its input is one object: each path and query parameter by its name, and `body` for a JSON request body, each required when the document says so. The schemas of `components` they refer to are held inside it, so a function's model reads one schema whole.
*   `GET` and `HEAD` are read-only, `DELETE` destructive, and `GET`, `HEAD`, `PUT` and `DELETE` idempotent. So `allowed`, `search` and a run's read-only calls read an API's tools as they read an MCP server's.

Some operations are not offered as tools. At start, the server logs a warning for each operation it leaves out, naming the entry and the reason. It leaves out an operation that:

*   needs a header or a cookie;
*   takes an object in its query;
*   takes a body that is not JSON;
*   is a `TRACE`, or a `GET` or `HEAD` that takes a body;
*   has a path parameter of a `style` other than `simple`;
*   has a query parameter of a `style` other than `form`, or with `explode: false`;
*   has an input schema of more than 64 KiB.

A call:

*   fills the path and the query, with an array sent as repeated keys;
*   refuses a path argument that is empty, holds a `/`, or is `.` or `..`;
*   sends nothing to an address that would leave the origin or the path of `base_url`;
*   sends the body as JSON, with the entry's headers and query parameters.

The response is read as the result of an MCP server's call is read, within the same bounds:

| The API responds with | The call's result |
| --- | --- |
| JSON | The tool's structured content, and its text |
| Text | Text |
| Any other media type | Noted, and not shown |
| `401` | A key the server refused |
| `429` | Waits out a `Retry-After` that fits |
| `5xx` | A failing server |
| A redirect | An error result with its status. It is not followed, so the entry's headers and token go to no address but its own |
| Any other response, including `404` | An error result carrying the API's response |

Listing an HTTP API's tools asks the API nothing: the document is the list.

### Limit the tools a function may use

`allowed` narrows what a function may name, as `allowed_models` narrows the models. A server's own policy remains the hard boundary.

*   A function's `server/*` offers exactly the tools its server's entry allows.
*   `allowed` is the one list of a server's tools. The five operations over the playbook's tool servers, `search`, `introspect`, `validate`, `dry_run` and `execute`, show and call the tools it allows and no other.

### Try the tools by hand

An agent or a person can look at a server's tools, and call one, before a function names it.

*   `search` shows anyone who may read a playbook the entries that serve it, with the tools that each entry lets functions name. At the org level, a caller who may read every playbook of the org (with `org:read` or `playbook:read`) sees every entry that serves the org, whether the entry names the org in `org` or uses `*`. Each comes with its `playbooks` (`["*"]` when they are left out).
*   `search` and `dry_run` say whether the server marks a tool read-only (`readOnlyHint`).
*   `search` and `introspect` add no header, environment value, URL, command or credential of their own. What a server writes is passed on with the secrets above scrubbed out, except values shorter than 8 characters.
*   `execute` calls one tool as a run would, under the entry's credential, which is the playbook's. So it needs `playbook:write` whatever the tool does, and it is live.
*   While a tool server is configured, `execute` is marked destructive, so a client that honours the MCP hints asks the person before a call.
*   Every call made with `execute` is recorded in the playbook's history under the caller, with its content where the entry keeps it.

[Tool servers](https://on.auto/docs/reference/http#tool-servers) in the HTTP reference sets out the routes, fields and permissions of the five tools.

### What the server checks at start

The server checks every entry at start. It stops at any of these, naming the setting and the place but never a value:

*   an entry with none of `url`, `command` and `document`, or with more than one;
*   the fields of another type;
*   a name that is not 1 to 32 lowercase letters, digits and hyphens starting with a letter;
*   a name with two hyphens together or one at the end, which would blur where the server's name ends in the `server__tool` a model sees;
*   the name of a model provider or gateway;
*   a missing `org`;
*   a credential written out instead of referenced. This includes a password in a `url`, and a user, a query value or the part of an argument after its `=` that looks like a credential, such as `--key=sk-live-...`. The value is judged, never the name it is given;
*   a reference outside `headers`, `query_params`, `security`, `env` and `auth`;
*   a value of `security` written out instead of referenced;
*   an `Authorization` header beside an `auth` block;
*   a header the runtime sets itself;
*   an `issuer`, a `base_url` or a `document` URL that is neither https nor on a loopback address;
*   a `base_url`, or a first server of a document, with a query or a fragment;
*   a `document` path that leaves the directory of this file;
*   in `allowed`, an empty list, a name listed twice, a `*`, or a name that is not a tool name of 1 to 128 letters, digits, underscores, hyphens and dots (such as `graph/search`).

An unknown key in an entry is refused, like any other unknown key.

The server does not connect to an MCP server or start a process until a run needs one, or until `search`, `introspect`, `validate`, `dry_run` or `execute` asks it for its tools. The servers' messages, a process's output on stderr and every failed call go to the server's log, scrubbed of the entry's secrets.

## Connections

A connection is a tool server that a person adds to an org through their agent, which every playbook of the org may use, or the playbooks it names; a function names it by `name/tool` as it names an entry of `tool_servers`, and calls it in the same way. [Let people add connections](https://on.auto/docs/self-host/connections) sets out how a person adds and approves one, and what a connection that is not ready answers. The `connections` section of the configuration file says what people may add; `CONNECTIONS`, the same object as JSON, wins over it. Every rule has a default, so leaving the section out lets people add any connection but one of the credential `reference`, which names no variable until `variables` lists it.

```yaml
connections:
  people_may_add: true
  allowed_hosts: ['*.example.com', 'crm.example.com']
  credential_kinds: [none, reference, secret, client_credentials]
  variables: [CRM_API_KEY]
```

| Key | Default | Purpose |
| --- | --- | --- |
| `people_may_add` | `true` | `false` refuses every new connection, and the runtime uses none already added, though `list_connections` still lists them as they are |
| `allowed_hosts` | every host | The hosts a connection may reach, each a host or `*.` and a host for every host under it; the host of an `mcp` connection's `url`, of an `openapi` connection's `document` and `base_url`, and of the `issuer` of client credentials. Left out, a connection may reach every host, addresses inside your network such as `localhost` or `10.0.0.5` among them |
| `credential_kinds` | every kind | The kinds of credential a connection may take: `none`, `reference`, `secret` and `client_credentials` |
| `variables` | none | The environment variables a `reference` credential may name, such as `CRM_API_KEY`. None of them may be a variable the runtime reads for itself: any variable this page lists, such as `SECRETS_KEY`, `API_KEYS`, `PUBLIC_URL` or a provider's key; a credential the cloud libraries read, such as `AWS_SECRET_ACCESS_KEY`, `AZURE_CLIENT_SECRET`, `AZURE_CLIENT_CERTIFICATE_PASSWORD` or `AZURE_PASSWORD`; a gateway's `api_key_env`; or a variable that a reference of this file or of `TOOL_SERVERS` names |

`add_connection` refuses with `403` a connection one of these rules does not take, with the sentence of the rule, and with `409` a connection named as an entry of `tool_servers`. A key the section does not know, a pattern that is not a host, a kind there is not and a variable the runtime reads for itself stop the server at start, naming the line.

The rules are read at start, and checked again when a connection is first used after it, so a rule you change reaches a running server when it starts again: a connection a rule no longer takes is refused with the rule's sentence, which `search` shows, and with `people_may_add: false` the runtime uses no connection already added, in the sentence `This runtime uses no connection: its operator sets up its tool servers itself`. `allowed_hosts` holds every request a connection makes, not only those to the addresses it names: the server an `openapi` connection's document names, where `base_url` is left out, is checked once the document is read, and every request is checked before it is sent, those to an authorization server and those a redirect leads to among them. A call of an HTTP API follows no redirect, and the MCP client follows one only within the origin it asked. The value of each variable `variables` lists is read at start too, so a variable you set reaches a running server when it starts again.

## Sitemap

See the [full documentation and product sitemap](https://on.auto/sitemap.md) for related pages.
