| title | Configuration | ||
|---|---|---|---|
| type | reference | ||
| summary | Where Woopcode stores its settings, what each key means, and what happens when the file breaks. | ||
| prerequisites | |||
| related |
|
||
| since | 0.6.0 |
| Platform | Directory |
|---|---|
| macOS, Linux | $XDG_CONFIG_HOME/woopcode/, or ~/.config/woopcode/ |
The directory is created on first run.
| File | Contents |
|---|---|
providers.json |
Provider keys, the default provider, the selected model, the approval mode, session retention |
sessions/ |
Saved conversations, one directory per project |
models.json |
The model list |
Configuration is global, not per-repository. Conversation history is the one
thing scoped to where you are working: it lives under
sessions/<project>/<session-id>.json, keyed by the repository root. See
Sessions & history.
{
"defaultProvider": "google",
"selectedModel": "gemini-3.6-flash",
"approvalMode": "auto-read-only",
"retentionDays": 30,
"providers": {
"google": { "type": "api", "apiKey": "..." }
}
}| Key | Type | Default | Description |
|---|---|---|---|
defaultProvider |
string |
"google" |
Which provider a session starts with |
selectedModel |
string |
first model of the provider | Model id, as listed by woopcode models |
approvalMode |
string |
"auto-read-only" |
One of the four approval modes |
retentionDays |
number |
30 |
Days a session survives after its last turn; 0 keeps them forever |
sandbox |
boolean |
false |
Whether sessions start with sandboxed execution on |
providers |
object |
three entries | Keyed by provider id |
providers.<id>.type |
string |
"api" |
How the provider authenticates |
providers.<id>.apiKey |
string |
"" |
The stored key |
Unrecognised keys are preserved when Woopcode rewrites the file, so anything you add by hand survives.
:::warning
apiKey is stored in plain text, with no encryption and no keychain
integration. The file is only as protected as your home directory.
:::
You can, and Woopcode will read it. The safer path for the values that have a
command is to use the command, because it validates:
woopcode providers login verifies a key before saving
it, and /approval cannot write a mode that does not exist.
An approval mode the parser does not recognise falls back to the default rather than being treated as permissive. An unreadable setting must not be able to widen what runs without asking.
A providers.json or a session file that is not valid JSON — a truncated
write, a bad hand edit — is moved aside rather than crashing every command that
touches it:
Could not read provider config (invalid JSON). Moved it to
~/.config/woopcode/providers.json.corrupt-1785508675645 and started from
defaults.
Your broken copy is kept under that .corrupt-<timestamp> name; the next
launch starts clean.
A provider that an earlier version offered and Woopcode has since dropped is removed from your config on startup — but only if it has no key stored. An entry with a key is left alone: deleting a credential silently would hide something you may want to remove yourself.
| Variable | Effect |
|---|---|
XDG_CONFIG_HOME |
Moves the config directory on macOS and Linux |
Woopcode reads an API key from the environment when one is present, and prefers it over the stored config. This is what lets it run somewhere with no writable home directory and no terminal to run setup in — a CI job, a benchmark container built fresh for every trial.
They are checked in this order, and the first one set wins:
| Variable | Provider |
|---|---|
WOOPCODE_API_KEY |
Whatever WOOPCODE_PROVIDER names, or google |
GEMINI_API_KEY |
google |
GOOGLE_API_KEY |
google |
GOOGLE_GENERATIVE_AI_API_KEY |
google |
OPENAI_API_KEY |
openai |
ANTHROPIC_API_KEY |
anthropic |
An unusable provider is treated differently depending on which variable named
it. WOOPCODE_API_KEY with WOOPCODE_PROVIDER is an instruction addressed to
Woopcode, so naming a provider it has no client for is an error at startup
rather than a failure on the first turn. A vendor variable is not an
instruction — ANTHROPIC_API_KEY is usually exported for some other tool
sharing the shell — so an unusable one is skipped and the search continues to
the next, or to the stored config.
:::note
A key in the environment is used as-is and never written to providers.json.
Nothing is stored, which is the point for a container that is discarded after
the run.
:::
| Variable | Default | Effect |
|---|---|---|
WOOPCODE_PROVIDER |
google |
Pairs with WOOPCODE_API_KEY |
WOOPCODE_MAX_ITERATIONS |
40 |
Steps a turn may take before it stops to ask whether to keep going. Interactively the ceiling is a checkpoint, so it is set to catch a stuck loop rather than to ration requests — the provider rations those itself, and answering the checkpoint grants another 40. A headless run has nobody to ask, so this is the whole budget and exhausting it exits 2 |
WOOPCODE_MAX_WALL_SEC |
unset (off) | Wall-clock seconds a turn may take, counted from process start. Pass the whole budget the harness enforces; a reserve is held back internally so the last step, the final answer and the session write still land. The loop stops on whichever budget binds first, and a spent clock exits 2 like a spent ceiling. Unset interactively, where a person decides when a turn has gone on too long |
WOOPCODE_MAX_ATTEMPTS |
3 |
Tries per provider request before the error surfaces |
WOOPCODE_TOOL_HISTORY_BUDGET |
unset (off) | Characters of tool history to keep before older results are compacted. Off by default — see the measurements in runtime/compaction.ts |
WOOPCODE_THINKING_BUDGET |
-1 |
Reasoning depth; see below |
WOOPCODE_NON_INTERACTIVE |
unset | 1 stops Woopcode opening the setup wizard, so a missing key fails loudly instead of blocking on a prompt nothing can answer. CI=true does the same |
A value that is not a positive integer is ignored with a warning on stderr, and the default is used. An unreadable setting never silently changes behaviour.
Takes off, -1 (automatic, the default), or a token count. The three
providers accept different things, so the same value does not mean the same
thing everywhere — and a number is never faked into a budget the provider did
not apply.
| Value | OpenAI | Anthropic | |
|---|---|---|---|
off |
No thinking config sent | reasoning.effort: none |
thinking: disabled |
-1 |
Model decides | Model default | Model decides |
| a count | Used as the token budget | Model default | Model decides |
Only Gemini takes a token count. Current Claude models reject an explicit
budget, and OpenAI takes an effort level rather than a number, so on both a
count falls back to letting the model decide — which is what -1 already
meant. On Gemini, budgets below roughly a thousand are ignored by the model
rather than honoured.
Read only when sandboxed execution is on.
E2B_API_KEY is the one that decides whether it can be turned on at all —
without it, --sandbox and /sandbox on refuse rather than falling back to
running on this machine.
| Variable | Default | Effect |
|---|---|---|
E2B_API_KEY |
unset | Required. No key, no sandbox |
WOOPCODE_SANDBOX_TEMPLATE |
E2B's base image | Template id to build sandboxes from |
WOOPCODE_SANDBOX_TIMEOUT_MS |
600000 |
How long a sandbox lives untouched. Refreshed before every command, so this is the window a single command has to finish in — ten minutes rather than E2B's five, because five is run_terminal's own timeout and a command using its whole budget would race the reaper |
WOOPCODE_SANDBOX_MAX_FILE_BYTES |
1048576 |
Files larger than this are not sent |
WOOPCODE_SANDBOX_NETWORK |
full |
none blocks all egress from the sandbox. --sandbox-network overrides it |
WOOPCODE_SANDBOX_ENV |
unset | Comma-separated names to forward into the sandbox |
WOOPCODE_SANDBOX_SETUP |
unset | A command run once after the workspace is first pushed |
As with the behaviour variables above, a value that is not a positive integer is
ignored and the default is used, and an unreadable network mode falls back to
full rather than to whatever a typo happened to spell.
:::warning
WOOPCODE_SANDBOX_ENV is an allowlist, and it cannot be used to forward the
agent's own credentials. WOOPCODE_API_KEY, E2B_API_KEY and any provider key
are refused even when named explicitly, and the refusal is printed rather than
silent — a token that was quietly not forwarded looks like a broken build much
later.
:::
- Sandboxed execution — what the boundary does and does not cover
- Configuring providers — the task, not the schema
- Sessions & history — what a session holds, and how to resume one