Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help


audience: [contributor, agent] source_files:

  • crates/chatty-core/src/settings/repositories/mod.rs
  • crates/chatty-core/src/settings/repositories/generic_json_repository.rs
  • crates/chatty-core/src/settings/repositories/oauth_credential_json_repository.rs
  • crates/chatty-core/src/settings/repositories/module_settings_json_repository.rs
  • crates/chatty-core/src/services/mcp_token_store.rs
  • crates/chatty-core/src/settings/models/ related:
  • ./env-vars.md
  • ./provider-matrix.md
  • ./singleton-inventory.md
  • ../adrs/settings-integration-map.md

Settings schema reference

When to read this: Find the JSON file, Rust model, key fields, and defaults for a persisted setting. Source of truth: crates/chatty-core/src/settings/repositories/ + settings/models/.

Field tables are hand-maintained in scripts/gen-docs-reference.sh; CI checks the repository file names and the ExecutionSettingsModel / ModelConfig field lists against the source (make docs-check-reference).

Config vs data directories

JSON settings live under dirs::config_dir()/chatty (used by GenericJsonRepository). WASM module binaries and agent memory live under dirs::data_dir()/chatty.

KindResolverLinuxmacOSWindows
Config (this page)dirs::config_dir()/chatty~/.config/chatty/ ($XDG_CONFIG_HOME/chatty)~/Library/Application Support/chatty/%APPDATA%\chatty\
Datadirs::data_dir()/chatty~/.local/share/chatty/ ($XDG_DATA_HOME/chatty)~/Library/Application Support/chatty/%APPDATA%\chatty\

On macOS and Windows, config and data resolve to the same Application Support / AppData folder. On Linux they differ (~/.config vs ~/.local/share).

Persistence behaviour

  • Single-object files (load / save): missing file → T::default().
  • List files (load_all / save_all): missing file → [].
  • Settings JSON writes are atomic: pretty-printed JSON to a sibling *.json.<pid>.<seq>.tmp, then rename (GenericJsonRepository). Saves of one file are serialized and the newest call wins; an older save that lands late is dropped rather than written over a newer one.
  • Serde field names are snake_case unless a table notes otherwise.
  • #[serde(skip)] fields are runtime-only and never written to disk.

File inventory

FileShapeModelRepository
general_settings.jsonobjectGeneralSettingsModelGeneralSettingsJsonRepository
execution_settings.jsonobjectExecutionSettingsModelExecutionSettingsJsonRepository
search_settings.jsonobjectSearchSettingsModelSearchSettingsJsonRepository
training_settings.jsonobjectTrainingSettingsModelTrainingSettingsJsonRepository
user_secrets.jsonobjectUserSecretsModelUserSecretsJsonRepository
hive_settings.jsonobjectHiveSettingsModelHiveSettingsJsonRepository
extensions.jsonobjectExtensionsModelExtensionsJsonRepository
token_tracking.jsonobjectTokenTrackingSettingsTokenTrackingJsonRepository
module_settings.jsonobjectModuleSettingsModelModuleSettingsJsonRepository
providers.jsonarrayProviderConfigJsonFileRepository
models.jsonarrayModelConfigJsonModelsRepository
mcp_servers.jsonarrayMcpServerConfigJsonMcpRepository
a2a_agents.jsonarrayA2aAgentConfigA2aJsonRepository
mcp_oauth_<sanitized>.jsonobjectopaque StoredCredentials JSONlive: FileCredentialStore; also JsonOAuthCredentialRepository

Secrets in bold must never appear in logs, traces, or LLM-facing tool output.

Secrets kept outside the JSON (AGE-741). In providers.json, mcp_servers.json, a2a_agents.json and hive_settings.json the secret fields marked (store) below are not written to the file. The model keeps the plain field; on disk it becomes <field>_ref ("api_key_ref": "provider/<name>", mcp/<name>, a2a/<name>, hive/token, hive/refresh_token) and the value lives in the SecretStore (settings/secret_store.rs): the OS keychain under the service chatty:<config dir>, or, when no keychain answers, the owner-only secrets.json in the config dir (one warning line; CHATTY_SECRET_STORE=keychain refuses the fallback, =file forces it). Docs examples use "****" or omit the field.


general_settings.json — GeneralSettingsModel

Source: settings/models/general_model.rs.

FieldTypeDefaultNotes
font_sizef3214.0UI font size (px)
theme_nameOption<String>nullTheme folder name; null = built-in default
dark_modeOption<bool>nullnull = follow OS / theme default
{ "font_size": 14.0, "theme_name": null, "dark_mode": null }

execution_settings.json — ExecutionSettingsModel

Source: settings/models/execution_settings.rs. Master toggle enabled is opt-in (false) for security.

FieldTypeDefaultNotes
enabledboolfalseMaster code-execution toggle
approval_modeApprovalMode"AutoApproveSandboxed"JSON enum: AlwaysAsk, AutoApproveSandboxed, AutoApproveAll
workspace_dirOption<String>nullAbsolute path required for filesystem / git tools
filesystem_read_enabledbooltrueRequires workspace_dir
filesystem_write_enabledbooltrueRequires workspace_dir + approval
fetch_enabledbooltrueBuilt-in read-only HTTP GET
git_enabledboolfalseOpt-in; workspace must be a git repo
browser_enabledboolfalseOpt-in built-in browser tools; may download a pinned Chrome on first use
allow_private_network_accessboolfalseWith internet access on, let the browser reach private/internal IPs (RFC-1918) on your own network; the SSRF guard refuses them otherwise. Cloud-metadata 169.254.0.0/16 stays refused regardless (AGE-459)
execute_code_enabledboolfalseExposes execute_code to the model
docker_code_execution_enabledboolfalseDocker fallback for non-Monty code
docker_hostOption<String>nullSocket / URI; null = auto-detect
timeout_secondsu3230Execution timeout
max_output_bytesusize5120050 KiB cap
network_isolationboolfalseSandbox network isolation when available
sandbox_requiredboolfalseRefuse to start a shell outside the sandbox instead of falling back to the host (AGE-918)
max_agent_turnsu320Tool-call rounds per response; 0 = no cap (headless runs get a wall-clock budget instead, chatty-tui --max-duration)
memory_enabledbooltrueremember / search_memory
warn_on_external_agentboolfalsesay so before invoke_agent sends a prompt outside the fleet (ADR-0011 C5)
embedding_enabledboolfalseSemantic memory search
embedding_providerOption<ProviderType>nullIndependent of chat provider
embedding_modelOption<String>nulle.g. text-embedding-3-small
hosted_conversations_enabledboolfalseDeveloper-only: offers the per-conversation move between local and hosted (AGE-308)
tool_loadingToolLoading"all"How tools are offered to the model. JSON: all (every allowed tool on every request) or dynamic (a small core, plus groups the model loads with load_tools). Omitted from the file while all. CLI override: chatty-tui --tool-loading
ask_user_enabledbooltrueOffer the ask_user tool (ADR-0011 C7). Omitted from the file while true. CLI override: chatty-tui --disable ask-user (unattended runs, so a stray call fails fast instead of blocking)
terminal_accessboolfalseOffer the read-only terminal_read tool over the user’s tmux panes (AGE-577); registered only while a tmux server has panes. Omitted from the file while false. CLI: chatty-tui --enable terminal

ApprovalMode has no rename_all — JSON uses the Rust variant names above.


search_settings.json — SearchSettingsModel

Source: settings/models/search_settings.rs. Also stores Browser Use and Daytona keys (not only web search).

FieldTypeDefaultNotes
enabledboolfalseMaster web-search toggle
active_providerSearchProvider"Tavily"JSON: Tavily or Brave
tavily_api_keyOption<String>nullSecret
brave_api_keyOption<String>nullSecret
max_resultsusize5Search hit cap
browser_use_enabledbooltrueKey alone is enough to activate; set false to disable without deleting the key
browser_use_api_keyOption<String>nullSecret
daytona_enabledbooltrueSame pattern as browser-use
daytona_api_keyOption<String>nullSecret
rerank_urlOption<String>nullCohere/Jina-style /rerank endpoint; keyless search only, needs rerank_model too
rerank_modelOption<String>nullModel name sent to rerank_url
{
  "enabled": false,
  "active_provider": "Tavily",
  "max_results": 5,
  "browser_use_enabled": true,
  "daytona_enabled": true
}

training_settings.json — TrainingSettingsModel

Source: settings/models/training_settings.rs. Both flags opt-in.

FieldTypeDefaultNotes
atif_auto_exportboolfalseATIF JSON after each completed assistant turn
jsonl_auto_exportboolfalseJSONL (SFT + DPO) after each completed turn

user_secrets.json — UserSecretsModel

Source: settings/models/user_secrets_store.rs. Injected into shell sessions as env vars; never sent to the LLM.

FieldTypeDefaultPersisted?
secrets[{key, value}][]yes — values are secrets
revealed_keysHashSet<String>emptyno (#[serde(skip)]; UI-only)
{ "secrets": [ { "key": "EXAMPLE_TOKEN", "value": "****" } ] }

hive_settings.json — HiveSettingsModel

Source: settings/models/hive_settings.rs.

FieldTypeDefaultNotes
registry_urlStringhttp://localhost:8080DEFAULT_REGISTRY_URL
runner_urlStringhttp://localhost:8081DEFAULT_RUNNER_URL
tokenOption<String>nullOne-hour access JWT. Secret (store: token_ref → hive/token). is_logged_in() = token present
refresh_tokenOption<String>null30-day refresh token, rotated on every refresh. Secret (store: refresh_token_ref → hive/refresh_token)
expires_atOption<DateTime>nullWhen token expires
usernameOption<String>nullCached for UI
emailOption<String>nullCached for re-login

extensions.json — ExtensionsModel

Source: settings/models/extensions_store.rs. Unified install list for MCP, WASM, and A2A.

FieldTypeDefaultPersisted?
extensions[InstalledExtension][]yes
mcp_auth_statusesmapemptyno (#[serde(skip)])
a2a_statusesmapemptyno (#[serde(skip)])

InstalledExtension:

FieldTypeDefaultNotes
idStringrequiredUnique slug
display_nameStringrequiredUI name
descriptionStringrequired
kindExtensionKindrequiredInternally tagged kind: mcp (flattened McpServerConfig), wasm, a2a (flattened A2aAgentConfig)
sourceExtensionSourcerequiredInternally tagged type: hive (module_name, version) or custom
pricing_modelOption<String>nullHive marketplace classification
enabledbooltrue

token_tracking.json — TokenTrackingSettings

Source: settings/models/token_tracking_settings.rs. The compaction share — ADR-0007’s utilisation key. Ratio fields (high_threshold, critical_threshold) are fractions of the model’s context window, never a character count.

FieldTypeDefaultNotes
enabledbooltrueShow the token context bar
response_reserveusize4096Tokens reserved for model output
high_thresholdf640.70Ratio 0.0–1.0; bar turns amber
critical_thresholdf640.90Ratio 0.0–1.0; bar turns red
auto_summarizeboolfalseAuto-summarize on crossing critical_threshold
summarization_model_idOption<String>nullnull = use the conversation’s own model
cap_usdOption<f64>nullMonthly spend cap (USD); hive-only (AGE-416 / ADR-0010)

module_settings.json — ModuleSettingsModel

Source: settings/models/module_settings.rs. Load path (ModuleSettingsJsonRepository) runs normalize_module_dir(): empty values and the legacy .chatty/modules fallback become the platform data-dir default.

FieldTypeDefaultNotes
enabledboolfalseWASM module runtime
module_dirStringplatform data dir + /chatty/modulesLinux ~/.local/share/chatty/modules; macOS ~/Library/Application Support/chatty/modules; Windows %APPDATA%\chatty\modules. Last-resort fallback: .chatty/modules
gateway_portu168420Local protocol gateway
default_endpoint_budgetusize1Delegated workers the broker runs at once against one model endpoint when nothing more specific is known (ADR-0011 C6, AGE-305). Ollama’s OLLAMA_NUM_PARALLEL / a provider’s num_parallel beat it
endpoint_budgetsHashMap<String, usize>{}Per-endpoint overrides keyed by the server’s base URL (http://localhost:11434); beats the default and what the provider reports. Omitted when empty
virtual_agents[string][]The broker’s named workers (ADR-0011 C10, AGE-377), by agent spec name (AGE-614): each is <name>.toml in .chatty/agents/, the data directory’s chatty/agents/, or the presets. Agent objects (the old VirtualAgentConfig shape) fail to load. Empty = local-agent and every exposed spec (PL-U5). A --team run replaces this roster for that run only. Field table: Agents and specs
teamTeamConfig{}What the team shares: verification — a shell command run in each worker’s worktree at task end, whose exit code and tail go into the evidence envelope (ADR-0011 C12, AGE-406); isolate — each worker of a roster no team claims gets a git worktree of its own (default false, AGE-822). Omitted when empty

providers.json — [ProviderConfig]

Source: settings/models/providers_store.rs. See also Provider matrix.

FieldTypeDefaultNotes
nameStringrequiredDisplay name
provider_typeProviderTyperequiredJSON: open_router, ollama, azure_openai. Serde rename_all = "snake_case" on the variant OpenRouter yields open_router — the string openrouter does not deserialize. Legacy aliases open_ai, open_a_i, anthropic, gemini, mistral deserialize as OpenRouter
api_keyOption<String>omitted if nullSecret. Never expose to the LLM (store: api_key_ref → provider/<name>)
base_urlOption<String>omitted if nullOllama / Azure / OpenAI-compat
extra_configMap<String, String>{} (omitted if empty)Azure: auth_method = api_key (default) or entra_id

configured_providers(): Ollama always; Azure needs non-empty base_url plus API key or Entra ID; others need a non-empty API key.


models.json — [ModelConfig]

Source: settings/models/models_store.rs.

FieldTypeDefaultNotes
idStringrequiredInternal UUID (not the API model name)
nameStringrequiredUI label
provider_typeProviderTyperequiredSame enum as providers (open_router / ollama / azure_openai)
model_identifierStringrequiredAPI / Ollama model id
temperaturef321.0
preambleString""System prompt / GEPA target
max_tokensOption<i32>omitted if null
top_pOption<f32>omitted if null
extra_paramsMap<String, String>{} (omitted if empty)Azure: api_version (default 2025-03-01-preview)
cost_per_million_input_tokensOption<f64>omitted if nullUSD
cost_per_million_output_tokensOption<f64>omitted if nullUSD
cost_per_million_cache_read_tokensOption<f64>omitted if nullUSD; falls back to the input rate when unset
cost_per_million_cache_write_tokensOption<f64>omitted if nullUSD; falls back to the input rate when unset
supports_imagesboolfalseNew models inherit ProviderType::default_capabilities()
supports_pdfboolfalse
supports_temperaturebooltrueOff for some reasoning models
max_context_windowOption<i32>omitted if nullToken-bar budget
sourceModelSource"User"User (added by hand or from a catalogue pick) or Sync (owned by a provider sync)
is_favoriteboolfalseStarred rows sort to the top of the roster
is_defaultboolfalseModel new conversations start with

mcp_servers.json — [McpServerConfig]

Source: settings/models/mcp_store.rs. HTTP MCP endpoints only (the app does not spawn stdio servers). Writes are serialized with MCP_WRITE_LOCK.

FieldTypeDefaultNotes
nameStringrequiredUnique id
urlStringrequirede.g. http://localhost:3000/mcp
api_keyOption<String>omitted if nullBearer token. Secret. LLM-facing copies must not include the real value (store: api_key_ref → mcp/<name>)
enabledbooltrue
is_moduleboolfalse (omitted when false)Auto-registered WASM gateway entry

Runtime McpAuthStatus is not persisted.

MASKED_API_KEY_SENTINEL is "****": on edit, that string means “keep the stored key”. There is no env map on McpServerConfig in current source.


a2a_agents.json — [A2aAgentConfig]

Source: settings/models/a2a_store.rs.

FieldTypeDefaultNotes
nameStringrequiredAlso the /agent <name> token
urlStringrequiredA2A base URL
api_keyOption<String>omitted if nullBearer token. Secret (store: api_key_ref → a2a/<name>)
enabledbooltrue
skills[String][] (omitted if empty)Cached from the remote agent card

Runtime A2aAgentStatus is not persisted.


mcp_oauth_<sanitized>.json — OAuth credentials

One file per MCP server name in the config dir. Non-alphanumeric characters in the server name (except - _) become _. Example: server my server/with:special.chars → mcp_oauth_my_server_with_special_chars.json.

The live writer is FileCredentialStore (mcp_token_store.rs), used by McpService. JsonOAuthCredentialRepository uses the same filenames but is not registered in init_repositories(). FileCredentialStore writes the JSON directly (not the temp-file rename used by settings repos).

Shape is opaque rmcp::StoredCredentials JSON (client_id, token_response, granted_scopes, …). Do not paste real token files into issues, PRs, or docs. Corrupt files are deleted on load.


Not persisted as JSON (yet)

StoreTypical pathNotes
TokenTrackingSettingsin-memory GPUI globalDefaults: enabled true, response_reserve 4096, high_threshold 0.70, critical_threshold 0.90, auto_summarize false, summarization_model_id omitted. Source comment: JSON persistence is a follow-up.
Conversationsconfig dir chatty/conversations.dbSQLite, not settings JSON
Provider keys, MCP/A2A tokens, Hive tokensOS keychain, service chatty:<config dir>; fallback config dir chatty/secrets.jsonReferenced from the JSON by <field>_ref (AGE-741)
Agent memory / skillsdata dir chatty/memvid + skill files
WASM module binariesdata dir chatty/modules/See module_dir
Pdfium native libdata dir chatty/lib/See env-vars
Math SVG cachedata dir chatty/math_cache/

Planned research settings (FlowSettingsModel, playbook store) are not implemented — see settings integration map.

openrouter_curated.json also lives in the config dir (GPUI OpenRouter catalog override: [{id, name}, …]). It is not a settings repository.