> ## Documentation Index
> Fetch the complete documentation index at: https://docs.neuralcleave.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Configuration

> Complete config.toml reference — agent, models, memory, voice, gateway, and secret resolution.

NeuralCleave is configured with a single TOML file at `~/.neuralcleave/config.toml`.

## Secret resolution

Instead of hard-coding an API key:

```toml theme={null}
anthropic_api_key = "sk-ant-abc123..."  # ❌ stored in plain text
```

Use one of two resolvable prefixes:

```toml theme={null}
anthropic_api_key = "ENV:ANTHROPIC_API_KEY"                  # ✅ read from an environment variable
anthropic_api_key = "op://Private/Anthropic/api_key"         # ✅ resolved via the 1Password CLI (`op`)
```

A missing `ENV:` variable resolves to an empty string, not an error. An unresolvable `op://` reference raises a clear error naming the missing `op` CLI or the failed lookup.

## Full reference

### `[agent]`

```toml theme={null}
[agent]
name = "My Assistant"
model = "auto"
```

### `[models]`

```toml theme={null}
[models]
primary = "claude-opus-4-8"          # default model
fallback = "gemini-2.5-flash"        # used if primary fails
fast = "gemini-2.5-flash"            # used for simple/cheap tasks
local = "ollama/llama3.2:1b"         # used in privacy mode

# API keys — one field per provider, ENV:/op:// both supported
anthropic_api_key = "ENV:ANTHROPIC_API_KEY"
gemini_api_key = "ENV:GEMINI_API_KEY"
openai_api_key = "ENV:OPENAI_API_KEY"
deepseek_api_key = "ENV:DEEPSEEK_API_KEY"
mistral_api_key = "ENV:MISTRAL_API_KEY"
xai_api_key = "ENV:XAI_API_KEY"
cohere_api_key = "ENV:COHERE_API_KEY"
moonshot_api_key = "ENV:MOONSHOT_API_KEY"
zhipuai_api_key = "ENV:ZHIPUAI_API_KEY"
dashscope_api_key = "ENV:DASHSCOPE_API_KEY"
qianfan_api_key = "ENV:QIANFAN_API_KEY"
ark_api_key = "ENV:ARK_API_KEY"
openrouter_api_key = "ENV:OPENROUTER_API_KEY"
azure_api_key = "ENV:AZURE_OPENAI_API_KEY"
azure_endpoint = "ENV:AZURE_OPENAI_ENDPOINT"
bedrock_region = "us-east-1"          # AWS credentials come from the standard AWS SDK chain
groq_api_key = "ENV:GROQ_API_KEY"
together_api_key = "ENV:TOGETHER_API_KEY"
fireworks_api_key = "ENV:FIREWORKS_API_KEY"

# Local provider (no key needed)
ollama_base_url = "http://localhost:11434"

web_search_enabled = false
```

See [LLM Providers](/llm-providers) for the full provider list and [Settings API](/api-reference/settings) for changing these at runtime.

### `[memory]`

```toml theme={null}
[memory]
short_term_ttl = 3600                          # seconds a short-term turn stays in context
long_term_days = 90                            # long-term memory retention window
redis_url = "redis://localhost:6379"           # short-term store
qdrant_url = "http://localhost:6333"           # vector store for semantic retrieval
sqlite_path = "~/.neuralcleave/memory.db"      # long-term SQLite store
```

### `[voice]`

```toml theme={null}
[voice]
stt = "none"                    # none | whisper (see stt_model for size)
tts = "none"                    # none | pyttsx3 | kokoro | elevenlabs
stt_model = "base"               # tiny | base | small | medium | large-v3
stt_device = "cpu"               # cpu | cuda
tts_engine = "none"
tts_voice = "Rachel"
elevenlabs_api_key = "ENV:ELEVENLABS_API_KEY"
elevenlabs_voice_id = ""
language = ""                    # forced STT language, empty = auto-detect

# Wake word
wake_word = ""
wake_word_model_path = ""
wake_word_threshold = 0.5

# VAD (voice activity detection)
vad_backend = "energy"
vad_aggressiveness = 2
vad_silence_threshold = 300.0
vad_silence_duration_s = 0.8

continuous_voice_enabled = false
wake_handoff_duration_s = 10.0
ptt_max_duration_s = 30.0
voice_session_idle_s = 300.0
```

### `[gateway]`

```toml theme={null}
[gateway]
bind = "127.0.0.1"
port = 7432
api_key = ""          # optional — when set, required as X-API-Key for /api/* and /ws/* requests
```

### `[security]`

Controls the exec-approval gate consulted by the `shell` and `browser`
tools. Off by default — a command runs immediately with no prompt unless
you opt in.

```toml theme={null}
[security]
require_shell_approval = false   # true = gate shell/browser tool calls behind approval
security_mode = "allowlist"      # deny | allowlist | full
ask_mode = "on-miss"             # off | on-miss | always
```

* `require_shell_approval` — when `true`, every `shell` and `browser` tool
  call is queued and blocks until approved. When a pending request is
  queued, NeuralCleave also sends a notification into the channel that
  triggered it (Slack, Telegram, Discord, …) — reply `approve <id-prefix>`
  or `deny <id-prefix>` right there, or use `neuralcleave approvals approve/deny`.
* `security_mode` — `"deny"` denies every gated command outright; `"allowlist"`
  auto-approves commands matching a stored allowlist entry (see
  `neuralcleave approvals allowlist add/list/remove`) and falls through to
  `ask_mode` for the rest; `"full"` disables the gate entirely (auto-approves
  everything, equivalent to `require_shell_approval = false`).
* `ask_mode` — only consulted when `security_mode = "allowlist"`: `"off"`
  never prompts (an unmatched command is denied silently), `"on-miss"`
  prompts only when no allowlist entry matches (the default), `"always"`
  prompts even on an allowlist match.

### `[ui]`

```toml theme={null}
[ui]
web_port = 3000
```

### `[channels.<name>]`

Configure each channel adapter under its own key, e.g.:

```toml theme={null}
[channels.telegram]
enabled = true
bot_token = "ENV:TELEGRAM_BOT_TOKEN"

[channels.discord]
enabled = true
bot_token = "ENV:DISCORD_BOT_TOKEN"
```

Every channel adapter's secret fields resolve through the same `ENV:`/`op://` mechanism as `[models]`.

## Hot-reload

The gateway watches `config.toml` for changes, but only `[security]` (`require_shell_approval`, `security_mode`, `ask_mode`) is actually applied live — the same mutation `POST /api/v1/approvals/policy` does. Model/API-key settings and channel connections are **not** hot-reloaded despite being watched — restart the gateway (or use `neuralcleave channels remove`/`add` for channel config) after changing those.
