config.json
User-facing settings: model, browser, memory, skills, MCP servers, Telegram. Versioned and auto-migrated. Lives at <stateDir>/config.json. Edit by hand or with atomic-agent config set.
Atomic Agent reads its settings from three places: a config file you can edit by hand, a set of environment variables for deployment and tuning, and a secrets file that keeps API keys and tokens out of your config. This page explains what lives where, and which one wins when they disagree.
If you just want the agent to run, you donβt need to touch any of this β Atomic Agent writes a sensible config.json on first launch. Come back here when you want to point at a different model, enable a feature, or wire up a secret.
config.json
User-facing settings: model, browser, memory, skills, MCP servers, Telegram. Versioned and auto-migrated. Lives at <stateDir>/config.json. Edit by hand or with atomic-agent config set.
ATOMIC_AGENT_* env vars
Deployment and operational overrides: paths, timeouts, retries, loop thresholds, browser launch. Read at bootstrap. Ideal for CI and containers.
.env secrets
API keys and tokens. Lives at <stateDir>/.env, mode 0600, never written into config.json, scrubbed from logs.
Everything sits under one state directory. By default that is ~/.atomic-agent; override it with ATOMIC_AGENT_STATE_DIR.
<stateDir>/ # default: ~/.atomic-agentβββ config.json # user-facing settings (versioned schema)βββ .env # secrets: API keys, tokens (chmod 0600)βββ sessions.sqlite # conversation transcriptsβββ memory.sqlite # profile facts, notes, lessons, proceduresβββ tasks.sqlite # durable task queueβββ traces/ # NDJSON trace files (one per session)βββ skills/ # globally installed skillsβββ models/ # downloaded GGUF models (managed mode)To find the active config file path for a given state dir, the runtime resolves <stateDir>/config.json. You can read the current config at any time, and replace it:
atomic-agent config get # print the whole config.jsonatomic-agent config set '<json>' # REPLACE the whole config.jsonAt bootstrap, loadConfig() reads .env, then config.json, then the process environment, merges them under fixed precedence rules, validates the result, and caches one immutable config object for the whole runtime lifetime.
flowchart TD
A["createAgentRuntime β getConfig()"] --> B{cached?}
B -->|yes| Z["return cached config"]
B -->|no| C["resolve stateDir<br/>(ATOMIC_AGENT_STATE_DIR or ~/.atomic-agent)"]
C --> D["load <stateDir>/.env<br/>into process.env<br/>(skips already-set keys)"]
D --> E["read <stateDir>/config.json<br/>migrate if old version"]
E --> F["merge: file wins for user keys,<br/>env wins for operational keys"]
F --> G["resolve ~ paths, asset dirs,<br/>LLM provider API keys"]
G --> H["validate (ConfigValidationError on bad input)"]
H --> I["freeze + cache"]
I --> Z
The merge is not a single βenv always winsβ or βfile always winsβ rule β it splits by what kind of setting it is.
config.json is a versioned JSON document. When Atomic Agent starts:
These are the top-level blocks you will edit most often. Defaults shown are the shipped defaults.
| Key | Type | Default | What it does |
|---|---|---|---|
version | int | 37 | Schema version (managed by migration; donβt hand-edit). |
localModels.url | string | http://127.0.0.1:8080 | HTTP endpoint for the local llama.cpp server. |
localModels.mode | managed | external | external | external expects a llama-server you run yourself; managed runs the daemon for you. atomic-agent models use <id> flips this to managed. |
localModels.managed.modelId | string | null | null | Active model in managed mode. Nothing is active until you pull a model β qwen-3.5-4b is only what the setup wizard suggests. |
localModels.managed.port | int | 19091 | Port for the managed chat daemon. The embedding daemon uses 19092. |
localModels.completionMaxTokens | int | 8192 | Max new tokens per completion. |
log.level | debug|info|warn|error | info | Log verbosity. |
agent.tokenBudget | int | 3000 | Max prompt tokens budgeted per step. |
agent.maxSteps | int | 25 | Default max steps per turn. |
agent.toolTimeoutMs | int | 60000 | Tool execution timeout. |
agent.approvalLevel | int 1β5 | 1 | How much the agent may do without asking. See the ladder below. |
agent.conversationMaxTokens | int | 32000 | Conversation-history token cap (clamped to context window). |
agent.worldSnapshotMaxTokens | int | 8000 | Cap for the browser ARIA snapshot section. |
browser.channel | chrome|msedge|chromium | chrome | Which browser to drive. |
browser.headless | boolean | false | Run the browser headless. |
browser.executablePath | string | β | Explicit browser binary (overrides auto-detect). |
http.enabled | boolean | true | Let the agent make outbound HTTP requests. |
http.approvalMode | string | never | Extra approval mode for HTTP on top of the ladder. never means the ladder alone decides. |
web.search.enabled | boolean | true | Enable the web search tool. |
web.search.provider | string | exa | Search backend. Falls back to DuckDuckGo when no key is set. |
projects.roots | string[] | [] | Directories whose direct children are your projects. See below. |
vision.enabled | boolean | true | Register the vision.describe tool (also requires a vision-capable model). |
analytics.enabled | boolean | true | Anonymous usage stats. See below. |
skills.disabled | string[] | [] | Skill names to hide from the registry. |
skills.taps | string[] | 3 repos | GitHub repositories skill browse / skill search read from. |
skills.clawhub.enabled | boolean | true | Use the ClawHub skill registry. |
skills.clawhub.apiBase | string | https://clawhub.ai | ClawHub endpoint. |
tracing.trace.enabled | boolean | β | Write NDJSON traces per session. |
tui.theme | string | auto | auto | TUI colour theme. |
agent.approvalLevel replaced the old boolean agent.approvalRequired in schema v37. It runs from 1 to 5 and is cumulative: each level stops asking about everything the level below it stopped asking about.
| Level | Name | Stops asking about |
|---|---|---|
1 | paranoid | Nothing β every gated action asks first. This is the default. |
2 | workspace | File writes, edits, and patches strictly inside the session working directory. |
3 | home | Adds file writes anywhere under your home directory, moves to Trash, archive extraction, and HTTP requests. |
4 | operator | Adds guarded shell commands, skill scripts, and process kills. |
5 | full trust | Everything, including browser navigation to non-web URLs and writes to the agentβs own trust config. |
--no-approval forces level 5 for a single process. The flag is one-directional β it can only lower strictness for one run, never raise it. Hardline shell-guard rules sit outside the ladder and block at every level.
analytics.enabled defaults to true. What is sent is anonymous: the provider and model names, plus a random install id generated on your machine. Message content, file paths, tool arguments, and your IP address are never transmitted. The same flag also governs crash reporting β turning it off disables both.
Turning it off is one key:
{ "analytics": { "enabled": false } }You can also toggle it from the TUIβs Privacy tab (/privacy), which persists the same key. Change this key inside your existing document rather than passing the fragment above on its own.
projects.roots lists directories whose direct children are your projects. It is what lets the os.fs.locate_project tool turn a fuzzy name like βmy raylib thingβ into a real path, so you can refer to a project by name instead of typing its full path.
{ "projects": { "roots": ["~/code", "~/work/clients"] } }Entries may be absolute or start with ~. Relative paths are rejected.
The default is [], and that default is deliberate: nothing is scanned unless you declare it. With no roots, the tool falls back to the session working directory and its ancestors plus the working directories of recent sessions.
memory.*)The memory subsystem is tuned under config.memory.*. Most advanced features are opt-in and default off in recent schema versions.
| Key | Type | Notes |
|---|---|---|
memory.profile.enabled | boolean | Profile facts and their tools. |
memory.profile.maxTokens | int (512) | Cap for the ### profile prompt section. |
memory.notes.enabled | boolean | Freeform searchable notes. |
memory.notes.maxEntries | int (~1000) | Hard row cap; FIFO eviction on overflow. |
memory.dedup.enabled | boolean | Phase 1A dedup on note write. |
memory.embeddings.enabled | boolean | Hybrid BM25 + cosine recall (needs embedding daemon). |
memory.links.enabled | boolean | Link-graph BFS expansion. |
memory.lessons.enabled | boolean | Distilled lessons (changes the stable prefix). |
memory.procedures.enabled | boolean | Advisory how-to procedures (changes the stable prefix). |
memory.voting.enabled | boolean | Vote curation of memory items. |
memory.consolidation.enabled | boolean | Cold-path clustering/distillation. |
memory.reflection.enabled | boolean | Async end-of-turn memory formation. |
llm.*) and MCP servers (mcp.servers[])Multi-provider LLM β when an llm block is present, the runtime switches from single-llama to a provider registry:
| Key | What it does |
|---|---|
llm.activeTextProvider | Selected text provider id. |
llm.activeEmbeddingProvider | Selected embedding provider id. |
llm.toolTransport | auto | grammar | native_tools. |
llm.providers[] | Provider entries (id, kind, optional apiKey). |
llm.costTracking.enabled | Per-turn cost accumulation. |
MCP servers β external tool servers live under mcp.servers[]:
| Field | What it does |
|---|---|
name | Unique kebab-case namespace (max 32 chars, no dots). |
enabled | Connect at bootstrap. |
transport | { kind: 'stdio' }, { kind: 'streamable_http' }, or { kind: 'sse' }. |
trust | approval_gated (default, fail-closed) or pure_read (batches with other reads). |
env | Per-server env overrides for stdio transport. |
Discovered tools register as mcp.<server>.<rawName>. Trust defaults to approval_gated whenever unspecified.
ATOMIC_AGENT_* variables handle deployment, paths, and operational tuning. They are read at bootstrap; some (like max tokens) can be overridden per-process without touching the file.
| Variable | Default | What it does |
|---|---|---|
ATOMIC_AGENT_STATE_DIR | ~/.atomic-agent | Root for config, secrets, DBs, traces, skills, models. |
ATOMIC_AGENT_GRAMMARS_DIR | bundled | Override the GBNF grammar asset directory. |
ATOMIC_AGENT_TOOL_CALL_GRAMMAR | bundled | Path to the tool-call.gbnf grammar file (not the directory). Takes priority over every other lookup. |
ATOMIC_AGENT_RG_PATH | bundled | Override the ripgrep binary path. |
ATOMIC_AGENT_STABLE_PREFIX_SALT | atomic-agent-v1 | Salt mixed into the prompt-prefix hash that maps a session to a llama-server KV-cache slot. Change it to force cache invalidation. |
| Variable | Notes |
|---|---|
ATOMIC_AGENT_LLAMA_API_KEY | Bearer token for a protected llama-server. |
ATOMIC_AGENT_LLAMA_API_KEY | Bearer token for llama-server (optional). |
ATOMIC_AGENT_LLAMA_MAX_TOKENS | Max new tokens per completion (clamped 64β131072). |
ATOMIC_AGENT_LLAMA_HEALTH_TIMEOUT_MS | Health-probe timeout. |
ATOMIC_AGENT_LLAMA_REQUEST_TIMEOUT_MS | Per-request timeout. |
ATOMIC_AGENT_LLAMA_COMPLETION_RETRIES | Retry count on completion failure. |
ATOMIC_AGENT_LLAMA_COMPLETION_RETRY_BACKOFF_MS | Backoff between retries. |
ATOMIC_AGENT_LLAMA_TEMPERATURE / _TOP_P / _TOP_K / _SEED | Sampling overrides (parsed at module load, not per-request). |
| Variable | Notes |
|---|---|
ATOMIC_AGENT_BROWSER_CHANNEL | chrome | msedge | chromium. |
ATOMIC_AGENT_BROWSER_ENABLED | 1/true/yes/on to enable. |
ATOMIC_AGENT_BROWSER_HEADLESS | 1 for headless. |
ATOMIC_AGENT_BROWSER_EXECUTABLE_PATH | Explicit Chromium binary. |
ATOMIC_AGENT_BROWSER_NO_SANDBOX | 1 to pass --no-sandbox (containers/CI only). |
ATOMIC_AGENT_BROWSER_CDP_URL | Attach to an existing browser over CDP. |
ATOMIC_AGENT_BROWSER_LAUNCH_TIMEOUT_MS | Launch timeout. |
| Variable | Notes |
|---|---|
ATOMIC_AGENT_MAX_PARALLEL_TOOL_CALLS | Batch fan-out cap (1β16). |
ATOMIC_AGENT_LOADED_TOOLS_CAP | Loaded-tool cap (1β64). |
ATOMIC_AGENT_LOADED_TOOLS_MAX_TOKENS | Token ceiling for the ### loaded-tools prompt section (default 600). A safety cap, not a routine truncation point. |
ATOMIC_AGENT_AUTO_EXPAND_RARE_ON_ERROR | Auto-load a rare toolβs schema on error. |
ATOMIC_AGENT_BATCH_TOOL_RESULT_CHAR_CAP | Combined character budget for all tool-result summaries in one batched step (default 16000). Over budget, the oldest results in the batch get an extra truncation pass. |
ATOMIC_AGENT_LOOP_WARNING_THRESHOLD | Args-only repeat warn threshold. |
ATOMIC_AGENT_LOOP_CRITICAL_THRESHOLD | No-progress streak veto threshold. |
ATOMIC_AGENT_LOOP_BREAKER_VETO_STREAK | Consecutive vetoes before a forced graceful reply. |
ATOMIC_AGENT_LOOP_WANDERING_THRESHOLD / _ESCALATION | Distinct-args spread for wandering tools. |
The durable task queue has no config.json block. These variables are the only way to configure it.
| Variable | Default | Notes |
|---|---|---|
ATOMIC_AGENT_TASKS_ENABLED | true | Master task-queue switch. |
ATOMIC_AGENT_TASKS_MAX_ATTEMPTS | 3 | Default retry budget per task. |
ATOMIC_AGENT_TASKS_RUN_ON_CREATE | true | Run an unscheduled task immediately when it is created. |
ATOMIC_AGENT_TASKS_SCHEDULER_ENABLED | true | Background ticker switch. |
ATOMIC_AGENT_TASKS_SCHEDULER_TICK_MS | 5000 | Scheduler poll interval. |
ATOMIC_AGENT_TASKS_SCHEDULER_BATCH | β | Max tasks drained per tick. |
ATOMIC_AGENT_TASKS_MIN_INTERVAL_MS | 1000 | Floor on --every intervals. |
ATOMIC_AGENT_TASKS_BACKOFF_INITIAL_MS / _BACKOFF_MAX_MS | β | Retry backoff bounds. |
ATOMIC_AGENT_TASKS_STALE_AFTER_MS | β | When a running task is considered stale. |
ATOMIC_AGENT_TASKS_AGENT_TOOLS_ENABLED | β | Expose the task tools to the agent itself. |
| Variable | Default | Notes |
|---|---|---|
ATOMIC_AGENT_API_KEY | β | Bearer token for atomic-agent serve (fallback when --api-key is omitted). |
ATOMIC_AGENT_UPDATE_CHECK_ON_STARTUP | true | Check for a newer release at startup. |
ATOMIC_AGENT_REPO | β | Override the repository the update check queries. |
.env fileSecrets never belong in config.json. They live in <stateDir>/.env as plain KEY=VALUE lines, with the file mode set to 0600.
TELEGRAM_BOT_TOKEN=123456:ABC-your-bot-tokenOPENROUTER_API_KEY=sk-or-...OPENAI_API_KEY=sk-...At bootstrap, .env is parsed and merged into process.env β but only for keys not already set in the shell (see precedence above). Recognised secret keys include TELEGRAM_BOT_TOKEN, OPENROUTER_API_KEY, AIMLAPI_API_KEY, OPENAI_API_KEY, OPENAI_COMPAT_API_KEY, and ATOMIC_AGENT_OPENAI_API_KEY.
external is the shipped default, so a fresh install already expects a llama-server you run yourself. To point it at a different URL, edit the existing document and pass it back whole:
atomic-agent config set "$(atomic-agent config get \ | jq '.localModels.mode = "external" | .localModels.url = "http://127.0.0.1:8080"')"There is no environment variable for this. The URL is resolved from config alone β localModels.url in external mode, or localModels.managed.port in managed mode.
Run in a container with no display, no sandbox, and no approval prompts:
export ATOMIC_AGENT_STATE_DIR=/work/.atomic-agentexport ATOMIC_AGENT_BROWSER_HEADLESS=1export ATOMIC_AGENT_BROWSER_NO_SANDBOX=1atomic-agent run --no-approval --max-steps 40--no-approval forces approval level 5, which approves every gated action. Use it only in trusted, isolated environments.
Expose the OpenAI-compatible endpoint behind a bearer token:
export ATOMIC_AGENT_API_KEY="sk-local-secret"atomic-agent serve --host 127.0.0.1 --port 8787The key can also be passed inline with --api-key; the env var is the fallback when the flag is omitted. /health and /v1/models are reachable without auth.
config set takes the whole document. Read the file with config get, edit it, and pass it back complete.tasks block in config.json. The task queue is environment variables only (ATOMIC_AGENT_TASKS_*).config.json wonβt reload a running process..env.config.json (e.g. a dataDirOverride) is resolved against process.cwd(), not the state dir. ~ is expanded directly; $HOME is not interpolated.ConfigValidationError with a dotted field path like memory.reflection.timeoutMs.Local models
Managed vs external mode, GPU budgeting, and the embedding daemon.
Tasks & scheduling
The durable task queue and the single background scheduler tick.
MCP client
Configuring external MCP servers and trust levels.
Telegram
Pairing a bot, owner ownership, and inline approvals.