Configuration
The reactor.yml schema, environment variables, and the global flags every command honors.
Configuration
reactor init writes a fully-commented reactor.yml at the project root. Every command reads it from <project>/reactor.yml. When the file is absent, the documented defaults apply.
The reactor.yml schema
state:
dir: ./.reactor # durable state (receipts, world-models, IR cache)
model:
provider: openrouter # openrouter (default) | openai | anthropic | google | <custom>
render_model: google/gemini-3.5-flash
compile_model: google/gemini-3.5-flash
temperature: 0
max_turns: 200
# base_url: ... # optional: override the provider's endpoint
# api_key_env: ... # optional: read the key from a different env var
sandbox:
mode: none # none (default) | docker
shell_timeout_ms: 300000
gateways: # external-driven entry points
- node: inbox
source_id: inbox
connector:
type: static # static | http | file (or a connectors.{cjs,js} plugin)
id_field: id
items: [{ id: item-1, body: "the first item" }]
reactors: [] # optional: a multi-reactor host (see below)state
| Key | Default | Meaning |
|---|---|---|
state.dir | ./.reactor | The durable state directory: receipts, world-models, and the compiled IR cache. Resolved to an absolute path rooted at the project dir, so every command agrees on one location regardless of cwd. |
model
| Key | Default | Meaning |
|---|---|---|
model.provider | openrouter | The model provider. A built-in (openrouter, openai, anthropic, google) supplies its own endpoint + key env; any other name requires base_url + api_key_env. |
model.render_model | google/gemini-3.5-flash | The model used for renders at run/serve time. |
model.compile_model | google/gemini-3.5-flash | The model used for the compile sessions. It is part of the IR cache key. |
model.temperature | 0 | Sampling temperature. |
model.max_turns | 200 | The per-session turn ceiling. |
model.base_url | (provider default) | Override the OpenAI-compatible base URL. Optional for a built-in; required (with api_key_env) for a custom vendor. |
model.api_key_env | (provider default) | The env var holding the API key (e.g. ANTHROPIC_API_KEY). Optional for a built-in; set it to read from a non-default variable. |
The compile model is a component of the content-addressed cache key (contract-set fingerprint, SDK version, model id). Changing it invalidates the cache and forces a recompile.
Choosing a model provider
By default Reactor talks to models through OpenRouter, but it is not bound to
it. The CLI drives one bounded @openai/agents session per render/compile step,
and any vendor with an OpenAI-compatible Chat Completions endpoint plugs in by
naming it in model:. Point at OpenAI, Anthropic, or Google directly -- no
OpenRouter account required:
# Anthropic, directly (set ANTHROPIC_API_KEY in your env or a .env)
model:
provider: anthropic
render_model: claude-haiku-4-5
compile_model: claude-haiku-4-5Each built-in provider resolves to an endpoint + a key env var:
provider | Endpoint | Key env var | Example model id |
|---|---|---|---|
openrouter (default) | https://openrouter.ai/api/v1 | OPENROUTER_API_KEY | google/gemini-3.5-flash |
openai | https://api.openai.com/v1 | OPENAI_API_KEY | gpt-4o-mini |
anthropic | Anthropic Messages API (native, see below) | ANTHROPIC_API_KEY | claude-haiku-4-5 |
google | https://generativelanguage.googleapis.com/v1beta/openai/ | GEMINI_API_KEY | gemini-2.5-flash |
openrouter, openai, and google are OpenAI-compatible Chat Completions
surfaces. anthropic is special: the CLI routes it through Anthropic's native
Messages API (via the bundled @openai/agents AI-SDK adapter over
@ai-sdk/anthropic), because Anthropic's OpenAI-compat endpoint ignores
response_format and can't drive Reactor's structured compile/render. You don't
install or wire anything -- provider: anthropic just works, structured outputs and
all. An optional base_url points the adapter at a proxy in front of the Messages
API.
For any other OpenAI-compatible vendor (or a self-hosted gateway), name it freely
and supply both base_url and api_key_env:
model:
provider: together
base_url: https://api.together.xyz/v1
api_key_env: TOGETHER_API_KEY
render_model: meta-llama/Llama-3.3-70B-Instruct-Turbo
compile_model: meta-llama/Llama-3.3-70B-Instruct-Turboapi_key_env also overrides a built-in's key var -- handy when, say, your
OpenRouter key lives under a different name. Run reactor doctor to confirm the
configured provider's key is visible (it reports the exact env var, never printing
the value), and reactor doctor --live to drive one real round-trip against it.
The key is read from the named env var first, then a .env discovered by walking
up from the project directory. A missing key fails compile/run/serve with a
non-zero exit and a message naming the exact variable to set -- it never
silently misdirects you to OpenRouter, and never exits 0 on an auth dead-end.
Claude, two ways. provider: anthropic drives Claude directly through the
native Messages API and fully supports the structured compile/render path -- it is
the recommended route for Anthropic models. If you already aggregate models through
one gateway, provider: openrouter with a render_model/compile_model like
anthropic/claude-haiku-4-5 also works. Avoid pointing a base_url at Anthropic's
OpenAI-compatible endpoint (https://api.anthropic.com/v1/): it ignores
response_format and rejects Reactor's JSON-schema with
400 response_format.json_schema.strict. See the
SDK provider guide
for the underlying @openai/agents wiring.
sandbox
The sandbox block is the render threat-model knob.
| Key | Default | Meaning |
|---|---|---|
sandbox.mode | none | none runs renders in the SDK's cwd-scoped, bounded shell. docker runs each render command in a throwaway, network-disabled container. (A third value, unix-local, is accepted by the config parser but is not yet realized -- it currently behaves as none.) |
sandbox.shell_timeout_ms | 300000 | The per-command time bound (300 seconds) for the bounded shell. |
sandbox.image | node:22-bookworm-slim | The container image, when mode: docker. Falls back to node:22-bookworm-slim when unset. |
sandbox.network | (forced off) | Accepted for forward-compatibility but not yet honored: the docker runner currently forces --network=none on every render command regardless of this value (network isolation is the threat-model default). |
See connectors and sandbox for the full render-isolation behavior, including the Docker-absent fallback.
gateways
Each gateways entry is an external-driven entry point bound to a connector.
| Key | Meaning |
|---|---|
node | The gateway node id (must match a kind: gateway contract). |
source_id | The connector source id (defaults to the node id). Keys the durable idempotency cursor. |
poll | An optional poll cadence for the gateway. |
connector | The connector definition: its type plus type-specific fields such as id_field and items. |
See connectors and sandbox for the built-in connector types (static, http, file) and the connectors.{cjs,js} plugin shape.
reactors
A reactors list hosts N isolated reactors in one serve process. When the list is empty (the default), the project is single-reactor and the top-level state and gateways are that one reactor.
| Key | Default | Meaning |
|---|---|---|
name | reactor-<index> | The reactor's name, which becomes its HTTP namespace prefix /<name>/.... |
project | the project dir | The contracts directory for this reactor. |
state_dir | <state.dir>/<name> | This reactor's isolated durable state directory. |
gateways | [] | This reactor's gateways. |
Each entry is isolated: its own contracts directory, state directory, substrate, schedule, and cursors, so one reactor never corrupts another. See the multi-reactor host for how --concurrency parallelizes across reactors.
Environment variables
| Variable | Meaning |
|---|---|
OPENROUTER_API_KEY | The live key for the default provider. Required by compile, run, serve, and trigger when model.provider is openrouter. Read from the process env first, then a .env file discovered from the working directory upward. |
<model.api_key_env> | The live key for a non-default provider -- OPENAI_API_KEY, ANTHROPIC_API_KEY, GEMINI_API_KEY, or whatever model.api_key_env names. Resolved the same way (env first, then a discoverable .env). See Choosing a model provider. |
REACTOR_OFFLINE | Set to 1 (or true) to force offline mode. Equivalent to the --offline flag. |
Global flags
These flags are honored by every command and override the file:
| Flag | Meaning |
|---|---|
--state-dir <path> | Override state.dir. |
--project <dir> | The project directory containing reactor.yml (default .). |
--json | Machine-readable JSON output. |
--offline | Force offline mode (sets REACTOR_OFFLINE=1). |