config.json
User-facing settings: model, browser, memory, tasks, 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, tasks, 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 or merge-write the current config at any time:
atomic-agent config get # print the resolved config.jsonatomic-agent config set '{"log":{"level":"debug"}}' # merge-writeAt 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 | 29 | Schema version (managed by migration; donβt hand-edit). |
localModels.url | string | llama-server URL | HTTP endpoint for the local llama.cpp server. |
localModels.mode | managed | external | managed | managed runs the daemon for you; external expects a running server. |
localModels.managed.modelId | string | β | Active model in managed mode. |
log.level | debug|info|warn|error | info | Log verbosity. |
agent.tokenBudget | int | 6000 | Max prompt tokens budgeted per step. |
agent.maxSteps | int | 25 | Default max steps per turn. |
agent.toolTimeoutMs | int | 60000 | Tool execution timeout. |
agent.approvalRequired | boolean | true | Gate dangerous tools behind approval. |
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). |
tasks.enabled | boolean | true | Master switch for the durable task queue. |
tasks.maxAttempts | int | 3 | Retry budget per task. |
tasks.schedulerEnabled | boolean | β | Run the background scheduler ticker. |
tracing.trace.enabled | boolean | β | Write NDJSON traces per session. |
skills.disabled | string[] | [] | Skill names to hide from the registry. |
tui.theme | string | auto | auto | TUI colour theme. |
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_RG_PATH | bundled | Override the ripgrep binary path. |
| Variable | Notes |
|---|---|
ATOMIC_AGENT_LLAMA_URL | llama-server connection URL. |
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_AUTO_EXPAND_RARE_ON_ERROR | Auto-load a rare toolβs schema on error. |
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. |
| Variable | Notes |
|---|---|
ATOMIC_AGENT_TASKS_ENABLED | Master task-queue switch. |
ATOMIC_AGENT_TASKS_SCHEDULER_ENABLED | Background ticker switch. |
ATOMIC_AGENT_TASKS_SCHEDULER_TICK_MS | Scheduler poll interval. |
ATOMIC_AGENT_TASKS_SCHEDULER_BATCH | Max tasks drained per tick. |
ATOMIC_AGENT_API_KEY | Bearer token for atomic-agent serve (fallback when --api-key is omitted). |
.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.
Point Atomic Agent at a llama-server you run yourself, and turn off managed mode:
atomic-agent config set '{"localModels":{"mode":"external","url":"http://127.0.0.1:8080"}}'Or, for a one-off process, set it via the environment:
export ATOMIC_AGENT_LLAMA_URL="http://127.0.0.1:8080"atomic-agent runRun 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 auto-approves every dangerous tool. 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.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.