---
title: Environment variables
description: Every environment variable reeval reads, and the flag each one stands in for.
---
> ## Documentation Index
> Fetch the complete documentation index at: https://docs.reeval.ai/llms.txt
> Use this file to discover all available pages before exploring further.

`reeval run` loads `.env.local`, then `.env`, from the config's directory: where the `reeval.config.ts` reeval found (or the `--config` file) lives, not the current directory. Variables already set in your shell win over both files, and `.env.local` wins over `.env`. `reeval analyze` loads them the same way, for its model key; other commands don't read env files.

```bash title=".env.local"
CURSOR_API_KEY=...
TYPESAFE_AI_API_KEY=...
```

## Credentials

| Variable | Used for |
|---|---|
| `CURSOR_API_KEY` | `cursor()`. Stays on the host: the sandbox sees a placeholder, swapped for the key only on Cursor's endpoints. |
| `ANTHROPIC_API_KEY` | `claudeCode()`, the same way. Its adapter never reads `AI_GATEWAY_API_KEY`, `ANTHROPIC_AUTH_TOKEN`, `ANTHROPIC_BASE_URL` or a Claude subscription. Also `anthropic/*` judges, analysis models and compare-question models, directly. |
| `GEMINI_API_KEY` | `antigravity()`: a Gemini API key, the same way. |
| `TYPESAFE_AI_API_KEY` | `typesafe-ai/*` judges, such as Jev, directly. |
| `OPENAI_API_KEY` | `codex()`, and `fx()` on a bare OpenAI id: an OpenAI API key, brokered the same way. Also `openai/*` judges, analysis models and compare-question models, directly. |
| `GOOGLE_GENERATIVE_AI_API_KEY` | `opencode()`, `goose()` and `pi()`: a Gemini API key, brokered the same way. Also `google/*` judges, analysis models and compare-question models, directly. |
| `XAI_API_KEY` | `grokBuild()`: an xAI API key, brokered the same way. |
| `OPENROUTER_API_KEY` | `openrouter()`'s key, read only when the config or profile lists [`openrouter()` in `gateway`](/reference/gateway#openrouter) (or a judge pins it): for Claude Code, Codex, Goose, OpenCode and Grok Build, and for any `provider/model` analysis, compare-question or capture model, or decision-model judge (such as `typesafe/jev-1.14`), that resolves to it. Without `openrouter()`, reeval never reads it; `reeval doctor` checks it against OpenRouter's key endpoint. |
| `AI_GATEWAY_API_KEY`, `VERCEL_OIDC_TOKEN` | `vercel()`'s key, read only when the config or profile lists [`vercel()` in `gateway`](/reference/gateway#vercel-ai-gateway) (or a judge pins it): for Claude Code, Codex, Goose, pi, OpenCode, fx and Grok Build, and for any `provider/model` analysis, compare-question or capture model, or decision-model judge (such as `typesafe-ai/jev-1.14.0`), that resolves to it. `VERCEL_OIDC_TOKEN` stands in when `AI_GATEWAY_API_KEY` isn't set, and an expired one stops the plan. Without `vercel()`, reeval uses no gateway and never reads them. |
| Any name in `secret.fromEnv('NAME')` | Read on the host and passed into the sandbox (or brokered, or to checks only), redacted everywhere |

A credential with whitespace or a line break in it (a key copied from a wrapped terminal line) stops the run with exit 2, naming the variable and the character. For `analysis.model`'s key it's a plan warning instead, since insights never stop a run.

## reeval settings

| Variable | Same as | Effect |
|---|---|---|
| `REEVAL_PROFILE` | `--profile` | The profile to size the run with (and plan `reeval image build` with); the flag wins |
| `REEVAL_CONCURRENCY` | `--concurrency` | Beats the config's `concurrency`, loses to the flag |
| `REEVAL_UI` | `--ui` | `live`, `plain` or `agent` |
| `REEVAL_LOG_ORDER` | `--log-order` | `stream` or `grouped` |
| `REEVAL_LOG_PREFIX` | `--log-prefix` | `task` or `none` |
| `REEVAL_OUTPUT_LOGS` | `--output-logs` | `full`, `errors-only` or `none`. Default `errors-only`: only failing attempts print their detail lines (`full` with `-v`) |
| `REEVAL_HEARTBEAT` | | Plain mode's "still running" line after silence, e.g. `30s`. Default `60s`; `0` turns it off |
| `REEVAL_DEBUG` | | `1` streams the hidden engine's output and the Docker sandbox and image-build logs to stderr, and prints stacks for unexpected errors |
| `REEVAL_MCP_TOKEN` | `--token` | `reeval mcp --http`: the bearer token every request must send; unset, none is needed. The flag wins |
| `REEVAL_PRICES` | | `reeval run --dry-run`'s cost estimate: a JSON file of USD per million tokens by model id, whose prices win over [models.dev](https://models.dev)'s, or `none` for tokens only and no fetch. A relative path is from the working directory; a field other than `input`, `output`, `cache_read` and `cache_write` exits 2. See [the cost estimate](/reference/cli/run#the-cost-estimate) |
| `REEVAL_NO_REPORT_HINT` | | `1` hides the line suggesting `reeval report` after an infra error or a crash |
| `REEVAL_RAW_TRANSCRIPT` | | `1` also writes the harness's unnormalised stream to `transcript.raw.jsonl` |
| `REEVAL_CAPTURE_BROWSER` | | `0` refuses the host `chrome-headless-shell` binary. `c.capture()` then throws. Same as `capture: { browser: false }` |
| `REEVAL_FAKE_EXEC` | | Not read from your shell: on Windows, a random id reeval sets in each fake-sandbox command's environment, so that stopping the command also stops every MSYS process it started |
| `REEVAL_BACKGROUND` | | Not read from your shell: in `docker()`, a random id reeval sets in a `c.exec(cmd, { background: true })` or `c.serve` command's environment, so that `kill()` also stops every process it started |
| `REEVAL_BASE` | | Not read from your shell: the build argument reeval passes to a `docker({ dockerfile })` build, naming reeval's image for the cell's harness. Start a Dockerfile `ARG REEVAL_BASE` / `FROM ${REEVAL_BASE}` |
| `REEVAL_PYTHON` | | Overrides the Python interpreter for the runner (skips `uv run` and `.venv`). Unless it's set, reeval-python's `reeval` command sets it to the project's Python: `$VIRTUAL_ENV`'s, else the nearest `.venv` from the working directory up, else the Python the command runs on |
| `REEVAL_BINARY` | | reeval-python's `reeval` command: the reeval binary to run, instead of the one it downloads |
| `REEVAL_OFFLINE` | | reeval-python's `reeval` command: `1` never downloads the binary; it must already be in `<cache>/reeval/bin/<version>/`, from an earlier run or put there by hand |
| `REEVAL_VERSION` | | The install scripts: the version to install. reeval-python's `reeval` command: run this version rather than reeval-python's own (its own version changes nothing); it's downloaded and checked against `SHA256SUMS` only |
| `REEVAL_DOWNLOAD` | | The install scripts and reeval-python's `reeval` command: a URL or `file://` directory holding the binaries and `SHA256SUMS`, instead of the version's GitHub release. An `http:` URL works with a warning; a redirect from `https` to `http` is refused |
| `REEVAL_PYTHON_SDK` | | Path to the `reeval-python` package on the host; prepends its `src/` to `PYTHONPATH` when the eval project does not list the dependency |
| `REEVAL_PROJECT_ROOT` | | Not read from your shell: project root the worker passes to a remote language runner |
| `REEVAL_RUNNER` | | Not read from your shell: `stdio` in a language runner reeval started, which talks to reeval on its stdin and stdout |
| `REEVAL_RUNNER_PROTOCOL` | | Not read from your shell: the runner protocol reeval speaks, set beside `REEVAL_RUNNER`. A runner on another protocol stops and names both versions |

## Install scripts

`install.sh` and `install.ps1` ([Standalone binary](/guide/binary)) read these; `reeval` itself doesn't.

| Variable | Default | What it does |
|---|---|---|
| `REEVAL_VERSION` | the newest release | The version to install, such as `0.13.0` |
| `REEVAL_INSTALL_DIR` | `~/.local/bin`, or `%LOCALAPPDATA%\reeval\bin` on Windows | Where `reeval` goes |
| `REEVAL_DOWNLOAD` | the version's GitHub release | A URL (or `file://` directory) holding the binaries and `SHA256SUMS`; needs `REEVAL_VERSION` |
| `GITHUB_TOKEN` | none | Sent to the GitHub API when looking up the newest release, which allows 60 requests an hour per IP address without it. A token the API refuses is dropped |

## CI and agent detection

| Variable | Effect |
|---|---|
| `CI` (not `false`) | Auto-records under `record: 'ci'`, refuses `.only` (exit 2), picks `defaultProfile.ci`, names runs with `ci` |
| `BUILD_NUMBER`, `BUILD_ID`, `RUN_ID`, `TEAMCITY_VERSION`, … | Plain output with grouped logs (Turborepo's CI list). Only `CI` affects recording and `.only` |
| `GITHUB_ACTIONS=true` | `::group::` blocks for passing attempts, `::error` annotations for failing ones, colour on |
| `GITHUB_STEP_SUMMARY` | `summary.md` is appended to it |
| `GITHUB_*`, `GITLAB_CI`, `CI_JOB_URL`, `CI_COMMIT_REF_NAME` | Branch, PR and CI run URL in the record |
| `CURSOR_AGENT`, `CLAUDECODE`, `COPILOT_AGENT`, `AI_AGENT=<name>`, … | Agent output (std-env's agent detection). Set `REEVAL_UI=plain` to see everything |
| `NO_COLOR`, `FORCE_COLOR` | Colour off or on. Otherwise on for a TTY or GitHub Actions, never in agent mode |

## Related

- [CLI overview and exit codes](/reference/cli)
- [Output modes](/concepts/output)
- [Secrets and egress](/guide/secrets-and-egress)
