---
icon: lucide/settings
---

# Configuration

MindRoom is configured through a `config.yaml` file. This section covers all configuration options.

## Configuration File

MindRoom searches for the configuration file in this order (first match wins):

1. `MINDROOM_CONFIG_PATH` environment variable (if set)
2. `./config.yaml` (current working directory)
3. `~/.mindroom/config.yaml` (home directory)

Data storage (`mindroom_data/`) is placed next to the config file by default.

You can also validate a specific file directly:

```bash
mindroom config validate --path /path/to/config.yaml
```

## MCP Servers

MindRoom can connect to external Model Context Protocol servers through the top-level `mcp_servers` block.
See [MCP](../mcp.md) for transport-specific config, tool naming, examples, and agent setup.

## Tool Approval

Use the top-level `tool_approval` block to gate tool calls behind human approval in Matrix conversations.
Rules are evaluated in order and the first matching rule wins.
Each rule must set exactly one of `action` or `script`.
Use `action: require_approval` to always pause the tool call and send a Matrix approval card.
Use `script: ./approval_scripts/review.py` to run `check(tool_name, arguments, agent_name) -> bool` and require approval only when it returns `True`.
`timeout_days` sets the default approval expiry window and can be overridden per rule.
React to the approval card with `✅` to approve the tool call.
Reply to the approval card with a message to deny the tool call and record that text as the denial reason.
Only the original human requester can approve or deny their pending tool call.
Approval responses only resolve the live Matrix approval card in the same room; approval IDs are used only as a live client hint.
If MindRoom restarts before a tool call is approved, the live tool call is cancelled.
On startup, MindRoom attempts to mark recent unresolved approval cards sent by the current router as expired.
Agent-authored, system-authored, and configured bridge-bot-authored tool calls are denied instead of entering the approval flow.
OpenAI-compatible `/v1/chat/completions` has no approval transport, so any tool function that matches a required-approval rule, including script-based rules, is hidden from the `/v1` tool schema instead of being exposed and blocked later.

```yaml
tool_approval:
  default: auto_approve
  timeout_days: 7
  rules:
    - match: slack_*
      action: require_approval
    - match: run_shell_command
      script: ./approval_scripts/shell_review.py
      timeout_days: 3
```

## Environment Variables

### Core

| Variable | Description | Default |
|----------|-------------|---------|
| `MINDROOM_CONFIG_PATH` | Path to `config.yaml` | `./config.yaml` → `~/.mindroom/config.yaml` |
| `MINDROOM_STORAGE_PATH` | Data storage directory | `mindroom_data/` next to config |
| `MINDROOM_CONFIG_TEMPLATE` | Path to a config template. When set and `config.yaml` does not exist, MindRoom copies this template to the config path. Used in Docker containers to seed config from bundled templates | Same as config path |
| `MINDROOM_CREDENTIALS_ENCRYPTION_KEY` | Optional base64-encoded 32-byte key for encrypted-at-rest credential files | unset |
| `LOG_LEVEL` | Logging level for `mindroom run` (`DEBUG`, `INFO`, `WARNING`, `ERROR`) | `INFO` |
| `MINDROOM_LOGGER_LEVELS` | Optional comma- or semicolon-separated logger level overrides, for example `mindroom:DEBUG,httpx:WARNING,httpcore:WARNING,anthropic:INFO,nio:WARNING` | unset |

### Matrix

| Variable | Description | Default |
|----------|-------------|---------|
| `MATRIX_HOMESERVER` | Matrix homeserver URL | `http://localhost:8008` |
| `MATRIX_SERVER_NAME` | Server name for federation | _(derived from homeserver)_ |
| `MATRIX_SSL_VERIFY` | Verify SSL certificates | `true` |

### API Keys

Set the API key for each provider you use in `config.yaml`:

| Variable | Provider |
|----------|----------|
| `ANTHROPIC_API_KEY` | Anthropic (Claude) |
| `AZURE_OPENAI_API_KEY` | Azure OpenAI |
| `AZURE_OPENAI_ENDPOINT` | Azure OpenAI endpoint |
| `AZURE_OPENAI_API_VERSION` | Optional Azure OpenAI API version override |
| `OPENAI_API_KEY` | OpenAI |
| `GOOGLE_API_KEY` | Google (Gemini) |
| `OPENROUTER_API_KEY` | OpenRouter |
| `DEEPSEEK_API_KEY` | DeepSeek |
| `CEREBRAS_API_KEY` | Cerebras |
| `GROQ_API_KEY` | Groq |
| `OLLAMA_HOST` | Ollama (host URL, not a key) |
| `OPENAI_BASE_URL` | Base URL for OpenAI-compatible APIs (e.g., local inference servers) |

All API key variables also support a `_FILE` suffix for file-based secrets (e.g., `ANTHROPIC_API_KEY_FILE=/run/secrets/anthropic-api-key`).
See [Model Configuration — File-based Secrets](models.md#file-based-secrets) for details.

### Codex CLI Subscription Auth

The `codex` provider does not use an API key environment variable.
Run `codex login` so `~/.codex/auth.json` contains ChatGPT OAuth tokens.
Set `CODEX_HOME` only if your Codex CLI state lives outside `~/.codex`.

### Operational

| Variable | Description | Default |
|----------|-------------|---------|
| `MINDROOM_NAMESPACE` | Installation namespace for Matrix identity isolation (4–32 lowercase alphanumeric chars) | _(none)_ |
| `MINDROOM_PORT` | Port used by Google OAuth callback URL construction and deployment tooling. Does **not** change the API server bind port — use `mindroom run --api-port` for that | `8765` |
| `MINDROOM_API_KEY` | API key for authenticating dashboard/API requests (`mindroom config init` auto-generates one; unset = open access) | _(none)_ |
| `MINDROOM_DASHBOARD_CORS_ALLOWED_ORIGINS` | Comma-separated browser origins allowed to call the dashboard API with credentials | `http://localhost:3003`, `http://localhost:5173`, `http://127.0.0.1:3003`, `http://127.0.0.1:5173` |
| `MINDROOM_DASHBOARD_CORS_ALLOW_ALL_ORIGINS` | Set to `true` to allow every dashboard API origin while disabling credentialed CORS responses | _(unset)_ |
| `MINDROOM_NO_AUTO_INSTALL_TOOLS` | Set to `1`/`true`/`yes` to disable automatic tool dependency installation | _(unset — auto-install enabled)_ |
| `MINDROOM_MATRIX_HOMESERVER_STARTUP_TIMEOUT_SECONDS` | Seconds to wait for homeserver to become reachable at startup (0 = skip). MindRoom polls the homeserver's `/_matrix/client/versions` endpoint with exponential backoff retry, detecting permanent errors (e.g., wrong URL) vs transient failures | _(wait indefinitely)_ |
| `MINDROOM_DISPATCH_THREAD_READ_TIMEOUT_SECONDS` | Wall-clock seconds allowed for live dispatch-safe Matrix thread reads before dispatch proceeds with degraded thread evidence | `1.0` |
| `MINDROOM_WORKER_BACKEND` | Worker backend for tool execution (`static_runner`, `docker`, or `kubernetes`) | `static_runner` |

### OpenAI-Compatible API

| Variable | Description | Default |
|----------|-------------|---------|
| `OPENAI_COMPAT_API_KEYS` | Comma-separated API keys for authenticating `/v1/*` requests | _(none — locked without this or the flag below)_ |
| `OPENAI_COMPAT_ALLOW_UNAUTHENTICATED` | Set to `true` to allow unauthenticated `/v1/*` access (local dev only) | _(unset — locked)_ |

See [OpenAI-Compatible API](../openai-api.md) for the full auth matrix.

### Provisioning / Pairing

These are set automatically by `mindroom connect` and stored in `.env`:

| Variable | Description |
|----------|-------------|
| `MINDROOM_PROVISIONING_URL` | Provisioning service URL (e.g., `https://mindroom.chat`) |
| `MINDROOM_LOCAL_CLIENT_ID` | Client ID from hosted pairing |
| `MINDROOM_LOCAL_CLIENT_SECRET` | Client secret from hosted pairing |

### Frontend / Development

| Variable | Description | Default |
|----------|-------------|---------|
| `MINDROOM_FRONTEND_DIST` | Override path to pre-built frontend assets | _(auto-detected)_ |
| `MINDROOM_AUTO_BUILD_FRONTEND` | Set to `0` to skip automatic frontend build | _(enabled)_ |
| `DOCKER_CONTAINER` | Set to `true` when running inside the packaged Docker image | _(unset)_ |
| `BROWSER_EXECUTABLE_PATH` | Path to browser executable for the browser tool | _(system default)_ |

### Vertex AI

| Variable | Description |
|----------|-------------|
| `ANTHROPIC_VERTEX_PROJECT_ID` | Google Cloud project ID for Vertex AI Claude |
| `ANTHROPIC_VERTEX_BASE_URL` | Custom Vertex AI base URL |
| `CLOUD_ML_REGION` | Google Cloud region for Vertex AI |
| `GOOGLE_CLOUD_PROJECT` | Google Cloud project ID |
| `GOOGLE_CLOUD_LOCATION` | Google Cloud region |
| `GOOGLE_APPLICATION_CREDENTIALS` | Path to Google service account JSON |

Authenticate with `gcloud auth application-default login` or set `GOOGLE_APPLICATION_CREDENTIALS`.

### Worker / Sandbox

| Variable | Description | Default |
|----------|-------------|---------|
| `MINDROOM_SANDBOX_PROXY_URL` | Sandbox proxy endpoint URL (static runner) | _(none)_ |
| `MINDROOM_SANDBOX_PROXY_TOKEN` | Auth token for the sandbox proxy | _(none)_ |

See [Sandbox Proxy](../deployment/sandbox-proxy.md) for the full list of `MINDROOM_SANDBOX_*` variables and Kubernetes backend variables (`MINDROOM_KUBERNETES_WORKER_*`).

### SaaS-Only

| Variable | Description | Default |
|----------|-------------|---------|
| `CUSTOMER_ID` | Tenant identity for worker key derivation (SaaS platform only) | _(none)_ |
| `ACCOUNT_ID` | Account identity for worker key derivation (SaaS platform only) | _(none)_ |

## Basic Structure

```yaml
# Agent definitions (at least one recommended)
agents:
  assistant:
    display_name: Assistant        # Required: Human-readable name
    role: A helpful AI assistant   # Optional: Description of purpose
    model: sonnet                  # Optional: Model name (default: "default")
    tools:                         # Optional: Agent-specific tools (merged with defaults.tools)
      - file
      - shell
      # - shell:                    # Per-agent tool config overrides (single-key dict):
      #     extra_env_passthrough: "DAWARICH_*"
    include_default_tools: true    # Optional: Per-agent opt-out for defaults.tools
    skills: []                     # Optional: List of skill names
    instructions: []               # Optional: Custom instructions
    rooms: [lobby]                 # Optional: Rooms to auto-join
    accept_invites: true           # Optional: Accept authorized ad-hoc room invites
    markdown: true                 # Optional: Override default (inherits from defaults section)
    worker_tools: [shell, file]    # Optional: Override default (inherits from defaults section)
    worker_scope: user_agent       # Optional: Reuse one proxied runtime per requester+agent
    learning: true                 # Optional: Override default (inherits from defaults section)
    learning_mode: always          # Optional: Override default (inherits from defaults section)
    memory_backend: file           # Optional: Per-agent memory backend override (mem0, file, or none)
    memory_search:                 # Optional: Per-agent file-memory search override
      mode: semantic               # keyword or semantic; omitted fields inherit memory.search
      include:
        - memory/**/*.md
      include_entrypoint: false
    knowledge_bases: [docs]         # Optional: Assign one or more configured knowledge bases
    context_files:                 # Optional: Load files into each freshly built agent instance
      - SOUL.md
      - AGENTS.md
      - USER.md
      - IDENTITY.md
      - TOOLS.md
      - HEARTBEAT.md
  researcher:
    display_name: Researcher
    role: Research and gather information
    model: sonnet
  writer:
    display_name: Writer
    role: Write and edit content
    model: sonnet
  developer:
    display_name: Developer
    role: Write code and implement features
    model: sonnet
  reviewer:
    display_name: Reviewer
    role: Review code and provide feedback
    model: sonnet

# Model configurations (at least a "default" model is recommended)
models:
  default:
    provider: anthropic            # Required: openai, azure, anthropic, ollama, google, gemini, vertexai_claude, groq, cerebras, openrouter, deepseek
    id: claude-sonnet-4-6            # Required: Model ID for the provider
  sonnet:
    provider: anthropic            # Required: openai, azure, anthropic, ollama, google, gemini, vertexai_claude, groq, cerebras, openrouter, deepseek
    id: claude-sonnet-4-6            # Required: Model ID for the provider
    host: null                     # Optional: Host URL (e.g., for Ollama)
    api_key: null                  # Optional: API key (usually from env vars)
    extra_kwargs: null             # Optional: Provider-specific parameters
    context_window: null           # Optional: Needed on the active runtime model for replay safety; explicit compaction.model also needs its own window for summary generation

# Team configurations (optional)
teams:
  research_team:
    display_name: Research Team    # Required: Human-readable name
    role: Collaborative research   # Required: Description of team purpose
    agents: [researcher, writer]   # Required: List of agent names
    mode: collaborate              # Optional: "coordinate" or "collaborate" (default: coordinate)
    model: sonnet                  # Optional: Model for team coordination (default: "default")
    num_history_runs: 8            # Optional: Team-scoped replay policy
    num_history_messages: null     # Optional: Mutually exclusive with num_history_runs
    max_tool_calls_from_history: 6 # Optional: Limit replayed tool call messages
    compaction:                    # Optional: Team-scoped required-compaction overrides
      # Soft thresholds do not compact by themselves while history still fits.
      enabled: true
      threshold_percent: 0.8
      reserve_tokens: 16384
    rooms: []                      # Optional: Rooms to auto-join

# Culture configurations (optional)
cultures:
  engineering:
    description: Follow clean code principles and write tests  # Shared principles
    agents: [developer, reviewer]  # Agents assigned (each agent can belong to at most one culture)
    mode: automatic                # automatic, agentic, or manual

# Router configuration (optional)
router:
  model: default                   # Optional: Model for routing (default: "default")

# Default settings for all agents (optional)
defaults:
  tools:                           # Default: ["scheduler"] (added to every agent; set [] to disable)
    - scheduler                    # Plain string or single-key dict with inline config overrides
  markdown: true                   # Default: true
  enable_streaming: true           # Default: true (stream responses via message edits)
  streaming:
    update_interval: 5.0           # Default: 5.0 (steady-state seconds between streamed edits)
    min_update_interval: 0.5       # Default: 0.5 (fast-start seconds between early edits)
    interval_ramp_seconds: 15.0    # Default: 15.0 (set 0 to disable interval ramping)
    max_idle: 2.0                  # Default: 2.0 (event-driven idle ceiling before the next edit)
  learning: true                   # Default: true
  learning_mode: always            # Default: always (or agentic)
  max_preload_chars: 50000         # Hard cap for preloaded context from context_files
  tool_output_auto_save_threshold_bytes: 51200  # Auto-save supported tool outputs larger than 50 KiB
  show_stop_button: true           # Default: true (global only, cannot be overridden per-agent)
  num_history_runs: null           # Number of prior runs to include (null = all)
  num_history_messages: null       # Max messages from history (null = use num_history_runs)
  compress_tool_results: false     # Safer default; enabling can invalidate Anthropic/Vertex Claude prompt caches
  # Required compaction is enabled by default.
  # Soft thresholds do not compact by themselves while history still fits.
  # Set enabled: false to disable automatic pre-reply compaction globally.
  compaction:
    enabled: true
    threshold_percent: 0.8
    reserve_tokens: 16384
  max_tool_calls_from_history: null  # Limit tool call messages replayed from history (null = no limit)
  show_tool_calls: true            # Default: true (show tool details inline; hidden mode still allows generic worker warmup copy)
  worker_tools: null               # Default: null (tool names to route through workers; null = use MindRoom's default routing policy, [] = disable)
  worker_scope: null               # Default: null (no runtime reuse; set shared/user/user_agent to enable)
  worker_grantable_credentials: null  # Default: null (deny by default; list credential service names to make available inside isolated workers, e.g. [openai, github_private])
  allow_self_config: false         # Default: false (allow agents to modify their own config via a tool)
  thread_summary_temperature: 0.2  # Default: 0.2 (set null to omit temperature and use provider defaults)
  thread_summary_first_threshold: 1  # Default: 1 (first automatic thread summary after first message)
  thread_summary_subsequent_interval: 10  # Default: 10 (messages between later automatic thread summaries)

# defaults.tools are appended to each agent's tools list with duplicates removed.
# Set agents.<name>.include_default_tools: false to opt out a specific agent.
# defaults.streaming is also global-only and controls streamed message edit cadence.
# Tools can be plain strings or single-key dicts with per-agent config overrides.

MindRoom uses `defaults.thread_summary_temperature` for automatic thread summaries on providers that support runtime temperature overrides.
Set it to `null` to omit the field and use provider defaults.
MindRoom always omits temperature for Vertex Claude thread summaries because the provider rejects that field on this path.
Use `room_thread_summary_models` when automatic summaries in a specific room should use a different model from `defaults.thread_summary_model`.
Keys can be managed room aliases such as `lobby` or raw Matrix room IDs such as `!room:example.org`.

`defaults.worker_grantable_credentials` is a list of credential service names.
Use built-in names like `openai`, `azure`, `anthropic`, `google`, `openrouter`, `deepseek`, `cerebras`, `groq`, `ollama`, and `github_private`, or custom shared credential service names you saved through the dashboard or API.
Google OAuth client config and Google OAuth token services stay in the primary runtime and cannot be mirrored into isolated workers.
If a tool runs inside an isolated worker, only the services listed here are available to that worker.
Leave this unset to keep isolated workers deny-by-default for shared credentials.
This setting never injects provider env vars such as `OPENAI_API_KEY`.

For worker-routed tools, it only controls which shared credentials MindRoom may load inside isolated workers.
This setting also does not control local shared-only integrations that stay in the main runtime, such as `homeassistant`.
Those tools keep using normal shared credentials even when `worker_grantable_credentials` is empty.
`google_vertex_adc` is intentionally not supported here because isolated workers do not receive ADC files or `GOOGLE_APPLICATION_CREDENTIALS`; use that auth path only in the main runtime.
Sandbox-proxied execution is stricter than direct local execution: ordinary runtime `.env` values and provider env do not carry over unless they are explicitly passed through.

# Required compaction is destructive inside the active session.
# It uses one Matrix lifecycle notice that is edited in place.
# It runs before a reply when raw history exceeds the hard replay budget.
# It also runs before the next reply after a manual compact_context request.
# Otherwise MindRoom leaves the stored session unchanged and relies on replay fitting for that reply.
# It rewrites the stored session summary and removes compacted raw runs from the live session.
# Agno then replays only the summary plus recent runs.
# Use __MINDROOM_INHERIT__ inside a tool override to clear one inherited authored field
# while keeping the rest of defaults.tools for that agent.
# See agents.md for the full per-agent tool configuration syntax.
# These thresholds only affect automatic thread summaries; manual `set_thread_summary`
# tool calls write immediately and reset the automatic baseline from the new message count.

# Memory system configuration (optional)
memory:
  backend: mem0                    # Global default backend (mem0, file, or none); agents can override with memory_backend
  team_reads_member_memory: false  # Default: false (when true, team reads can access member agent memories)
  embedder:
    provider: openai               # Default: openai (openai, ollama, huggingface, sentence_transformers)
    config:
      model: text-embedding-3-small  # Default embedding model
      api_key: null                # Optional: From env var
      host: null                   # Optional: For self-hosted
      dimensions: null             # Optional: Embedding dimension override (e.g., 256)
  llm:                             # Optional: LLM for memory operations
    provider: ollama
    config: {}
  file:                            # File-backed memory settings (when backend: file)
    path: null                     # Optional: fallback root for file memory paths
    max_entrypoint_lines: 200      # Default: 200 (max lines preloaded from MEMORY.md)
  search:                          # File-backed memory search settings
    mode: keyword                  # Default: keyword (keyword or semantic)
    include:                       # Root-relative globs below the effective file-memory root
      - memory/**/*.md             # Default: dated daily memory files
    include_entrypoint: false      # Default: false (MEMORY.md is already preloaded)
  auto_flush:                      # Background memory auto-flush (file backend only)
    enabled: false                 # Default: false (enable background flush worker)
    flush_interval_seconds: 1800   # Default: 1800 (loop interval)
    idle_seconds: 120              # Default: 120 (idle time before flush eligibility)
    max_dirty_age_seconds: 600     # Default: 600 (force flush after this many seconds dirty)
    stale_ttl_seconds: 86400       # Default: 86400 (drop stale flush-state entries older than this)
    max_cross_session_reprioritize: 5  # Default: 5 (same-agent dirty sessions reprioritized per prompt)
    retry_cooldown_seconds: 30     # Default: 30 (cooldown before retrying a failed extraction)
    max_retry_cooldown_seconds: 300  # Default: 300 (upper bound for retry cooldown backoff)
    batch:
      max_sessions_per_cycle: 10   # Default: 10 (max sessions processed per auto-flush loop)
      max_sessions_per_agent_per_cycle: 3  # Default: 3 (max sessions per agent per loop)
    extractor:
      no_reply_token: NO_REPLY     # Default: NO_REPLY (token indicating no durable memory)
      max_messages_per_flush: 20   # Default: 20 (max messages considered per extraction)
      max_chars_per_flush: 12000   # Default: 12000 (max chars considered per extraction)
      max_extraction_seconds: 30   # Default: 30 (timeout for one extraction job)
      include_memory_context:
        memory_snippets: 5         # Default: 5 (max MEMORY.md snippets for dedupe context)
        snippet_max_chars: 400     # Default: 400 (max chars per snippet)
#
# See docs/memory.md for full auto-flush behavior and tuning guidance.
#
# Set memory.embedder.provider: sentence_transformers to run embeddings in-process.
# MindRoom auto-installs that optional extra on first use.

# Knowledge base configuration (optional)
# Keys must be non-empty single path components, so do not use "", ., .., /, \, or line breaks in a knowledge base ID.
knowledge_bases:
  docs:
    description: Product documentation, support notes, and operating procedures
    # Default: semantic.
    # Use files to skip embeddings and expose workspace files.
    mode: semantic
    path: ./knowledge_docs          # Folder containing documents for this base (Pydantic default)
    watch: false                   # Direct external edits require reindex; API mutations still schedule refresh
    chunk_size: 5000               # Default: 5000 (max characters per indexed chunk)
    chunk_overlap: 0               # Default: 0 (overlapping characters between chunks)
    git:                           # Optional: Sync this folder from a Git repository
      repo_url: https://github.com/pipefunc/pipefunc
      branch: main
      poll_interval_seconds: 300  # Interval for background Git refresh scheduling
      lfs: false                   # Optional: enable Git LFS support (requires git-lfs on the runtime host)
      sync_timeout_seconds: 3600   # Optional: abort a hung git command after this many seconds
      skip_hidden: true
      include_patterns: ["docs/**"]  # Optional: root-anchored glob filters
      exclude_patterns: []
      credentials_service: github_private # Optional: service in CredentialsManager

# Voice message handling (optional)
voice:
  enabled: false                   # Default: false
  visible_router_echo: true        # Optional: show the normalized voice text from the router
  stt:
    provider: openai               # Default: openai
    model: whisper-1               # Default: whisper-1
    api_key: null
    host: null
  intelligence:
    model: default                 # Model for command recognition

# Internal MindRoom user account (optional, omit for hosted/public profiles)
# When present, defaults are: username: mindroom_user, display_name: MindRoomUser
mindroom_user:
  username: mindroom_user          # Set before first startup (account-creation request localpart only)
  display_name: MindRoomUser       # Can be changed later

# Matrix room onboarding/discoverability (optional)
matrix_room_access:
  mode: single_user_private        # Default keeps invite-only/private behavior
  multi_user_join_rule: public     # In multi_user mode: public or knock
  publish_to_room_directory: false # Publish managed rooms in server room directory
  invite_only_rooms: []            # Room keys/aliases/IDs that stay invite-only/private
  reconcile_existing_rooms: false  # Explicit migration of existing managed rooms

# Authorization (optional)
authorization:
  global_users: []                 # Users with access to all rooms
  room_permissions: {}             # Keys: room ID (!id), full alias (#alias:domain), or managed room key (alias)
  default_room_access: false       # Default: false
  config_command_enabled: false    # Enable !config for global admin users; default: false
  aliases: {}                      # Map canonical Matrix user IDs to bridge aliases (see authorization docs)
  agent_reply_permissions: {}      # Per-agent/team/router (or '*') reply allowlists; supports globs like '*:example.com'

# Managed room metadata (optional)
# Keys are managed room aliases.
# Rooms listed here are created even before agents or teams are assigned.
rooms:
  lobby:
    display_name: Lobby
    description: Main assistant room

# Room-specific model overrides (optional)
# Keys are room aliases, values are model names from the models section.
# Example: room_models: {dev: sonnet, lobby: gpt4o}
room_models: {}

# Room-specific automatic thread summary model overrides (optional)
# Keys are room aliases or raw Matrix room IDs, values are model names from the models section.
# Example: room_thread_summary_models: {lobby: haiku}
room_thread_summary_models: {}

# Non-MindRoom bot accounts to exclude from multi-human detection (optional)
# These accounts won't trigger the mention requirement in threads
bot_accounts:
  - "@telegram:example.com"

# Plugin paths (optional)
plugins: []

# Matrix Space grouping (optional)
matrix_space:
  enabled: true                    # Default: true (create a root Matrix Space for managed rooms)
  name: MindRoom                   # Default: "MindRoom" (display name for the root Space)

# Matrix delivery policy (optional)
matrix_delivery:
  ignore_unverified_devices: false # Default: false (keep Matrix E2EE device-trust checks enabled)

# Timezone for scheduled tasks (optional)
timezone: America/Los_Angeles      # Default: UTC
```

When `matrix_space.enabled` is `true`, MindRoom grants root Space admin power to concrete users in `authorization.global_users`.
If `mindroom_user` is configured and its account exists, MindRoom grants that internal account root Space admin power too.
Room-specific `authorization.room_permissions` users are invited only through their room permissions and do not become root Space admins unless they are also global users.
Root Space admin reconciliation is grant-only and preserves existing Matrix admins.
Removing a user from `authorization.global_users` stops future MindRoom authorization but does not automatically demote that user in the Space.
Demote stale Space admins manually in a Matrix client when needed.

`matrix_delivery.ignore_unverified_devices` is an explicit opt-in for outgoing encrypted Matrix sends.
Leave it `false` to preserve Matrix E2EE device-trust checks.
Setting it to `true` can improve bot delivery when rooms contain unverified devices, but Matrix may encrypt messages for devices the bot has not verified.

## Credential Seeds

MindRoom can bootstrap additional shared credential services at startup from explicit seed declarations.
Use this for deployment-managed credentials that should live in `CredentialsManager` without requiring inline one-off migration scripts.
Seeded credentials are marked `_source=env`: MindRoom updates them on later startups, but it never overwrites dashboard-managed credentials (`_source=ui`) or legacy credentials with no source marker.

Set `MINDROOM_CREDENTIAL_SEEDS_FILE` to a JSON file path, or `MINDROOM_CREDENTIAL_SEEDS_JSON` to equivalent inline JSON.
Relative file paths resolve from the config directory.
Credential fields can read from env vars, from files, or from literal values:

```json
[
  {
    "service": "example_oauth_client",
    "credentials": {
      "client_id": {"env": "EXAMPLE_CLIENT_ID"},
      "client_secret": {"env": "EXAMPLE_CLIENT_SECRET"}
    }
  }
]
```

Env refs use the existing secret convention: if `EXAMPLE_CLIENT_SECRET` is unset, MindRoom also checks `EXAMPLE_CLIENT_SECRET_FILE` and reads that file.
If any declared field is missing or empty, MindRoom skips that seed instead of creating a partial credential document.

## Credential Storage Encryption

Set `MINDROOM_CREDENTIALS_ENCRYPTION_KEY` to encrypt stored credential files at rest.
The key must be a base64-encoded 32-byte value and must stay stable across restarts.
Generate one with:

```bash
python - <<'PY'
import base64
import os

print(base64.urlsafe_b64encode(os.urandom(32)).decode("ascii"))
PY
```

When this variable is configured, `CredentialsManager` writes encrypted credential files with mode `0600` and creates credential directories with mode `0700`.
Encrypted mode refuses plaintext credential JSON files.
No plaintext-to-encrypted migration is performed automatically, so configure the key before saving credentials that must be encrypted.

## Debug Logging

`debug.log_llm_requests` enables pre-provider request assembly logging for troubleshooting.
When enabled, MindRoom writes JSONL request records under `debug.llm_request_log_dir` or `mindroom_data/logs/llm_requests` by default.
Those records include prompts, messages, tool schemas, model parameters, correlation IDs, requester metadata, and source Matrix event metadata.
The same flag also records successful tool-call rows in `mindroom_data/tracking/tool_calls.jsonl` so tool activity can be correlated with LLM request logs.
Tool failures are always recorded in `tool_calls.jsonl`, even when request logging is disabled.
Audit logging remains enabled.
Credential-bearing fields such as tokens, cookies, passwords, API keys, and authorization headers are redacted before log records are emitted.
These artifacts can still contain sensitive non-credential prompt, argument, and result data.
Leave the flag disabled unless you are actively debugging.

## Built-In Prompt Overrides

MindRoom keeps built-in prompt defaults as uppercase globals in `src/mindroom/prompts.py`.
Use the optional root `prompts` block to override those defaults without editing Python code.
Keys must match the uppercase global names exactly, and unknown keys fail config validation.

```yaml
prompts:
  HIDDEN_TOOL_CALLS_PROMPT: |
    Tool calls are hidden from users.
    Answer directly without narrating internal tool use.
  ROUTER_AGENT_SELECTION_PROMPT_TEMPLATE: |
    Decide which agent or team should respond to this message.

    Available agents and teams:

    {agents_info}

    User message: {message}

    Return only the agent or team name.
```

Discover allowed names by inspecting `src/mindroom/prompts.py` or by importing `PROMPT_DEFAULT_NAMES` from `mindroom.prompts`.
For prompts with `{...}` placeholders, discover the allowed fields from `PROMPT_TEMPLATE_FIELDS` in `mindroom.prompts`.
MindRoom validates configured prompt placeholders at config load, and unsupported placeholders fail validation before runtime.
Prompt placeholders are not Jinja and not Python `str.format`; MindRoom only replaces exact `{field_name}` placeholders.
Only bare placeholder names are supported; compound access, conversions, and format specs such as `{message.text}`, `{message!r}`, or `{message:.2f}` are rejected.
Escape literal braces as `{{` and `}}` inside prompt overrides that use placeholders.
Changing construction-time root prompt overrides in a running process restarts existing agents, teams, and the router through hot reload.
Prompt overrides that are read per request are picked up without restarting bots.

## Managed Avatars

MindRoom can generate managed avatars for agents, teams, rooms, and the optional root Matrix Space.
Use the root `prompts` block to override the built-in avatar prompt styles without editing Python code.
Every omitted prompt falls back to MindRoom's built-in default.

```yaml
prompts:
  AVATAR_CHARACTER_STYLE: "professional AI avatar portrait, abstract geometric silhouette"
  AVATAR_ROOM_STYLE: "minimalist wayfinding icon, precise geometry, strong silhouette"
  AVATAR_AGENT_SYSTEM_PROMPT: "You are creating distinctive visual elements for a professional AI agent avatar."
  AVATAR_TEAM_SYSTEM_PROMPT: "You are creating distinctive visual elements for a professional AI team avatar."
  AVATAR_ROOM_SYSTEM_PROMPT: "You are creating a refined, minimalist icon design for a room avatar."
```

`mindroom avatars generate` only creates missing local avatar files by default.
Run `mindroom avatars generate --force` to overwrite existing managed workspace avatar files after changing prompts or styles.
`mindroom avatars sync` only fills missing Matrix avatars by default.
Run `mindroom avatars sync --force` to replace existing Matrix room or root-space avatars.

## Internal User Username

- Configure `mindroom_user.username` with the Matrix localpart to request before first startup.
- After the account is created, `mindroom_user.username` is locked as the account-creation request and cannot be changed in-place.
- Hosted provisioning can return a different actual Matrix ID; MindRoom persists that actual ID and uses it for runtime authorization.
- You can safely change `mindroom_user.display_name` at any time.

## Sections

- [Agents](agents.md) - Configure individual AI agents
- [Models](models.md) - Configure AI model providers
- [Teams](teams.md) - Configure multi-agent collaboration
- [Cultures](cultures.md) - Configure shared agent cultures
- [Router](router.md) - Configure message routing
- [Dynamic Tools](../tools/dynamic-tools.md) - Configure optional tools that agents load on demand
- [Memory](../memory.md) - Configure memory providers and behavior
- [Knowledge Bases](../knowledge.md) - Configure file-backed knowledge bases
- [Voice](../voice.md) - Configure speech-to-text voice processing
- [Authorization](../authorization.md) - Configure user and room access control
- [Matrix Space](../matrix-space.md) - Configure the root Matrix Space for managed rooms
- [Skills](../skills.md) - Skill format, gating, and allowlists
- [Plugins](../plugins.md) - Plugin manifest and tool/skill loading

## Notes

- All top-level sections are optional with sensible defaults, but at least one agent is recommended for Matrix interactions
- A model named `default` is required unless agents, teams, and the router all specify explicit non-`default` models
- Agents can set `knowledge_bases`, but each entry must exist in the top-level `knowledge_bases` section
- `agents.<name>.accept_invites` defaults to `true`; when enabled, authorized ad-hoc room invites are accepted and persisted across restarts without adding those rooms to the static `rooms` list
- Approval-gated tools require the router to be joined to the Matrix room.
- In ad-hoc invited rooms accepted through `accept_invites`, approval only works if the router is already joined to that room.
- `agents.<name>.context_files` load files from the agent's workspace into each agent instance, so edits take effect on the next reply without restarting (see [Agents](agents.md))
- `agents.<name>.room_thread_modes` overrides `thread_mode` for specific rooms, and resolution is room-aware for agents, teams, and router decisions (see [Agents](agents.md))
- `memory.backend` sets the global memory default, and `agents.<name>.memory_backend` overrides it per agent
- `memory.search` controls file-backed `search_memories`, and `agents.<name>.memory_search` overrides it per agent
- `memory.backend: none`, `memory: none`, or `agents.<name>.memory_backend: none` disables built-in durable memory for the effective agent without disabling Agno Learning
- `defaults.max_preload_chars` caps preloaded file context (`context_files`)
- When `authorization.default_room_access` is `false`, only users in `global_users` or room-specific `room_permissions` can interact with agents
- `authorization.config_command_enabled` defaults to `false`; when set to `true`, `!config` still requires `global_users`
- `authorization.agent_reply_permissions` can further restrict which users specific agents/teams/router will reply to
- `authorization.aliases` maps bridge bot user IDs to canonical users so bridged messages inherit the same permissions (see [Authorization](../authorization.md))
- `authorization.room_permissions` accepts room IDs, full room aliases, and managed room keys
- `matrix_room_access.mode` defaults to `single_user_private`; this preserves current private/invite-only behavior
- In `multi_user` mode, MindRoom sets managed room join rules and directory visibility from config
- In `multi_user` mode, MindRoom also reconciles managed room power levels so `com.mindroom.thread.tags` can be written at PL0
- Publishing to the room directory requires the managing service account (typically router) to have moderator/admin power in each room
- Thread-tag power-level reconciliation also requires the managing service account to be joined and able to update `m.room.power_levels`
- The `memory` system works out of the box with OpenAI; use `memory.llm` for memory summarization with a different provider
