---
title: Configuration
url: https://arcana.otnelhq.com/docs/configuration
---

# Configuration



Arcana is configured through JSON config files — a project-level `arcana.json` plus an optional global file. Most settings have sensible defaults and can be overridden with environment variables.






             Tip 

Prefer environment variables for API keys so they stay out of version control.






## Config file location



Arcana merges configuration from three layers, later winning:



| Layer | Path | Notes |
| --- | --- | --- |
| Global | `~/.config/arcana/arcana.json` (or `.jsonc`) | Created automatically with a `$schema` stub on first run |
| Project | `arcana.json` / `arcana.jsonc` in your repo | Arcana walks up parent directories to find the nearest one |
| Environment | env vars | Always win for API keys and provider overrides |



The legacy layout is still loaded: `~/.arcana/config.json`, with the root overridable via `ARCANA_HOME`:



```bash
export ARCANA_HOME=/custom/path
# Legacy config is then loaded from /custom/path/config.json
```



Escape hatches: `ARCANA_CONFIG` / `ARCANA_CONFIG_DIR` / `ARCANA_CONFIG_CONTENT` point config at an explicit path or inline JSON, and `ARCANA_DISABLE_PROJECT_CONFIG=1` ignores project files entirely. Unknown keys are rejected with a validation error.




## Full config reference



```json
{
  "$schema": "https://arcana.otnelhq.com/config.json",
  "model": "anthropic/claude-sonnet-4-6",
  "small_model": "anthropic/claude-haiku-4-5",
  "provider": "openai",
  "utilityModel": "gpt-4o-mini",
  "apiKey": "sk-...",
  "default_agent": "primary",
  "instructions": ["Always run tests after refactoring"],
  "agent": {
    "reviewer": { "description": "Code review agent", "model": "anthropic/claude-opus-4" }
  },
  "permission": {
    "edit": "allow",
    "bash": "ask",
    "webfetch": "deny"
  },
  "mcp": {
    "browser": { "type": "local", "command": ["agent-browser", "mcp", "--tools", "core"], "enabled": true }
  },
  "dataDir": "~/.arcana/data",
  "skillsDirs": ["~/.arcana/skills"],
  "memory": {
    "enabled": true,
    "maxSessions": 1000
  },
  "cron": {
    "enabled": true,
    "intervalSeconds": 60
  },
  "gateway": {
    "telegram": { "token": "...", "allowedUsers": ["12345678"] },
    "discord": { "token": "...", "allowedChannels": ["987654321"] },
    "slack": { "botToken": "xoxb-...", "signingSecret": "...", "allowedChannels": ["C0123"] },
    "whatsapp": { "phoneNumberId": "...", "accessToken": "...", "appSecret": "...", "allowedUsers": ["14155551234"] }
  }
}
```




## Core settings



| Key | Type | Default | Description |
| --- | --- | --- | --- |
| `provider` | string | auto-detected | LLM provider (`openai`, `anthropic`, `gemini`, etc.) |
| `model` | string | auto-detected | Model ID (`gpt-4o`, `claude-sonnet-4`, or `provider/model`) |
| `small_model` | string | — | Cheap model for extraction and compaction (new-style key) |
| `utilityModel` | string | main model | Cheap model for extraction and compaction (legacy key, still honored) |
| `apiKey` | string | — | Provider API key (prefer env vars) |
| `default_agent` | string | `primary` | Agent used when none is specified |
| `instructions[]` | string[] | — | Project instructions injected into every session |
| `agent{}` | object | — | Custom agents: `description`, `mode`, `model`, `permission`, `disable` |
| `permission{}` | object | — | Per-tool `allow`/`ask`/`deny` with glob patterns (last match wins). See [Permissions](/docs/permissions) |
| `mcp{}` | object | — | MCP servers: local `command[]` or remote `url` + `headers`. See [MCP & Tools](/docs/mcp) |
| `dataDir` | string | `~/.arcana/data` | Directory for sessions, memory, and local DB |




### Provider auto-detection



If neither `provider` nor `model` is set in the config or environment, Arcana auto-detects from available API keys using the models.dev catalog (200+ models across 33 providers).



```bash
# Set a provider key — Arcana detects it automatically
export OPENAI_API_KEY=sk-...
arcana run "hello"  # Uses OpenAI automatically
```




## Memory settings



| Key | Type | Default | Description |
| --- | --- | --- | --- |
| `memory.enabled` | boolean | `true` | Enable conversation memory and fact extraction |
| `memory.maxSessions` | number | `1000` | Maximum sessions to retain in local DB |



Memory stores conversation history, extracted facts, and skill usage stats in SQLite under `~/.arcana/data/`.




## Session compaction



Long sessions auto-summarize near the context limit (default **85%**).



| Key | Type | Default | Description |
| --- | --- | --- | --- |
| `compaction.auto` | boolean | `true` | Enable automatic compaction |
| `compaction.threshold_percent` | number | `85` | Trigger when usage reaches this % of context (1–100) |
| `compaction.intra` | boolean | `true` | Mid-loop compact during multi-step tool runs |
| `compaction.intra_min_steps` | number | `3` | Min agent loop steps before intra compact |
| `compaction.intra_min_tokens` | number | `5000` | Min usage before intra compact is worth it |



```json
{
  "compaction": {
    "auto": true,
    "threshold_percent": 85,
    "intra": true
  }
}
```



Set `"intra": false` to only compact between user turns. Set `"auto": false` to disable all auto compact (manual `/compact` still works).






             Warning 

Setting `"auto": false` will stop all automatic compaction. You can still compact manually with `/compact`.






## Cron settings



| Key | Type | Default | Description |
| --- | --- | --- | --- |
| `cron.enabled` | boolean | `true` | Enable the cron scheduler |
| `cron.intervalSeconds` | number | `60` | How often the scheduler checks for due jobs |




## Skills settings



| Key | Type | Default | Description |
| --- | --- | --- | --- |
| `skillsDirs` | string[] | `["~/.arcana/skills", "/skills"]` | Directories to scan for SKILL.md files |




## Gateway settings



Configure chat platform adapters. See [Gateway](/docs/gateway) for full setup instructions.



| Key | Type | Description |
| --- | --- | --- |
| `gateway.telegram.token` | string | Telegram bot token |
| `gateway.telegram.allowedUsers` | string[] | Allowed Telegram user IDs |
| `gateway.discord.token` | string | Discord bot token |
| `gateway.discord.allowedChannels` | string[] | Allowed Discord channel IDs |
| `gateway.slack.botToken` | string | Slack bot token (`xoxb-...`) |
| `gateway.slack.signingSecret` | string | Slack signing secret |
| `gateway.slack.allowedChannels` | string[] | Allowed Slack channel IDs |
| `gateway.whatsapp.phoneNumberId` | string | Meta phone number ID |
| `gateway.whatsapp.accessToken` | string | Meta access token |
| `gateway.whatsapp.appSecret` | string | Meta app secret (required for production) |
| `gateway.whatsapp.allowedUsers` | string[] | Allowed phone numbers (with country code) |




## Environment variables



All config file settings can be overridden with environment variables.



| Env var | Overrides | Description |
| --- | --- | --- |
| `ARCANA_HOME` | config path | Root directory for the legacy layout (default: `~/.arcana`) |
| `ARCANA_PROVIDER` | `provider` | LLM provider |
| `ARCANA_MODEL` | `model` | Model ID |
| `ARCANA_API_KEY` | `apiKey` | Provider API key |
| `OPENAI_API_KEY` | `apiKey` | Fallback when provider is `openai` |
| `ARCANA_SKILLS_DIRS` | `skillsDirs` | Skill directories (separated by `;`) |
| `ARCANA_PROXY_KEY` | — | Proxy license key (auto-loaded from `~/.arcana/proxy_key`) |
| `ARCANA_CONFIG` | config path | Explicit config file path |
| `ARCANA_CONFIG_DIR` | config dir | Explicit config directory |
| `ARCANA_CONFIG_CONTENT` | config body | Inline JSON config content |
| `ARCANA_DISABLE_PROJECT_CONFIG=1` | — | Ignore project-level `arcana.json` |
| `ARCANA_PURE=1` | — | Skip all external config, skills, and state |
| `ARCANA_SERVER_USERNAME` | — | Username for non-loopback `serve` (default `arcana`) |
| `ARCANA_ENGINE_BASE_URL` | — | Engine HTTP endpoint (default `http://localhost:4096`) |
| `ARCANA_DEPLOYMENT_MODE` | — | `LOCAL`, `HYBRID`, or `ENTERPRISE` |
| `ARCANA_ML_RUNTIME=1` | — | Enable the signal engine (quality gates, auto model switching) |




### Provider-specific env vars



| Env var | Provider |
| --- | --- |
| `ANTHROPIC_API_KEY` | Anthropic |
| `GEMINI_API_KEY` | Google Gemini |
| `AWS_ACCESS_KEY_ID` + `AWS_SECRET_ACCESS_KEY` | Amazon Bedrock |
| `CLOUDFLARE_ACCOUNT_ID` + `CLOUDFLARE_API_TOKEN` | Cloudflare Workers AI |
| `XAI_API_KEY` | xAI (Grok) |



Use `arcana doctor` to confirm which keys are detected.




## Data directory



Default: `~/.arcana/data/`



| Path | Contents |
| --- | --- |
| `sessions/` | Session transcripts |
| `memory.db` | SQLite DB with facts, skill stats |
| `cron-jobs.json` | Scheduled job definitions |
| `~/.arcana/workspace-trust.json` | Trusted workspace fingerprints (at the Arcana home root, not in `data/`) |




## Trust and security settings



| Setting | Location | Description |
| --- | --- | --- |
| `arcana trust` | CLI command | Trust current workspace for project plugins/tools |
| `ARCANA_DISABLE_WORKSPACE_TRUST=1` | Env var | Skip trust checks (dev only) |
| `ARCANA_TRUST_WORKSPACE=1` | Env var | Force-trust for CI |
| `ARCANA_SERVER_PASSWORD` | Env var | Required for non-loopback `arcana serve` |
| `ARCANA_GATEWAY_OPEN=1` | Env var | Allow empty gateway allowlists (dev only) |
