Skip to content

Configuration

Newt reads one TOML file. The path is the NEWT_CONFIG environment variable, or server.toml in the working directory. server.example.toml in the repository root is a complete working example; a test parses it, so it stays valid.

Every table rejects unknown keys. Secrets are never in the file: the file holds the names of environment variables, and newt refuses to start if one is unset. A bad file fails at startup with a message naming the key.

Other environment variables:

Variable Default Meaning
NEWT_CONFIG server.toml Path to the config file.
RUST_LOG info Log filter (tracing env-filter syntax).
HOSTNAME newt Prefix of the runner’s lease owner id, which is {HOSTNAME}-{pid}-{uuid}. Shows up as lease_owner in the session status.

At startup newt connects to Postgres and applies the files in migrations/, then starts listening.

Key Type Default Meaning
listen socket address required Address the HTTP API binds to, for example "127.0.0.1:7878". Plain HTTP; terminate TLS in front.
Key Type Default Meaning
url_env string none Name of the environment variable that holds the Postgres connection URL.
embedded path none Run newt’s own Postgres with its data in this directory instead.
max_connections integer 10 Size of the connection pool. newt opens every connection at startup, so a burst of sessions does not wait for the pool to grow.

Set exactly one of url_env and embedded. With embedded, newt downloads the Postgres 18 binaries for this platform once (zonky’s builds from Maven Central, which bring their own libraries; into ~/.theseus/postgresql, checked against their published hash), creates the database in <embedded>/data on the first start, runs the server on a free local port while newt runs and stops it on exit. A server left running by a newt that was killed is stopped and restarted. It is meant for one machine: several newt instances share a Postgres you run, through url_env. Postgres refuses to run as root, so neither does embedded; the Docker setup in deploy/ uses its own Postgres container. Builds without the default embedded-postgres feature reject embedded.

The pool serves event accepts, turn starts and commits, journal writes, the sweeper and live feeds. Accepting an event takes one connection and starting a turn three, so under a burst (hundreds of sessions receiving events in the same second) turns wait for the pool, not the model. Size it for your bursts: roughly one connection per session you expect to start in the same second, up to what your Postgres allows (max_connections there is 100 by default, shared by every newt instance).

Optional. The JSONL event file, used for local runs, sims and newt-report.

Key Type Default Meaning
jsonl path none Append one JSON line per event to this file. No file is written when it is unset.
capture_content boolean false Keep message content (model input and output) in the file. When false, content is removed and only fingerprints, sizes, timings and usage remain.
Key Type Default Meaning
max_sessions integer, at least 1 64 Sessions this process runs turns for at once. Others wait.
cache_key_buckets integer, at least 1, or "auto" "auto" How many prompt cache keys the sessions of one agent share. See below.

For openai-responses routes newt sends prompt_cache_key = "{agent}/{n}". Anthropic routes do not use it.

Sessions of one agent share the same system prompt and tools. With one key, a brand-new session can reuse the cached system prompt that every other session has already written, so its first call is not a full miss. The cost: OpenAI spreads one key across more machines once it receives more than roughly 15 requests a minute, and a request that lands on another machine misses the cache. So each key should carry as much traffic as it can without passing that rate.

With "auto" newt counts each agent’s model calls over the last minute and keeps every key under 12 calls a minute: it adds keys as soon as the rate needs them, up to 256 (about 3,000 calls a minute), and gives them up only after the rate has stayed low for 10 minutes, so a quiet minute does not reshuffle sessions. An agent starts with one key. A number fixes the count instead. Either way n comes from the session key by jump consistent hashing: a session keeps its key while the count holds, and going from n to n + 1 keys moves only one session in n + 1. A session that used its key in the last 10 minutes, while the provider still holds its prefix, keeps that key when keys are added, unless the key itself passed 12 calls in the last minute: new, idle and crowded sessions take their hashed key, and no other session moves.

These are constants in RunnerConfig::default() (crates/newt-server/src/runner.rs), not config keys:

Setting Value
Lease TTL 30 s. A heartbeat renews running leases every 10 s.
Sweep interval 1 s. Each sweep wakes up to 100 sessions with pending events and no live lease.
Retry backoff after a failed turn 2 s doubling per failed attempt, capped at 300 s.
Attempts before dead-lettering a turn that had no effects 5
Shutdown grace period 8 s, then running turns are cancelled and given 5 s more.

Optional. newt deletes rows that only serve operations once they are old. History and checkpoints are the agent’s memory and are never deleted on their own: delete a whole session with DELETE /v1/agents/{agent}/sessions/{key} (Protocol, 5.6).

Key Type Default Meaning
inbox_days integer 30 Keep events this many days after the turn that consumed them committed, dead-lettered ones included. A repeat of an event id is recognised for as long as the event is kept. 0 keeps them forever.
live_days integer 7 Keep live feed frames this many days. 0 keeps them forever.

Each instance prunes once at start and then every hour, in batches of 5,000 rows.

A route is one way to reach one model.

Key Type Default Meaning
protocol "anthropic-messages", "openai-responses" or "openai-chat" required The wire protocol. openai-chat is Chat Completions, which vLLM, Ollama, OpenRouter, Together, Groq and most other hosts speak; it carries no reasoning between calls and sends no prompt_cache_key.
model string required The provider’s model id, sent in the request.
base_url string required Must start with http:// or https://. Newt appends /v1/messages for anthropic-messages (so use https://api.anthropic.com), /responses for openai-responses (so use https://api.openai.com/v1) and /chat/completions for openai-chat (for example http://localhost:11434/v1 for Ollama, https://openrouter.ai/api/v1 for OpenRouter).
key_env string required Name of the environment variable that holds the API key.
api_key_header string none Header that carries the key. By default anthropic-messages sends x-api-key and openai-responses sends Authorization: Bearer. Set it for gateways that use another header, such as api-key.
max_concurrent integer, at least 1 required Model calls in flight on this route. Further calls wait. Calls from background-priority turns may use at most max_concurrent - 1 of the slots (at least 1), which keeps a slot free for interactive turns.

Anthropic requests also send anthropic-version: 2023-06-01. Connect timeout is 10 s, a stream that goes silent for 300 s fails, and any one model call has a 10 minute deadline. These are constants.

A model alias is the name a bootstrap uses in its model field.

Key Type Default Meaning
chain array of route names required Routes to try, in order. Must be non-empty, and every name must be a route.

Each call tries the first route; a retryable failure (a network error, a timeout or deadline, HTTP 408, 409, 429 or 5xx, or an overloaded, api, rate_limit or server error from the provider) that happens before any output has streamed gets one more try on the same route after a 200 ms pause, then moves to the next route. A failure after output has started streaming is not retried, because the live feed has already shown it.

An agent is the name in the event API path (/v1/agents/{name}/...).

Key Type Default Meaning
context_provider string one of context_provider and mcp Base URL of the agent’s context provider; must start with http:// or https://. See Protocol.
mcp string one of context_provider and mcp Streamable HTTP URL of the one MCP server the agent is made of: its instructions, tools, skills and resources (Protocol 2.7). Must start with http:// or https://.
model string required with mcp, not allowed with context_provider A [models] alias. A context provider names its model in the bootstrap instead.
token_env string required Name of the environment variable that holds the agent’s token. It must be set and non-empty. The same token authenticates callers of the event API, and newt sends it to the context provider, to MCP servers that ask for auth: "agent_token", and to an mcp agent’s server.

Optional. Without this table, no traces are exported. With it, newt exports one trace per turn over OTLP/HTTP as JSON.

Key Type Default Meaning
endpoint string required OTLP traces URL; must start with http:// or https://.
profile "generic" or "langfuse" "generic" "langfuse" also sets Langfuse’s session, observation types and usage details.
headers_env string none Name of an environment variable holding request headers as name=value,name=value. For Langfuse: Authorization=Basic <base64 public:secret>,x-langfuse-ingestion-version=4. Must be set if named.
capture_content boolean true Export the messages new to each model call and its response. When false, only timings, token counts and tool names are exported, the session key in gen_ai.conversation.id is hashed, and error text becomes failed. Note the default differs from [events] capture_content.
service_name string "newt" The service.name resource attribute.

Span ids derive from the session, turn and call, so exporting the same turn twice overwrites rather than duplicates. Export runs off the turn’s path and drops events when it falls behind.

server = { listen = "127.0.0.1:7878" }
database = { url_env = "NEWT_DATABASE_URL", max_connections = 10 }
events = { jsonl = "newt-events.jsonl" }
runner = { max_sessions = 64, cache_key_buckets = "auto" }
retention = { inbox_days = 30, live_days = 7 }
[routes.openai]
protocol = "openai-responses"
model = "gpt-6.1-terra"
base_url = "https://api.openai.com/v1"
key_env = "OPENAI_API_KEY"
max_concurrent = 6
[routes.anthropic]
protocol = "anthropic-messages"
model = "claude-opus-5-5"
base_url = "https://api.anthropic.com"
key_env = "ANTHROPIC_API_KEY"
max_concurrent = 6
[models.default]
chain = ["openai", "anthropic"]
[agents.assistant]
context_provider = "http://localhost:4000/assistant"
token_env = "ASSISTANT_TOKEN"
[agents.tasks]
mcp = "http://localhost:4000/tasks/mcp"
model = "default"
token_env = "TASKS_TOKEN"
[tracing]
endpoint = "https://cloud.langfuse.com/api/public/otel/v1/traces"
profile = "langfuse"
headers_env = "NEWT_OTLP_HEADERS"
capture_content = true