Skip to main content
Configuration is realm-scoped. The active realm_id determines which config file is read and written.

Realm-scoped config

Every realm has its own config file and runtime state:
  • config.toml (user settings)
  • config_state.json (generation counter for CAS)
  • realm_manifest.json (pinned backend + metadata)
The canonical location is under the active realm state root. For CLI commands, the default is project-local:
For daemon/API surfaces that do not receive a --state-root, the default state root is the platform data directory:
  • macOS: ~/Library/Application Support/meerkat/realms/<realm>/
  • Linux: ~/.local/share/meerkat/realms/<realm>/
  • Windows: %APPDATA%\meerkat\realms\<realm>\
Use realms to choose whether state is shared or isolated.

Realm config inheritance

A realm config doc may declare a parent, and resolution walks a single chain head -> parent -> ... -> global rooted at the reserved global realm. A realm with no explicit parent that is not itself global implicitly parents to global, so global is the universal default head of every chain.
  • Config inherits along the chain: models, MCP servers, hooks, skills, limits, and auth bindings are composed parent-first with child-wins.
  • State never inherits: sessions, session stores, event stores, and the .rkat/ projection stay realm-local to the consuming (head) realm.
The global realm’s config document is home-rooted at ~/.rkat/config.toml (a single well-known location, not a per-workspace file), which is why a single rkat auth login is inherited by every workspace realm via the chain tail. See Realm inheritance for the full model, including credential read-inherit / write-strict-owner and the add/override-but-not-remove rules for MCP servers and hooks.

Effective precedence

For a given realm, effective persisted configuration is:
  1. Built-in defaults
  2. Inherited parent-chain config (parent-first, child-wins, ending at global)
  3. Realm config.toml (the head realm’s own doc)
  4. Per-request runtime parameters (for example model/system prompt/tool toggles on create/resume calls)
The agent build path on every surface (CLI, REST, RPC, MCP server) composes this effective config over the head realm, so model/MCP/hook/skill/limit inheritance applies on every build, not just the auth path. config get / config set operate on the raw head doc (a read/write split), so an inherited entry is read through composition but never flattened into the child’s own doc on write. Environment variables still matter, but they are primarily part of runtime credential resolution, not a general config layer that mutates the loaded realm config. In practice:
  • realm config remains the canonical persisted settings surface
  • provider credentials can still come from RKAT_* or provider-native env vars at resolve time
  • bindings and auth profiles decide how those credentials are used at runtime

Config APIs and CAS

RPC, REST, and MCP all expose the same config envelope:
  • config
  • generation
  • realm_id
  • instance_id
  • backend
Some surfaces also expose resolved_paths in diagnostic/admin contexts. Treat that field as optional rather than universal across all public config envelopes. Writes support optimistic concurrency:
  • config/set and config/patch accept optional expected_generation
  • stale writes return generation conflict
This gives deterministic behavior when multiple clients or processes update config concurrently.

Merge semantics

Meerkat uses three update models:
  1. Layered config loading (defaults -> file -> runtime) uses field-wise merge:
  • scalar/option values: last non-default wins
  • section values (for example store, comms, compaction): replace whole section
  • hook entries: append/extend
  1. Realm-chain composition folds each realm doc parent-first with child-wins:
  • per-provider model defaults: union, child-wins per provider (a child overrides its anthropic default while inheriting the parent’s openai default)
  • MCP servers: map-key union, child-wins per name (add or override, never remove)
  • hooks and skill sources: append parent-first (child cannot remove inherited entries)
  • limits and other config sections: per-field child-wins (a child can tighten max_sessions while inheriting other caps)
  • the binding/backend/auth maps are not merged across realms: each realm’s binding resolves its backend_profile / auth_profile only within its own section (the owning realm)
  1. Runtime patch APIs (config/patch, PATCH /config) use RFC 7396 JSON merge-patch semantics.
Use config/set when replacing the whole config intentionally, and config/patch for surgical updates.

Presence-based override (Option fields)

Config.max_tokens and agent.max_tokens_per_turn are optional. None means “inherit / use the template default” and is resolved at point-of-use (resolved_max_tokens / resolved_max_tokens_per_turn). Because merge is presence-based (an explicit value wins, regardless of whether it equals the default), a child realm can override a non-default parent value back down to the default — something a != default heuristic could not express.

CLI behavior

CLI config commands operate on the selected realm:
Without --realm, CLI derives a stable workspace realm (ws-...) from the current directory, or from --context-root when supplied.

What about ~/.rkat/config.toml and .rkat/config.toml?

~/.rkat/config.toml is the home-rooted config document for the reserved global realm — the universal tail of every realm chain. rkat auth login writes the [realm.global] binding section here, and every workspace realm inherits it. The project-local .rkat/config.toml still exists for developer ergonomics (rkat init, layered hook loading), but the active realm’s config remains the canonical runtime configuration for CLI/RPC/REST/MCP surfaces.

Backend and storage settings

Session backend is pinned per realm in realm_manifest.json (sqlite, jsonl, or memory). --realm-backend only matters on first creation of a realm.

Compaction settings

Compaction runtime policy is configurable in realm config:
When session-compaction is enabled, AgentFactory maps these values into the runtime CompactionConfig.

Retry and stream watchdog settings

LLM retry policy (RetryPolicy) is configurable in realm config:
call_timeout is a tri-state override for the hard per-LLM-call deadline: omitted inherits the profile-derived default, "disabled" turns it off, and a duration sets it explicitly. stream_inactivity_timeout (0.8.4+) is the per-provider-stream inactivity watchdog. It is on by default with a 300-second window: an LLM call whose stream reports no events for the window is aborted with the retryable StreamStalled failure and flows through normal retry handling — before 0.8.4 a silent provider stream hung the turn forever. Each received stream event re-arms the window, so long-but-alive responses are unaffected. Set stream_inactivity_timeout = "disabled" to opt out, or a duration ("120s") for an explicit window. Unlike call_timeout it never inherits from the model profile; omitted means the built-in 300s default. The watchdog covers all built-in provider clients; a custom AgentLlmClient that does not report stream liveness fails open (no watchdog — only the hard call timeout applies).

Model fallback settings

The model fallback chain is realm-scoped configuration. Empty chain means “use the catalog default chain”; a non-empty chain is an explicit ordered operator policy:
A fallback target is identified by model, provider, self-hosted server, and auth_binding. This means an explicit custom chain can retry the same model/provider through a different credential binding. Catalog-default chains stay inside the selected non-env auth realm and skip providers that are not registered there. Use enabled = false to disable runtime model failover. If a lower-precedence layer disabled fallback or supplied a custom chain, set use_catalog_default_chain = true in a higher-precedence layer to restore the built-in catalog chain. Fallback activation is still runtime-governed: the generated recovery authority must classify the LLM failure as recoverable, and the core run loop must decide the retry is pre-stream safe. Network/call timeouts never trigger model fallback, and cross-model fallback is suppressed after user-visible text or reasoning stream output has been emitted. On activation, Meerkat recomputes the active model’s tool capability filter and token limits before retrying.

See also