Configuration
Anode reads JSON config plus environment variables. The important rule: the main config file is first-valid, not layered.
Config File
Section titled “Config File”Anode loads the first candidate that exists, passes safety checks, and parses:
$ANODE_CONFIG~/.config/anode/config.json./anode.json
Earlier files with errors are skipped. anode config list shows each
candidate’s status, and anode config doctor exits nonzero when any candidate
cannot be safely read or parsed.
Inspect the current discovery order and effective file:
anode config listPrint only the effective config path for scripts or editor integrations:
anode config pathWhen $ANODE_CONFIG is set but cannot be loaded, stdout still prints the
fallback effective path and stderr carries the warning.
Use anode config doctor in CI or release checks; it exits nonzero when a
config file cannot be parsed or safely read, and successful checks end with
Config doctor passed..
Run a command with a specific config:
ANODE_CONFIG="$PWD/anode.json" anode -x "summarize this repository"Minimal Config
Section titled “Minimal Config”{ "model": "anthropic/claude-sonnet-4-6", "profile": "craft", "providers": { "anthropic": { "apiKey": "{env:ANTHROPIC_API_KEY}" } }}You often do not need a config file. If you set a supported provider environment variable, Anode auto-registers that provider.
Top-Level Keys
Section titled “Top-Level Keys”| Key | Use |
|---|---|
model | Default model in provider/model format. |
profile | Default profile. Defaults to craft. Aliases: smart -> craft, rush -> quick, deep -> study, search -> find. |
providers | Provider map. |
profiles | Custom or overridden agent profiles. |
permissions | User-level permission rules. |
permissionPreset | unrestricted, careful, or strict; yolo is an alias for unrestricted. |
context | Context window and compaction settings. |
tools | Tool limits, disabled tools, toolbox dirs. |
shell | Shell tool environment and blocked command substrings. |
ui | TUI appearance and status display. |
web | Web tool controls. |
mcp | Workspace MCP trust and connection timeout. |
plugins | Process plugin paths and timeout. |
skills | Parsed skill settings. See Agents and skills. |
session | Local session storage settings. |
Provider Config
Section titled “Provider Config”{ "providers": { "work": { "type": "openai-compatible", "apiKey": "{env:WORK_LLM_KEY}", "baseURL": "https://llm.example.com/v1", "models": ["fast", "large"], "timeoutSec": 180, "maxRetries": 3 } }, "model": "work/fast"}Provider fields:
| Key | Default | Use |
|---|---|---|
type | provider ID | Provider type, such as anthropic, openai, zai, openai-compatible, custom, ollama, azure-openai, or bedrock. |
apiFormat | openai | Custom endpoint wire format: openai, anthropic, gemini, or generic. Only used with type: "custom". |
apiKey | - | Key value or resolver. |
baseURL | provider default | Override endpoint. Required for many custom endpoints. |
models | provider default | Allowed model names. Required for useful custom endpoints. |
modelDefaults | – | Per-model BYOK overrides: display name, reasoning capability, thinking budgets, token limits, vision support. See Per-Model Defaults. |
timeoutSec | 120 | Model request timeout in seconds. |
maxRetries | 2 | Transient retry count. Negative disables retries. |
Transient retries cover retryable HTTP statuses such as rate limits and server
errors. When a provider returns Retry-After, Anode honors seconds or HTTP-date
headers and caps the sleep at 30 seconds so a single request cannot stall the
agent indefinitely. Successful retries are still recorded in the run harness
ledger as provider retry telemetry with provider, model, attempt, status,
Retry-After, sleep duration, and transport error fields when available.
apiKey supports:
| Format | Meaning |
|---|---|
"{env:VAR}" | Read environment variable VAR. |
"VAR_NAME" | If the value is all-caps style, read that environment variable. |
"!command" | Run a shell command and use stdout. |
"literal" | Use the literal value. |
Per-Model Defaults
Section titled “Per-Model Defaults”Anode ships a built-in registry of model reasoning capabilities (effort range,
thinking requirements, defaults) for the public model catalogues of every
auto-detected provider. The TUI /effort, the --reasoning-effort flag, and
the model picker all consult this registry. Models the registry doesn’t know
fall back to a zero capability — meaning reasoning controls are hidden, no
display label is shown, and defaultEffort is ignored.
When you connect to a custom provider that exposes private aliases, preview
names, BYOK wrappers, or any model the public registry doesn’t list yet,
declare its defaults locally under providers[*].modelDefaults. This gives
Anode the same per-model knowledge a hosted UI like droid would carry,
without requiring a source fork.
{ "providers": { "my-proxy": { "type": "openai-compatible", "baseURL": "http://localhost:9000/v1", "apiKey": "{env:MY_PROXY_KEY}", "models": ["opus-latest", "preview-fast", "image-only"], "modelDefaults": { "opus-latest": { "displayName": "Opus Latest", "supportsReasoning": true, "defaultEffort": 4, "minEffort": 1, "maxEffort": 5, "thinkingRequired": true, "thinkingMaxTokens": 64000, "maxOutputTokens": 128000, "maxContextTokens": 1000000 }, "preview-fast": { "displayName": "Preview Fast", "supportsReasoning": true, "defaultEffort": 2, "minEffort": 1, "maxEffort": 4 }, "image-only": { "displayName": "Image Only", "supportsReasoning": false, "supportsVision": true } } } }}Fields (every field optional):
| Key | Use |
|---|---|
displayName | Friendly label shown in the model picker. Falls back to the wire model name. |
supportsReasoning | True if the model accepts reasoning effort at all. |
defaultEffort | Effort level 1–5 used when none is supplied. |
minEffort | Lowest effort the model accepts. Effort is clamped to this. |
maxEffort | Highest effort the model accepts. Effort is clamped to this. |
thinkingRequired | True when the model requires explicit thinking tokens (Anthropic-style). |
thinkingMaxTokens | Upper bound on the thinking budget when reasoning is enabled. |
maxOutputTokens | Cap on completion length. |
maxContextTokens | Total context window the model accepts. |
supportsVision | Set false for text-only models. Omit for the default (vision-capable when the provider supports it). |
Rules:
- Built-in registry entries always win. Overrides only fill in gaps for models the upstream registry does not list, so upstream updates remain authoritative.
- A defaults entry that is entirely zero/false is ignored — it would just shadow the default zero capability.
- Keys are wire model names (no
provider/prefix). The same model name may appear under multiple providers with different defaults; the last one loaded wins. - Entries from every configured provider’s
modelDefaultsare merged into a single runtime override map at startup. - The legacy field name
modelCapabilitiesis still accepted as an alias formodelDefaultsso older configs keep working.
Auto-Detected Providers
Section titled “Auto-Detected Providers”Anode auto-registers providers when these variables are set:
| Variable | Provider ID |
|---|---|
ANTHROPIC_API_KEY | anthropic |
OPENAI_API_KEY | openai |
ZAI_API_KEY | zai |
ZAI_CODING_API_KEY | zai-coding |
GEMINI_API_KEY | gemini |
GROQ_API_KEY | groq |
DEEPSEEK_API_KEY | deepseek |
MISTRAL_API_KEY | mistral |
FIREWORKS_API_KEY | fireworks |
XAI_API_KEY | xai |
OPENROUTER_API_KEY | openrouter |
TOGETHER_API_KEY | together |
CEREBRAS_API_KEY | cerebras |
DEEPINFRA_API_KEY | deepinfra |
HUGGINGFACE_API_KEY | huggingface |
OLLAMA_HOST | ollama |
Default model selection prefers Anthropic, then OpenAI, then Z.ai Coding, then Z.ai. Otherwise Anode uses the first configured provider model it can find.
Profiles
Section titled “Profiles”{ "profiles": { "security-review": { "model": "anthropic/claude-sonnet-4-6", "promptKind": "review", "readOnly": true, "tools": ["read", "finder", "web_search", "todo_read"], "maxTurns": 15, "defaultEffort": 4 } }}Profile fields:
| Key | Use |
|---|---|
model | Profile model override. |
promptKind | chat, quick, study, review, oracle, find, or aggman. Aliases: rush -> quick, deep -> study, search -> find. |
promptPath | Extra prompt text from a file, relative to workspace when not absolute. |
systemPrompt | Inline extra prompt text. |
tools | Allowed tool names or glob patterns. |
readOnly | Hide mutation tools from the model. |
maxTurns | Turn limit. |
defaultEffort | Reasoning effort level from 1 to 5, clamped by model capability. |
See Profiles.
Canonical built-in profile names are craft, quick, study, review,
oracle, find, and aggman. See Profiles for aliases and
custom profile configuration.
Context
Section titled “Context”{ "context": { "window": 200000, "reserveTokens": 16384, "keepRecent": 20000, "compactPercent": 0.85, "compactEnabled": true, "maxOutputTokens": 16384 }}| Key | Default | Use |
|---|---|---|
window | 200000 | Approximate model context window. A per-model maxContextTokens (under providers[*].modelDefaults) wins for that model. |
reserveTokens | 16384 | Space reserved for response when compactPercent is unset. |
keepRecent | 20000 | Recent tokens preserved during compaction. |
compactPercent | 0.85 | Share of the effective context window allowed before auto-compaction fires. Set to a value in (0, 0.99]; values are clamped. Pass a negative value (-1) to fall back to the legacy window - reserveTokens cutoff. |
compactEnabled | true | Enable automatic compaction. |
maxOutputTokens | 16384 | Max response tokens sent to provider. |
Per-model context windows
Section titled “Per-model context windows”If a model accepts a larger or smaller window than the global default, set
it on the provider entry instead of the global context.window:
{ "providers": { "anthropic": { "modelDefaults": { "claude-opus-4-7": { "maxContextTokens": 1000000 }, "claude-haiku-4-5": { "maxContextTokens": 200000 } } } }}Resolution order when the agent picks the effective window: per-model
maxContextTokens → context.window → built-in default (200000). The
compactPercent knob is then applied to whichever window won, so a 1M
token model auto-compacts around 850k tokens with the default 85% trigger.
{ "tools": { "bashTimeout": 60, "externalTimeout": 30, "maxResultSize": 20000, "finderResultLimit": 100, "disabled": ["web_*"], "toolboxDirs": ["./tools"] }}| Key | Default | Use |
|---|---|---|
bashTimeout | 30 | Shell timeout in seconds. |
externalTimeout | 20 | MCP/toolbox/plugin timeout in seconds. |
maxResultSize | 20000 | Max tool result characters passed back to the model. |
finderResultLimit | 50 | Default finder result limit. |
disabled | [] | Tool names or glob patterns to disable globally. |
toolboxDirs | [] | Executable tool directories. |
{ "shell": { "defaultShell": "bash", "loginShell": false, "env": ["NODE_ENV=test"], "blockedCmds": ["rm -rf /"] }}Shell commands use non-login -c execution by default so validation and
benchmark runs do not load user startup files. Set loginShell to true only
when commands intentionally depend on login-shell initialization.
blockedCmds are substring matches applied before shell execution.
{ "ui": { "theme": "dark", "showTokens": true, "showElapsed": true, "showGitStats": true, "composerHeight": 3, "startScreen": "chat" }}startScreen accepts chat, plan, diff, terminal, files,
connections, settings, or logs. dashboard is an alias for Chat.
Unknown values fall back to Chat.
{ "web": { "enabled": true, "maxBytes": 50000, "blockedHosts": ["internal.example.com"], "userAgent": "Anode/0.4" }}ANODE_WEB_ENABLED is used only when web.enabled is not set. Values 0,
false, no, off, and disabled disable web access.
{ "mcp": { "allowWorkspace": false, "connectTimeout": 10 }}Workspace MCP config requires trust unless allowWorkspace is true or
ANODE_ENABLE_WORKSPACE_MCP=true is set.
See MCP.
Plugins
Section titled “Plugins”{ "plugins": { "enabled": true, "paths": ["~/.config/anode/plugins/my-plugin"], "timeoutSeconds": 5 }}See Hooks and plugins.
Skills
Section titled “Skills”{ "skills": { "dirs": ["~/.config/anode/skills"], "maxSize": 65536, "allowMCP": false }}dirs adds extra skill roots to scan. Relative entries resolve from the
workspace root. maxSize reads up to 65,536 bytes of SKILL.md content by
default; longer files end with ...[truncated] after that prefix. allowMCP
enables skill-local MCP activation; profile and tool policy still apply.
Sessions
Section titled “Sessions”{ "session": { "autoSave": true, "maxSessions": 100, "storePath": "~/.config/anode/sessions" }}maxSessions default is 0, meaning no configured retention limit.
Other Files Anode Reads
Section titled “Other Files Anode Reads”Subsystems have their own files:
| Area | Files |
|---|---|
| MCP | ~/.config/anode/mcp.json, trusted anode.json, trusted .mcp.json |
| LSP | ~/.config/anode/lsp.json, .lsp.json, .anode/lsp.json |
| Hooks | ~/.config/anode/hooks.json, .anode/hooks.json, hooks.json |
| Permissions | permissions in main config, .agents/permissions.json, .anode/permissions.json |