Skip to content

Configuration

A corralai brain is configured entirely through environment variables — there is no config file to hand-edit, so dev mode and a production deployment run the exact same code paths. Everything below is optional; a brain with no environment set at all boots in dev mode against sensible ~/.claude/* defaults.

Every store defaults under ~/.claude/; point them elsewhere to relocate a brain’s data or to isolate a test/demo brain.

Variable What it is Default
CORRALAI_ADDR Listen address (loopback by default; front it with a tunnel/proxy) 127.0.0.1:9019
CORRALAI_DB Coordination SQLite (missions, tasks, claims) ~/.claude/corralai_coord.sqlite3
CORRALAI_MEMORY_DB Memory DuckDB (the searchable knowledge store) ~/.claude/corralai_memory.duckdb
CORRALAI_MEMORY_DIR Where new memory entries are written (markdown files) ~/.claude/projects/default/memory
CORRALAI_RECORDINGS_DB Recordings DuckDB for scrubbed replay exports ~/.claude/corralai_recordings.duckdb
Variable What it is Default
CORRALAI_TASK_STALL_SECONDS How long a claimed-but-untouched task may sit before the brain treats it as stalled and lets the herd reclaim it 300

This is the knob behind graceful degradation over deadlock: a worker that claims a task and then dies (crash, quota blip) must never strand the mission — after the stall window its lease is reclaimable by the rest of the herd.

With CORRALAI_OIDC_ISSUER unset, auth is disabled (dev mode — any caller is trusted). Set it to any OIDC provider to turn auth on.

Variable What it is
CORRALAI_OIDC_ISSUER / CORRALAI_OIDC_AUDIENCE The primary trusted OIDC issuer + expected audience (empty issuer ⇒ auth off)
CORRALAI_OIDC_CLIENTS Extra trusted clients, "issuer|aud,issuer|aud"
CORRALAI_ALLOWED_PRINCIPALS Day-0 seed of member emails (the DB is canonical afterward)
CORRALAI_ADMIN_PRINCIPALS Day-0 seed of superuser emails
CORRALAI_ALLOWED_HOSTS Accepted Host headers (add the public hostname a tunnel exposes)
CORRALAI_MEMORY_OWNERS Emails allowed to read/write memory
Variable What it is
CORRALAI_GIT_TOKEN Forge PAT for clone + PR; empty ⇒ repo engine off unless CORRALAI_REPO_ENABLE=1
CORRALAI_FORGES Multi-forge config (GitHub / GitLab / Gitea) — each token is only ever used against its own host
CORRALAI_REPO_WORKSPACE Root dir for per-mission working copies

The token lives only in the brain — it is scrubbed from the environment, injected into a push URL for one call, and never written to .git/config. See Trust & security.

Variable What it is
MODEL_BACKEND / OPENAI_BASE_URL Bring your own model — point at any OpenAI-compatible endpoint (local Ollama to frontier)
CORRALAI_ADVPOOL_MODELS The hosted pool’s herd: mutant-generator=<m>,test-writer=<m>,test-critic=<m> (test-critic=off or omitted leaves the critic unstaffed). Required — see below
CORRALAI_EMBED_URL Embeddings endpoint for reference RAG + semantic code search
CORRALAI_MOTHERDUCK Fleet-sync target (md:<db> or a .duckdb path); empty ⇒ sync off
CORRALAI_BRAIN_ID Tag for this brain’s rows in the fleet (default: hostname)

corral has no default models — anywhere. The hosted adversarial pool used to cold-start on a hardcoded pair of small local models, which meant a brain nobody had configured still ran something. It no longer does: with no CORRALAI_ADVPOOL_MODELS the pool logs DISABLED — no models assigned and is never registered, and a run started without an assignment is refused. A malformed CORRALAI_ADVPOOL_MODELS is refused too, rather than quietly falling back to something you did not choose.

The models are yours to name, from any provider you hold a key for. The only rule is that the test-critic must differ from the test-writer — that decorrelation is what the verdict rests on, and it is a property, not a vendor, so any two distinct models satisfy it. (The critic may also be left unfilled: test-critic=off. It is advisory and never gates a verdict.)

The herd is the allowlist. The brain’s leaderboard can re-seat a role — but only to a model you named somewhere in the herd, only once that model has at least 5 completed tasks in that role, and only when it outranks your own pick on the same evidence. A model you never named cannot be routed to, whatever a worker reported about it, and the roster each run actually used is logged at run start.

Provider API keys and the worker token are configured with the embedded keystore rather than left in a plaintext env file:

Terminal window
corral secret set OPENAI_API_KEY # value read from stdin — never a CLI arg
corral secret set OPENROUTER_API_KEY # one OpenRouter key covers gemini + gpt + claude
corral secret list # names only, never values
corral secret rm GEMINI_API_KEY

Each secret resolves through env var → OS keyring → an age-encrypted file (first hit wins, so an env var always overrides a stored value). On a desktop the OS keychain (macOS Keychain, Windows Credential Manager, Linux Secret Service) holds it directly. On a headless server it’s an age-encrypted store under your config dir (CORRAL_CREDS_DIR to override), whose encryption identity comes from CORRAL_AGE_IDENTITY, a systemd credential (age-identity), or a 0600 key file — and fails closed if that source is present but malformed, so it never silently orphans your store. Secret values are redacted to a fingerprint in every log and error, and corral-agent reads OPENAI_API_KEY / ANTHROPIC_API_KEY / CORRALAI_BRAIN_KEY through the keystore — so they need never sit in the process environment at all.

See Running it for how these come together in dev vs. auth-on, and Multi-model herds for the model story.