Getting started
Install
Section titled “Install”go install github.com/pdbethke/corralai/cmd/corral@v1.0.0-rc.13Or clone and build from source:
git clone https://github.com/pdbethke/corralaicd corralaigo build ./...See it work first: corral demo
Section titled “See it work first: corral demo”One command, no setup beyond a provider key:
corral demo --writer-model <model> --mutant-model <model> --critic-model <model>It writes a small Go package with a five-clause password rule and a test that checks
only two of them, then audits it with the real certify --local. You need a Go
toolchain — you installed corral with one — and one provider key. No venv, no
database, no fixtures. The project is left on disk so you can read the test and see
what it never asserts.
Then point it at your own code, with corral doctor first.
Your first audit: corral certify --local
Section titled “Your first audit: corral certify --local”The fastest way to see corral do the thing it’s for — certify a change by execution, not opinion — is one command, off your own key, no daemon:
export ANTHROPIC_API_KEY=sk-ant-... # or OPENAI_/GEMINI_/OPENROUTER_API_KEY
corral certify --local \ --code path/to/your/file.go \ --goal "what this code must guarantee" \ --writer-model claude-sonnet-5 \ --mutant-model claude-sonnet-5 \ --critic-model claude-haiku-4-5 \ -- go test ./...Those model names are an example, not a default — corral has none. Every seat is
yours to name, from whichever provider you hold a key for; the models above are what
we run. The only rule is that the test-critic must differ from the test-writer,
because that decorrelation is what the verdict rests on — a property, not a vendor,
so any two distinct models satisfy it. --critic-model off drops the critic
entirely (advisory, never gates the verdict). A run with an unnamed seat is refused,
and the refusal reports which provider credentials it can see.
That runs the full adversarial testing pool in-process: mutant-generators
seed goal-violating bugs into your code, your own test is scored against them
by executing it in a jail (never a self-report), a test-writer proves any gap
is real by writing and killing the test you were missing, and a decorrelated
test-critic reads your suite cold. You get a signed verdict — certified or
needs-review — and can re-check it offline any time:
corral certify verify <record> --pubkey "$(corral certify pubkey)" --allow-unanchoredA record’s own embedded key is never a trust anchor, so verify refuses to run
without --pubkey (or --brain); --allow-unanchored accepts a record that
was not logged to a transparency witness.
Before you spend a run: corral doctor
Section titled “Before you spend a run: corral doctor”An audit costs real money and real minutes, and it is almost always the
environment that stops one — the sandbox won’t start, the toolchain is invisible
inside it, the key for the model you assigned is missing, the file has no paired
test. Discovered one at a time, each of those costs another run, and most cost money
to learn. doctor checks them all up front for free — no model is ever called —
in the order the audit itself would hit them, so the first FAIL is the first thing
to fix:
corral doctor --code path/to/your/file.go \ --writer-model claude-sonnet-5 --mutant-model claude-sonnet-5 \ -- go test ./... [ok ] sandbox starts [ok ] toolchain reachable inside the sandbox [FAIL] credential for mutant-generator (claude-sonnet-5) agentbackend: ForModel: model "claude-sonnet-5" needs an Anthropic key — set ANTHROPIC_API_KEY
1 check(s) failed — fix these before spending a run.Every argument is optional and each unlocks more checks: --code/--test add the
test-pairing check, a test command after -- adds the in-sandbox toolchain check,
and --mutant-model/--writer-model/--critic-model check the credential for
exactly the models you plan to route to. It exits non-zero if anything failed.
Two things it deliberately does not check, because both need a real seeded
workspace: whether your suite passes on unmutated code inside the sandbox — the
most common way an audit dies — and whether a multi-file project needs --repo-dir.
certify --local reports the first as COULD-NOT-GRADE, with the runner’s own
output.
On a file with several functions the generation fans out — one seat per
group of functions, so every function gets probed, not just whichever one a
single generator happened to pick (--max-shards N bounds the width; the
default is 8 — only --swarm sizes itself to your machine). Name a --shadow-model and a
challenger model attacks the same regions in parallel, purely to measure one
model against another on identical ground — it’s recorded for comparison and
never part of the verdict (off unless named, like every seat). And a mutant
that makes your suite hang is killed fast and counted as caught, so a runaway
loop can’t stall the audit (--test-timeout overrides the auto-derived cap).
Corral has no default models — every seat is named by you, and a run with an
unnamed seat is refused. One key can still satisfy the distinctness rule on its
own: two different models from the same provider (Sonnet writing and mutating,
Haiku critiquing, say) off that single ANTHROPIC_API_KEY. Naming
--critic-model gemini-3.6-flash plus a Google key (GEMINI_API_KEY or
GOOGLE_API_KEY) routes the critic to Gemini via the OpenAI-compatible Google
endpoint — a real cross-vendor critic, writer and mutant-generator unchanged. A
missing key fails the run closed instead of silently falling back. It supports
Go, Python (pytest), Ruby (minitest/RSpec), JavaScript (node:test),
TypeScript (tsc + node:test), and PHP (PHPUnit) — the language is inferred
from --code’s extension.
It always runs sandboxed. bwrap is the Linux default; --jail container
falls back to docker/podman; macOS uses sandbox-exec. There’s no unsandboxed
option. On Ubuntu 24.04+, apparmor disables unprivileged user namespaces by
default and bwrap won’t start — the error message spells out the exact
one-line fix (or pass --jail container). And the language toolchain has to be
jail-visible: installed system-wide under /usr (your distro’s
golang/python3 package), not a --user/snap/pyenv install — a snap go or
a pip install --user pytest is invisible inside the sandboxed mount
namespace and the run will fail closed looking for it.
Python: you do not have to install pytest system-wide. Plenty of developers
will not contaminate their system interpreter for an audit tool, and they
shouldn’t have to. The jail binds /usr but not your home directory, so what
actually breaks is a runner living under $HOME — pip install --user, pyenv,
or a virtualenv in the default ~/.local/share/virtualenvs. Put the virtualenv
inside the project instead, at ./.venv or ./venv, and --repo-dir
auto-detects it and bind-mounts it read-only into the jail (see
Dependency dirs).
Then run your suite through it:
python3 -m venv .venv && .venv/bin/pip install pytest # or:PIPENV_VENV_IN_PROJECT=1 pipenv install --dev pytest
corral certify --local --repo-dir . --code pkg/thing.py --test tests/test_thing.py \ --goal "…" -- .venv/bin/python -m pytest -qpipenv’s default venv location is outside the project, which is exactly the
invisible case — PIPENV_VENV_IN_PROJECT=1 is what moves it somewhere the jail
can see.
Walk through a real verdict end to end in Your first audit, in detail.
The brain (optional)
Section titled “The brain (optional)”Everything on this page runs with no server. The brain is corral’s optional coordination daemon — the substrate under a coordinated herd, a live console and the human-gated proposal loop. Nothing in it is required for a verdict, and nothing in it is read by an audit. If you want it, start with Running it, which says what it is and is not, or read the brain, and what it is not.
Common knobs
Section titled “Common knobs”MODEL_BACKEND/OPENAI_BASE_URL— pin every seat to one gateway endpoint (OpenRouter, a local daemon); without it each seat routes by the model you nameCORRALAI_MODELS_FILE/CORRALAI_MODELS— the model registry, so seats can be named by aliasCORRAL_LEDGER— wherecertify --repowrites its entry and reads its prior (default: the audited repo’s.corral/ledger/)CORRALAI_CACHE_DB— corral’s local cache of derived goals and test selectionsCORRALAI_CERTIFY_KEY_FILE— where the signing key seed lives (--localverdicts,--attest)
The daemon’s own knobs (CORRALAI_OIDC_ISSUER, CORRALAI_GIT_TOKEN,
CORRALAI_MOTHERDUCK, …) are on Running it; every variable
is documented from the binary’s own source comment on
corral’s CLI page.