Skip to content

Configuration

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.

The three sources at a glance

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.

Where everything lives

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:

Terminal window
atomic-agent config get # print the whole config.json
atomic-agent config set '<json>' # REPLACE the whole config.json

How the three sources combine

At 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

Precedence rules

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

config.json is a versioned JSON document. When Atomic Agent starts:

  • Missing file β†’ it writes defaults and warns on stderr.
  • Older schema version β†’ it parses your file, fills any new fields from defaults, rewrites the file atomically, and warns that it migrated.
  • Current version β†’ it parses and uses it as-is.

Key blocks

These are the top-level blocks you will edit most often. Defaults shown are the shipped defaults.

KeyTypeDefaultWhat it does
versionint37Schema version (managed by migration; don’t hand-edit).
localModels.urlstringhttp://127.0.0.1:8080HTTP endpoint for the local llama.cpp server.
localModels.modemanaged | externalexternalexternal expects a llama-server you run yourself; managed runs the daemon for you. atomic-agent models use <id> flips this to managed.
localModels.managed.modelIdstring | nullnullActive 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.portint19091Port for the managed chat daemon. The embedding daemon uses 19092.
localModels.completionMaxTokensint8192Max new tokens per completion.
log.leveldebug|info|warn|errorinfoLog verbosity.
agent.tokenBudgetint3000Max prompt tokens budgeted per step.
agent.maxStepsint25Default max steps per turn.
agent.toolTimeoutMsint60000Tool execution timeout.
agent.approvalLevelint 1–51How much the agent may do without asking. See the ladder below.
agent.conversationMaxTokensint32000Conversation-history token cap (clamped to context window).
agent.worldSnapshotMaxTokensint8000Cap for the browser ARIA snapshot section.
browser.channelchrome|msedge|chromiumchromeWhich browser to drive.
browser.headlessbooleanfalseRun the browser headless.
browser.executablePathstringβ€”Explicit browser binary (overrides auto-detect).
http.enabledbooleantrueLet the agent make outbound HTTP requests.
http.approvalModestringneverExtra approval mode for HTTP on top of the ladder. never means the ladder alone decides.
web.search.enabledbooleantrueEnable the web search tool.
web.search.providerstringexaSearch backend. Falls back to DuckDuckGo when no key is set.
projects.rootsstring[][]Directories whose direct children are your projects. See below.
vision.enabledbooleantrueRegister the vision.describe tool (also requires a vision-capable model).
analytics.enabledbooleantrueAnonymous usage stats. See below.
skills.disabledstring[][]Skill names to hide from the registry.
skills.tapsstring[]3 reposGitHub repositories skill browse / skill search read from.
skills.clawhub.enabledbooleantrueUse the ClawHub skill registry.
skills.clawhub.apiBasestringhttps://clawhub.aiClawHub endpoint.
tracing.trace.enabledbooleanβ€”Write NDJSON traces per session.
tui.themestring | autoautoTUI colour theme.

The approval ladder

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.

LevelNameStops asking about
1paranoidNothing β€” every gated action asks first. This is the default.
2workspaceFile writes, edits, and patches strictly inside the session working directory.
3homeAdds file writes anywhere under your home directory, moves to Trash, archive extraction, and HTTP requests.
4operatorAdds guarded shell commands, skill scripts, and process kills.
5full trustEverything, 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

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.

Project roots

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 fabric tuning (memory.*)

The memory subsystem is tuned under config.memory.*. Most advanced features are opt-in and default off in recent schema versions.

KeyTypeNotes
memory.profile.enabledbooleanProfile facts and their tools.
memory.profile.maxTokensint (512)Cap for the ### profile prompt section.
memory.notes.enabledbooleanFreeform searchable notes.
memory.notes.maxEntriesint (~1000)Hard row cap; FIFO eviction on overflow.
memory.dedup.enabledbooleanPhase 1A dedup on note write.
memory.embeddings.enabledbooleanHybrid BM25 + cosine recall (needs embedding daemon).
memory.links.enabledbooleanLink-graph BFS expansion.
memory.lessons.enabledbooleanDistilled lessons (changes the stable prefix).
memory.procedures.enabledbooleanAdvisory how-to procedures (changes the stable prefix).
memory.voting.enabledbooleanVote curation of memory items.
memory.consolidation.enabledbooleanCold-path clustering/distillation.
memory.reflection.enabledbooleanAsync end-of-turn memory formation.
LLM provider registry (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:

KeyWhat it does
llm.activeTextProviderSelected text provider id.
llm.activeEmbeddingProviderSelected embedding provider id.
llm.toolTransportauto | grammar | native_tools.
llm.providers[]Provider entries (id, kind, optional apiKey).
llm.costTracking.enabledPer-turn cost accumulation.

MCP servers β€” external tool servers live under mcp.servers[]:

FieldWhat it does
nameUnique kebab-case namespace (max 32 chars, no dots).
enabledConnect at bootstrap.
transport{ kind: 'stdio' }, { kind: 'streamable_http' }, or { kind: 'sse' }.
trustapproval_gated (default, fail-closed) or pure_read (batches with other reads).
envPer-server env overrides for stdio transport.

Discovered tools register as mcp.<server>.<rawName>. Trust defaults to approval_gated whenever unspecified.

Environment variables

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.

Core and paths

VariableDefaultWhat it does
ATOMIC_AGENT_STATE_DIR~/.atomic-agentRoot for config, secrets, DBs, traces, skills, models.
ATOMIC_AGENT_GRAMMARS_DIRbundledOverride the GBNF grammar asset directory.
ATOMIC_AGENT_TOOL_CALL_GRAMMARbundledPath to the tool-call.gbnf grammar file (not the directory). Takes priority over every other lookup.
ATOMIC_AGENT_RG_PATHbundledOverride the ripgrep binary path.
ATOMIC_AGENT_STABLE_PREFIX_SALTatomic-agent-v1Salt mixed into the prompt-prefix hash that maps a session to a llama-server KV-cache slot. Change it to force cache invalidation.

llama-server / inference

VariableNotes
ATOMIC_AGENT_LLAMA_API_KEYBearer token for a protected llama-server.
ATOMIC_AGENT_LLAMA_API_KEYBearer token for llama-server (optional).
ATOMIC_AGENT_LLAMA_MAX_TOKENSMax new tokens per completion (clamped 64–131072).
ATOMIC_AGENT_LLAMA_HEALTH_TIMEOUT_MSHealth-probe timeout.
ATOMIC_AGENT_LLAMA_REQUEST_TIMEOUT_MSPer-request timeout.
ATOMIC_AGENT_LLAMA_COMPLETION_RETRIESRetry count on completion failure.
ATOMIC_AGENT_LLAMA_COMPLETION_RETRY_BACKOFF_MSBackoff between retries.
ATOMIC_AGENT_LLAMA_TEMPERATURE / _TOP_P / _TOP_K / _SEEDSampling overrides (parsed at module load, not per-request).

Browser

VariableNotes
ATOMIC_AGENT_BROWSER_CHANNELchrome | msedge | chromium.
ATOMIC_AGENT_BROWSER_ENABLED1/true/yes/on to enable.
ATOMIC_AGENT_BROWSER_HEADLESS1 for headless.
ATOMIC_AGENT_BROWSER_EXECUTABLE_PATHExplicit Chromium binary.
ATOMIC_AGENT_BROWSER_NO_SANDBOX1 to pass --no-sandbox (containers/CI only).
ATOMIC_AGENT_BROWSER_CDP_URLAttach to an existing browser over CDP.
ATOMIC_AGENT_BROWSER_LAUNCH_TIMEOUT_MSLaunch timeout.

Agent loop tuning

VariableNotes
ATOMIC_AGENT_MAX_PARALLEL_TOOL_CALLSBatch fan-out cap (1–16).
ATOMIC_AGENT_LOADED_TOOLS_CAPLoaded-tool cap (1–64).
ATOMIC_AGENT_LOADED_TOOLS_MAX_TOKENSToken ceiling for the ### loaded-tools prompt section (default 600). A safety cap, not a routine truncation point.
ATOMIC_AGENT_AUTO_EXPAND_RARE_ON_ERRORAuto-load a rare tool’s schema on error.
ATOMIC_AGENT_BATCH_TOOL_RESULT_CHAR_CAPCombined 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_THRESHOLDArgs-only repeat warn threshold.
ATOMIC_AGENT_LOOP_CRITICAL_THRESHOLDNo-progress streak veto threshold.
ATOMIC_AGENT_LOOP_BREAKER_VETO_STREAKConsecutive vetoes before a forced graceful reply.
ATOMIC_AGENT_LOOP_WANDERING_THRESHOLD / _ESCALATIONDistinct-args spread for wandering tools.

Tasks

The durable task queue has no config.json block. These variables are the only way to configure it.

VariableDefaultNotes
ATOMIC_AGENT_TASKS_ENABLEDtrueMaster task-queue switch.
ATOMIC_AGENT_TASKS_MAX_ATTEMPTS3Default retry budget per task.
ATOMIC_AGENT_TASKS_RUN_ON_CREATEtrueRun an unscheduled task immediately when it is created.
ATOMIC_AGENT_TASKS_SCHEDULER_ENABLEDtrueBackground ticker switch.
ATOMIC_AGENT_TASKS_SCHEDULER_TICK_MS5000Scheduler poll interval.
ATOMIC_AGENT_TASKS_SCHEDULER_BATCHβ€”Max tasks drained per tick.
ATOMIC_AGENT_TASKS_MIN_INTERVAL_MS1000Floor 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.

HTTP and updates

VariableDefaultNotes
ATOMIC_AGENT_API_KEYβ€”Bearer token for atomic-agent serve (fallback when --api-key is omitted).
ATOMIC_AGENT_UPDATE_CHECK_ON_STARTUPtrueCheck for a newer release at startup.
ATOMIC_AGENT_REPOβ€”Override the repository the update check queries.

Secrets: the .env file

Secrets never belong in config.json. They live in <stateDir>/.env as plain KEY=VALUE lines, with the file mode set to 0600.

<stateDir>/.env
TELEGRAM_BOT_TOKEN=123456:ABC-your-bot-token
OPENROUTER_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.

Worked examples

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:

Terminal window
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.

Gotchas to remember

  • config set takes the whole document. Read the file with config get, edit it, and pass it back complete.
  • No tasks block in config.json. The task queue is environment variables only (ATOMIC_AGENT_TASKS_*).
  • Frozen snapshot. The agent runs against one immutable config object. Hand-editing config.json won’t reload a running process.
  • Split precedence. File wins for user keys; env wins for operational keys; shell env always beats .env.
  • Migration ratchet. Auto-migration bumps the schema version on write; old releases can’t read the new file.
  • Relative paths resolve against cwd. A relative path in config.json (e.g. a dataDirOverride) is resolved against process.cwd(), not the state dir. ~ is expanded directly; $HOME is not interpolated.
  • Validation errors are precise. A bad value raises 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.