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, 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.

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 or merge-write the current config at any time:

Terminal window
atomic-agent config get # print the resolved config.json
atomic-agent config set '{"log":{"level":"debug"}}' # merge-write

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
versionint29Schema version (managed by migration; don’t hand-edit).
localModels.urlstringllama-server URLHTTP endpoint for the local llama.cpp server.
localModels.modemanaged | externalmanagedmanaged runs the daemon for you; external expects a running server.
localModels.managed.modelIdstringβ€”Active model in managed mode.
log.leveldebug|info|warn|errorinfoLog verbosity.
agent.tokenBudgetint6000Max prompt tokens budgeted per step.
agent.maxStepsint25Default max steps per turn.
agent.toolTimeoutMsint60000Tool execution timeout.
agent.approvalRequiredbooleantrueGate dangerous tools behind approval.
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).
tasks.enabledbooleantrueMaster switch for the durable task queue.
tasks.maxAttemptsint3Retry budget per task.
tasks.schedulerEnabledbooleanβ€”Run the background scheduler ticker.
tracing.trace.enabledbooleanβ€”Write NDJSON traces per session.
skills.disabledstring[][]Skill names to hide from the registry.
tui.themestring | autoautoTUI colour theme.
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_RG_PATHbundledOverride the ripgrep binary path.

llama-server / inference

VariableNotes
ATOMIC_AGENT_LLAMA_URLllama-server connection URL.
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_AUTO_EXPAND_RARE_ON_ERRORAuto-load a rare tool’s schema on error.
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 and HTTP

VariableNotes
ATOMIC_AGENT_TASKS_ENABLEDMaster task-queue switch.
ATOMIC_AGENT_TASKS_SCHEDULER_ENABLEDBackground ticker switch.
ATOMIC_AGENT_TASKS_SCHEDULER_TICK_MSScheduler poll interval.
ATOMIC_AGENT_TASKS_SCHEDULER_BATCHMax tasks drained per tick.
ATOMIC_AGENT_API_KEYBearer token for atomic-agent serve (fallback when --api-key is omitted).

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

Point Atomic Agent at a llama-server you run yourself, and turn off managed mode:

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

Terminal window
export ATOMIC_AGENT_LLAMA_URL="http://127.0.0.1:8080"
atomic-agent run

Gotchas to remember

  • 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.