Docs/Operations/Configuration reference

Configuration reference

Complete reference for Fermix configuration: the config.toml file, its sections and keys, three-layer precedence, and all environment variable overrides.

All operator-editable configuration lives in ~/.fermix/config.toml (or $FERMIX_HOME/config.toml if you override the home directory). The setup wizard writes this file; the runtime reads it on startup through FermixCore.Config and provider-specific config modules. You can edit it by hand or re-run fermix setup --reconfigure to regenerate specific sections.

The macOS app and browser setup save through the daemon’s shared settings service. If you edit the file while Fermix is running, the next settings save is refused and names the changed sections. Use Reload settings from disk before saving again. Fix any parse error first; an unreadable file cannot be reloaded. Reloading imports the settings, while a restart applies changes that require one; health reports managed sections in restart_reasons and uses settings_file for an external edit.

Configuration precedence

Fermix resolves configuration in three layers, each overriding the one before it:

  1. Compile-time defaults (baked into the release in config/config.exs)
  2. Persisted setup snapshot from config.toml
  3. Environment variable overrides applied at runtime (config/runtime.exs)

Environment variables win over everything. This means a value set in config.toml can be silently superseded by an env var of the same semantic, so check both when debugging unexpected behavior.

FERMIX_HOME

FERMIX_HOME defaults to ~/.fermix. The setup wizard creates the following layout under it:

~/.fermix/
  config.toml          # main configuration file
  auth.json            # OAuth token store (all providers; mode 0600)
  memory.db            # durable memory (SQLite)
  daemon.sock          # socket the CLI uses to talk to the daemon (the background Fermix service)
  realtime.sock        # macOS app voice socket
  bootstrap/           # prompt files (main/{IDENTITY,FERMIX,SOUL,REALTIME}.md)
  memory/              # profile files rebuilt by background memory review
  skills/              # user-installed skills
  plugins/             # installed plugin store
  browser/             # browser profile
  journals/
  realtime/
  traces/              # JSONL trace files
  logs/                # rotating daemon log
  workspace/           # default sandbox workspace root
  grants/              # sandbox grant records

The home directory itself is mode 0700, readable only by you: it holds memory.db with full conversation text, config.toml, and every trace file. The daemon sets that mode on each boot and each setup save, and logs the failure rather than refusing to start if it cannot. fermix doctor’s home perms check fails when the mode is anything else and prints the chmod 700 that fixes it.

Set FERMIX_HOME to a different path to run a second Fermix instance (for example, a dev install alongside a production one). Each instance must have its own config.toml and, if using Telegram, its own bot token. See auth and secrets for keychain namespacing when running multiple instances.

[fermix_core]

Top-level core settings.

Key Default Notes
profile "general" Keychain namespace prefix. Set before running fermix setup on a non-default install. Changing it after setup orphans existing keychain entries.

[fermix_core.agent]

Core agent identity settings.

Key Default Notes
name "fermix" Public-facing agent name used in prompts and channel replies.

The primary provider is selected by setting primary = true in the desired [fermix_core.providers.<name>] block, not via a key here. The legacy provider key in this section is read-only migration input (used only if no primary flag exists anywhere); setup no longer writes it.

[fermix_core.providers.<name>]

One sub-table per provider. Valid names: openai, openai_codex, anthropic, xai, openrouter, mistral, ollama.

Key Notes
primary true/false. Exactly one provider block must have primary = true; that provider handles main turns. All other configured providers become ordered fallbacks.
api_key Plaintext API key or the @keyring sentinel (reads from the OS keychain). After running fermix setup, secrets are normally in the keychain and this key is absent or set to @keyring.
default_model Model identifier. See providers and models for the full catalog.
reasoning_effort How much internal thinking the model does before answering (higher means more thorough but slower and pricier). One of "none", "low", "medium", "high", "xhigh", "max". Applies only to effort-capable providers (OpenAI, OpenAI Codex, Anthropic, xAI). OpenRouter, Mistral, and Ollama blocks reject this key at config load. Anthropic has no "none" (floor is "low").
base_url Override the default API base URL. Useful for proxies or compatible endpoints. Not accepted in the openai block (writing it there fails at config load).
auth_mode "api_key" or "oauth". Valid only in the anthropic and xai blocks. openai is always API-key; openai_codex is always OAuth — neither block accepts this key.
fast openai_codex only. true/false. When true, Codex requests run on the priority service tier.

Each provider block only accepts the keys that apply to it. An unknown or misplaced key (for example reasoning_effort in an openrouter, mistral, or ollama block) is rejected at config load and the daemon refuses to boot until it is removed. See providers and models for the full per-provider key list.

Example:

[fermix_core.providers.openai]
api_key = "@keyring"
default_model = "gpt-5.5"
reasoning_effort = "medium"

[fermix_core.providers.anthropic]
api_key = "@keyring"
default_model = "claude-opus-4-8"

[fermix_core.personalization]

Feeds the agent’s identity prompt section.

Key Notes
user_name Your name, used in greetings and self-reference.
timezone IANA timezone string (e.g. "America/New_York").
communication_style Free-form style hint injected into the system prompt.

[fermix_core.compaction]

Controls automatic conversation compaction: when a conversation grows too long to fit the model’s memory, Fermix summarizes the older parts to make room. The trigger uses the provider’s reported context (prompt) token count from the turn, not a local estimate. When that count reaches threshold * model_context_window (a fraction of the maximum amount of text the model can consider at once), Fermix summarizes older messages and replaces the stored history with the summary plus recent turns.

Key Default Notes
enabled true Set to false to disable automatic compaction.
threshold 0.85 Fraction of the model context window that triggers compaction.
reasoning_effort "medium" Reasoning effort the summarization call uses, separate from the main turn’s effort.

The /compact channel command triggers immediate compaction on demand. /new clears the conversation window without affecting long-term memory, scheduled jobs, or resource revisions. See prompt and compaction for details.

[fermix_core.memory]

Tuning knobs for the background memory-review (“dreaming”) pass — how often it runs, how much it reads, and how it backs off after a failure. Most installs leave this section at defaults. See memory.

Key Default Notes
review_interval_hours 24 How often background memory review (“dreaming”) consolidates facts and rebuilds the injected USER.md/MEMORY.md profiles.
review_max_messages 40 Maximum recent messages a single review pass reads.
review_input_token_budget 4000 Token budget for the input a review pass sends to the model.
review_failure_backoff_ms 300000 How long to wait before retrying after a failed review (5 minutes).

[fermix_core.jobs]

Scheduled job runtime settings. See scheduled jobs. default_delivery_target is shared with reminders: it is a reminder’s destination too, so setting it once configures both rails.

Key Default Notes
default_delivery_mode How to deliver job results. One of "none", "origin", "channel", "local".
default_delivery_target.platform Channel platform for delivery (e.g. "telegram"). Also the destination for reminders, which accept telegram, slack, discord, signal, or whatsapp but refuse cli.
default_delivery_target.chat_id External channel conversation id (e.g. a Telegram chat id). Not a Fermix session id.
network_readiness_enabled true When enabled, a job run that fires at host-wake time first waits on a short TCP readiness probe to the primary provider before the first model call.

Delivery settings are copied into each job at creation time, and onto each reminder when its date is stored. Changing the default afterward retargets neither.

On the reminder side a destination has to resolve before the date is stored, and there are three outcomes. A configured default_delivery_target is snapshotted onto the date. With that key absent, Fermix derives your inbox from the first channel configured with an explicit owner user id — Telegram, then Signal, then WhatsApp — and storing succeeds on the first one that resolves. Storing fails loud, with setup guidance naming both remedies, only when there is neither a configured target nor an owner-configured channel to derive from; a configured target that is itself invalid (a cli platform, or no destination) fails the same way rather than falling through to derivation.

A default_delivery_mode of "none", "origin", or "local" is refused ahead of all of that: storing a date returns an invalid-delivery-mode error and no derivation is attempted, because those modes mean background delivery is off. Reminders need "channel", or an unset mode alongside a configured target.

[fermix_core.jobs]
default_delivery_mode = "channel"

[fermix_core.jobs.default_delivery_target]
platform = "telegram"
chat_id = "8217352118"

[fermix_core.routing]

Model routing overrides for sub-agents, scheduled jobs, and meeting summaries.

Key Notes
subagent_model Model slug (the short identifier for a model, e.g. gpt-5.4-mini) for sub-agent runs.
subagent_provider Provider for sub-agent runs. When omitted, a bare subagent_model runs on the primary provider (the model’s catalog owner is used only when no primary is configured) — set this explicitly to run sub-agents on a non-primary provider.
subagent_reasoning_effort Reasoning effort for sub-agent runs.
cron_model Model slug for scheduled job runs.
cron_provider Provider for scheduled job runs. When omitted, a bare cron_model runs on the primary provider (the model’s catalog owner is used only when no primary is configured) — set this explicitly to run jobs on a non-primary provider.
cron_reasoning_effort Reasoning effort for scheduled job runs.
meeting_model Model slug for meeting summaries.
meeting_provider Provider for meeting summaries. A provider or model pin selects one summary route.
meeting_reasoning_effort Reasoning effort for meeting summaries. An effort-only override preserves the default provider chain.

The model_routing_config tool reads and edits sub-agent and cron overrides. Setup exposes only the sub-agent group. Edit meeting overrides in config.toml; fermix doctor validates all three groups. With no meeting override, summaries use the primary provider and its fallbacks. The summary provider receives the transcript and participant roster, even if speech-to-text runs locally.

[fermix_core.routing]
meeting_provider = "openai"
meeting_model = "gpt-5.6-luna"
meeting_reasoning_effort = "low"

[fermix_core.browser]

Host exceptions for the browser tool. This section accepts only allowed_hosts; adding a timeout, tab limit, or any other key refuses config load.

Key Default Notes
allowed_hosts ["localhost", "127.0.0.1", "::1"] Exact host exceptions checked before internal-name and private-address blocks. Use hostnames or IP addresses, without a scheme, path, port, or wildcard. Internationalized domains must use their ASCII punycode spelling.
[fermix_core.browser]
allowed_hosts = ["dashboard.internal", "192.168.1.20"]

Localhost and loopback remain allowed. An exception permits the host for both navigation and downloads; it does not permit local-file documents or bypass TLS verification. See browser policy.

Selects the search backend for the web_search built-in tool. When this section is absent, Fermix uses keyless DuckDuckGo, so most installs need nothing here.

Key Notes
backend One of "duckduckgo" (default, keyless), "tavily", "exa", "parallel", "brave", "perplexity", "firecrawl". An unrecognized value falls back to DuckDuckGo.
tavily_api_key / exa_api_key / parallel_api_key / brave_api_key / perplexity_api_key / firecrawl_api_key API key for the matching backend. These live in this section and nowhere else: after setup the value is the @keyring sentinel and the real secret is read from the OS keychain. Names like TAVILY_API_KEY are keychain entry names, not environment overrides — exporting them has no effect.

If a configured non-DuckDuckGo backend hard-errors (bad key, no credits, transport failure), web_search degrades once to DuckDuckGo and logs a warning — it does not silently swallow the failure.

brave_api_key does double duty: it also enables the place_search built-in (tool reference), which is advertised only while this key resolves — whatever backend is selected — and whose lookups Brave bills separately from web-search calls.

Controls tool-schema deferral, which is on by default. Deferral means the model is not given the full definition of every tool up front; it looks them up as needed, saving tokens. When enabled, plugin and MCP tool schemas (the detailed input descriptions) are left out of requests to the provider (names stay listed in the prompt) and three bridge tools are registered: tool_search (keyword search over deferred schemas), tool_describe (fetch one schema on demand), and tool_call (invoke a deferred tool by name).

Key Default Notes
enabled true Set to false to send all tool schemas inline and remove the bridge tools.

[fermix_core.tools.generate_image]

Configuration for the generate_image built-in tool.

Key Notes
backend Image generation provider. One of "openai", "openai_codex", "xai", "google".
model Model identifier for the chosen backend.
size Default image size (backend-specific format).
google_api_key API key for the Google backend, stored in the OS keychain as @keyring after setup. The OpenAI and xAI backends reuse that provider’s chat key, and openai_codex needs no key at all.

The openai_codex backend runs on a connected ChatGPT/Codex login instead of an image API key — billed to that subscription. It is opt-in and fails loudly if the login is missing or not entitled; it never silently falls back to another backend. The keys in the table above are the only ones this section accepts; any other key stops the daemon from booting.

[fermix_core.transcription]

Speech-to-text settings for inbound voice notes, audio attachments, and live meeting transcription unless a meeting-specific backend is selected. An unknown key, an unknown backend, or a non-positive max_file_mb fails config load loudly.

Key Default Notes
backend "openai" Speech-to-text backend: "openai", "xai", "deepgram", or "local". Local requires the installed on-device speech binary and model.
model "gpt-4o-mini-transcribe" Shared model key. OpenAI offers gpt-4o-mini-transcribe, gpt-transcribe, gpt-4o-transcribe, and whisper-1; Deepgram offers nova-3 (default) and nova-2. Setup resets the model when switching backends. SpaceXAI and local have no model selector.
openai_api_key / xai_api_key Per-backend API-key override, set in this block. When unset, the OpenAI and xAI backends reuse that provider’s chat key — which OPENAI_API_KEY or XAI_API_KEY can supply. The xAI (SpaceXAI) speech-to-text endpoint requires an API key — a Grok-subscription OAuth token does not work for transcription.
deepgram_api_key Required for the Deepgram backend, which has no chat provider to reuse. Set it in this block.
max_file_mb 20 Audio size cap, enforced against the declared size before download when the attachment carries one, otherwise against the downloaded file.

Keys are stored in the OS keychain as @keyring after setup. Configure from browser setup’s Voice notes tab or the Mac app’s Settings → Voice. On standalone installs, use fermix setup --transcription-backend/--transcription-model/--transcription-api-key (the generic key flag stores under the selected hosted backend’s slot). fermix doctor reports the active backend and its credential or installation state.

On-device transcription

Select On this Mac in the browser setup’s Voice notes tab to install the verified fermix-stt binary and Parakeet speech model. Local transcription is supported on Apple Silicon macOS only. There is no API key or model menu, and audio stays on the machine for transcription. Selecting local by CLI or editing the file installs nothing; boot also performs no speech-model download. A missing binary or model is reported explicitly, with no switch to a cloud backend.

[fermix_core.transcription]
backend = "local"
max_file_mb = 20

Live listeners use this same engine: Deepgram, SpaceXAI, and local stream segments as speech arrives; OpenAI transcribes buffered spoken chunks in order. Voice notes continue to use the file transcription path. Meeting summaries are a separate model call controlled by meeting_* routing.

[fermix_core.computer_use]

Experimental and off by default. Computer use lets the agent drive your real desktop by taking screenshots and moving the mouse and keyboard. It is the most dangerous capability Fermix has — ambient control of your live logged-in session with no undo, and screenshots the model must treat as untrusted content — so it ships off and must be turned on deliberately. See capabilities and tools for the full safety model.

Turning it on is one operator step: set enabled = true in this section and restart the daemon. Computer use needs a small native helper alongside the daemon, but you do not install it by hand. On a real daemon boot, if computer use is enabled and the helper matching your build is missing, Fermix fetches it before the readiness gates run: the helper comes from the compux project’s own signed release and is verified against a SHA-256 checksum baked into your Fermix build, then cached under ~/.fermix/plugins/compux/. That is what keeps computer use working across an upgrade that changes the helper version. It happens on daemon boot only (not during fermix setup), is bounded to about 30 seconds, and is fail-soft — a failed fetch leaves computer use off until the next restart or the setup card.

The browser setup Plugins page (the Computer Use card), or the Mac app’s Settings → Computer, is the interactive route: it installs the helper on demand and reports install and permission status. Computer use is supported only on Apple Silicon (M-series) macOS and Linux x86_64; Intel Macs are unsupported and the install fails loudly.

The capability is offered to the model only once it is enabled and the helper binary is installed. Operating-system permissions (macOS Screen Recording and Accessibility, or X11 versus the unsupported Wayland on Linux) are checked separately by fermix doctor’s computer use check, not by this flag — Fermix probes the Accessibility grant when a session starts, and while it is missing every mutating action is refused with an error naming the System Settings fix, while screenshots keep working.

Key Default Notes
enabled false Turn the feature on. Requires a daemon restart.
display 0 Which display to capture, by index.
screenshot_after true Return a fresh screenshot after every mutating action so the model can verify the result.
max_actions 80 Per-session action budget — halts a runaway loop.
max_retained_screenshots 3 How many recent screenshots to keep in the model’s context.
courtesy "yield" Whether the agent steps aside for a present human before an action that would move the pointer or type. "off" disables it. Idle detection is macOS-only. Where it is unavailable the action proceeds, so this is a courtesy that keeps the agent from fighting you for the cursor, not a safety boundary.
courtesy_idle_ms 1000 How much input quiet counts as you being idle.

There is no mode key: computer use is host-desktop control only, so a legacy mode entry in an old config is ignored. There is also no separate posture key — the tool’s access posture (whether it can only look, must confirm before irreversible actions, or acts autonomously) is derived 1:1 from [sandbox] mode: strict looks only, standard acts but confirms irreversible actions conversationally, open acts autonomously. Computer use is operator-only, never delegated to sub-agents, and a host session starts only from an attended origin (an interactive chat, fermix ask, or the voice companion) — scheduled and cron runs cannot start it.

[fermix_core.computer_history]

Computer History records activity only from the apps you select. It is off by default and requires macOS, a compatible capture driver, and Accessibility permission. Enable it from browser setup’s Plugins → Computer History card or the Mac app’s Settings → Computer, where the app picker installs the driver and records your consent. An empty app selection cannot be enabled there.

Key Default Notes
enabled false Enable activity capture and its summarizer. Turning it off keeps existing stored activity; use /history purge to erase it.
apps [] Allowed macOS bundle IDs. The setup picker writes these from the app names you select.
sites [] Reserved host allowlist for browser navigation events. The current driver captures browser titles only, so this does not enable page text or URL capture.
summarizer "default" One summary route: the configured sub-agent provider, otherwise the primary; "local" selects on-device inference; a provider ID selects that provider. There is no vendor failover for raw activity.
remote_summaries [] Additional provider IDs permitted to receive derived activity summaries. While enabled with the default summarizer, its resolved provider is also permitted. Every remote provider in a chat’s fallback chain must be permitted before that turn can recall activity.
[fermix_core.computer_history]
enabled = false
apps = []
sites = []
summarizer = "default"
remote_summaries = []

Enabling the default summarizer allows selected raw activity to be sent to the route named on the setup card. Local capture does not by itself mean local summarization. Retention and recall budgets are fixed behavior, not configuration keys; unknown keys in this section refuse config load. See Computer History for retention, pause, purge, and provider access.

[fermix_core.harness]

Coding agents: the agent hands a repository task to the operator’s own Codex CLI or Claude Code CLI and reports the outcome back. The feature ships enabled but inert — no run can start until the owner approves coding agents once per machine on the setup Coding Agents tab (or by setting approved = true here), and until then every coding-agent tool is withheld from the model. An unknown key in this section refuses boot: config load fails loudly naming the key. Changes apply on the next daemon restart. There are no fermix setup flags and no environment-variable overrides for these keys.

Key Default Notes
enabled true The feature flag; false removes the feature entirely.
approved false One-time per-machine owner consent. Written by the setup Coding Agents tab.
cloud_enabled false Registers the two Codex cloud tools (codex_cloud_run, stop_tracking_coding_run).
default_vendor unset "codex" or "claude" only; anything else — including an empty string — fails config load. Filters which run tool is advertised.
max_active 2 Concurrent local runs; a launch past the bound is refused with a typed error.
default_timeout_minutes 30 Wall-clock cap per run; a per-run timeout_minutes caps at 240 minutes.
inactivity_minutes 10 No-activity notice threshold; advisory only, the run continues.
prompt_argv_max_kb 200 Prompts above this spill to a brief file instead of the command line.
max_event_bytes 1048576 Cap on a single vendor event (1 MiB).
max_framing_errors 20 Malformed-stream budget before the run fails; 0 means zero tolerance.
max_run_artifact_mb 64 Per-run event-spool cap; a breach truncates and the run continues.
artifact_quota_gb 5 Whole-store disk budget; a breach refuses new runs.
min_free_gb 2 Free-disk floor; 0 disables the probe.
artifact_retention_days 30 Run directories older than this are swept at boot and daily.
delivery_max_attempts 20 Result-delivery retries before dead-lettering.
delivery_max_age_hours 24 Result-delivery age before dead-lettering.
cloud_poll_seconds 120 Cloud status-poll interval.
cloud_poll_max_minutes 90 Cloud poll deadline; past it, tracking stops and the task URL is delivered.
codex_home unset Where the Codex CLI keeps its state; falls back to the daemon’s CODEX_HOME, then the vendor default.
claude_config_dir unset Mirrors CLAUDE_CONFIG_DIR; also re-keys Claude Code’s macOS Keychain entry, so set it if you log in under one.
[fermix_core.harness]
approved = true
default_vendor = "codex"

See coding agents.

[fermix_core.meetings]

The meeting notetaker: Fermix joins a Google Meet or Zoom meeting you name and delivers notes afterwards. Off by default. Tools appear once it is enabled and a lane is configured: the Google Meet sidecar and its browser are installed, or all four Zoom values resolve. Meet still needs the bot account signed in before a join can succeed. Edit these keys from browser setup’s Plugins → Meeting Notetaker → Configure panel or the Mac app’s Settings → Meetings; there are no fermix setup flags or terminal-wizard step. The block is snapshotted when a meeting is requested, so a mid-meeting edit cannot change that capture’s settings. Restart after changing configuration.

Key Default Notes
enabled false The feature flag.
bot_name "Fermix Notetaker" The name used in the default announcement. Google Meet shows the bot Google account’s own profile name in the roster instead, so name that account accordingly.
announce true Post one consent message in the meeting chat on admission, then stay silent. Google Meet only — Zoom RTMS has no chat to post into.
announce_message unset Replaces the default announcement. Blank uses the built-in line, filled with bot_name and [fermix_core.personalization] user_name.
transcription_backend unset Use a different speech-to-text backend for meetings only. Blank uses the one configured in [fermix_core.transcription].
retain_audio false Keep the raw audio alongside the transcript. Not exposed in setup.
zoom_account_id unset Zoom RTMS credential.
zoom_client_id unset Zoom RTMS credential.
zoom_client_secret unset Zoom RTMS credential. Stored in the OS keychain and referenced here as @keyring — see auth and secrets.
zoom_ws_subscription_id unset Zoom RTMS credential; the subscriptionId from the WebSocket URL Zoom shows for the event subscription.

The four Zoom keys are all-or-nothing: the lane counts as configured only when every one resolves to a real value, so a partly-filled set refuses at the join gate rather than failing inside Zoom’s handshake.

MEETINGS_ZOOM_CLIENT_SECRET can supply the Zoom secret instead of the keychain. It stays inside the daemon and is not passed to the meeting browser. The other meeting settings have no environment overrides.

[fermix_core.meetings]
enabled = true
bot_name = "Fermix Notetaker"
announce = true
retain_audio = false

[fermix_core.skill_curation]

The background pass that reads your own recent messages, spots tasks you repeat, and proposes a skill for them. It proposes only: nothing is written to disk without your approval, and archiving is reversible. One key, because the cadence, budgets, and staleness thresholds are internal constants rather than settings. An unknown key in this section refuses boot, naming the key.

Key Default Notes
enabled true false stops the pass; a pending proposal can still be denied but not approved. The setup wizard’s Personalization step asks the same yes/no. Requires memory persistence — with memory off, /skills review and /skills approve report that instead of running, while the rest of the command family still works.
[fermix_core.skill_curation]
enabled = true

[fermix_core.plugins]

Key Notes
enabled Array of plugin names that are turned on. Managed by fermix plugins enable/disable and the setup Plugins page rather than edited by hand.
dev_local Path to a local plugin checkout directory (e.g. a fermix-plugins repo’s plugins/ subdirectory). Subdirectories load as plugins without an install step. For development use only.

Per-plugin configuration lives under [fermix_core.plugins.<name>] (manifest-declared values in UPPER_SNAKE form, set with fermix plugins config set NAME KEY VALUE and injected into an MCP plugin’s process environment).

[fermix_core.plugin_secrets]

Per-plugin static api_key credentials, keyed by plugin name. fermix plugins auth set NAME prompts for the secret on the terminal without echoing it, and fermix plugins auth set NAME --stdin reads it from a pipe; passing the secret as an argument is refused, because an argument is visible in ps and lands in your shell history. Either way the secret is stored in the OS keychain and the entry here holds the @keyring sentinel rather than plaintext. A plugin that authenticates with a static key reports needs_secret until one is set. The Slack, Discord, and AgentMail plugins use this path. See auth and secrets.

[fermix_core.oauth.<provider>]

OAuth client credentials for plugin OAuth providers. Valid provider names: google, github, notion, x, slack.

Key Notes
client_id OAuth application client ID.
client_secret OAuth application client secret. Stored in the OS keychain as @keyring after setup.

Logging and traces

The daemon log (~/.fermix/logs/fermix.log, rotated) and the JSONL trace directory (~/.fermix/traces, written as YYYY-MM-DD/<type>.jsonl) are not configured through config.toml. Their paths are set by the release defaults and can be overridden only with the FERMIX_LOG_FILE and FERMIX_TRACE_DIR environment variables (see the environment variables table below). See traces and telemetry.

[fermix_core.realtime]

Local Realtime voice companion configuration. The keys below correspond to flags accepted by fermix setup --reconfigure --realtime-enabled .... Removed keys that will raise on startup: tool_policy, allow_network_tools, turn_detection, activation, max_buffer_chunks, idle_timeout_ms, max_input_audio_seconds_per_session.

Key Notes
enabled true/false. When true, the daemon opens a 0600 Unix-domain socket at ~/.fermix/realtime.sock on startup.
provider Only "openai" is supported.
model OpenAI Realtime model identifier.
voice Voice name (e.g. "marin").
reasoning_effort Reasoning effort for the voice agent, sent on the OpenAI Realtime session.update (nested under reasoning). One of "minimal", "low", "medium", "high", "xhigh". Defaults to "low". Also selectable in the web setup Realtime pane; there is no fermix setup flag for it.
max_session_minutes Per-session duration cap.
max_estimated_cost_cents_per_session Per-session estimated cost cap in cents.
persist_transcripts When true, stores final spoken text as voice_turn memory rows with source_type = "realtime". Defaults to false.
screen_share true/false. Whether a call may watch your screen continuously — started on your request, or by the agent itself when the task you bring to the call concerns your screen (it always announces the start, and “stop watching” or hanging up ends it). Defaults to true, and means nothing unless computer use is enabled and installed — the feed uses the same helper and the same macOS Screen Recording grant. Set it to false to withhold the capability, and a call is never offered it.

There is no knob for how often frames are captured, how many are kept, or what share of the call’s cost cap the feed may spend. Those are fixed internal behavior, so the only decision left to you is whether watching is allowed at all.

[fermix_core.realtime]
enabled = true
provider = "openai"
model = "gpt-realtime-2"
voice = "marin"
max_session_minutes = 15
max_estimated_cost_cents_per_session = 100
persist_transcripts = false
screen_share = true

See realtime voice.

[sandbox]

Controls the sandbox (the walled-off environment that limits which files and commands a tool can touch) applied to shell tool calls. See sandbox.

Key Default Notes
mode "standard" Sandbox strictness. One of "standard", "strict", "open".
workspace_root ~/.fermix/workspace Primary workspace root inside the sandbox.
allowed_roots [] Additional filesystem roots to allow beyond the mode defaults. Approved grants persist here — fermix grant path PATH and an owner-confirmed request_directory_access request append the granted root; fermix sandbox explain labels these roots (granted).
blocked_roots [] Roots to block regardless of mode.

Fermix’s own home directory is not a key here — it is derived from FERMIX_HOME and is always refused as a grant root. It also does not control what open mode exposes or what the credential blocklist protects; those key off the real OS home ($HOME), which is detected separately.

The shell command allow-set (bare, assistant, or extended) is configured in its own [sandbox.commands] sub-table via the profile key (default "bare"), not here. See sandbox for command profiles and per-command tool definitions.

[sandbox.env]

Controls which environment variables are visible to sandboxed processes.

Key Notes
mode "selected" exposes only explicitly listed vars; "all" passes the full environment.
allow Array of variable names to allow (used with "selected" mode).
deny Array of variable names to deny (used with "all" mode).

Sub-blocks [sandbox.env.<NAME>] declare per-key resolvers (each names a source of "env" or "command"). See sandbox for the full env source reference.

[fermix_channels.<platform>]

One sub-table per channel. Valid platform names: telegram, whatsapp, slack, discord, signal.

Key Notes
enabled true/false.
owner_user_id The operator’s external user id on this platform. Required for operator-level trust. Also serves as the ingress allowlist when no explicit allowed_user_ids is set.
allowed_user_ids / allowed_sender_ids Guest list. Users listed here receive :guest (read-only chat) trust.
command_allowlist Ids permitted to run slash commands without full ingress allowlist membership.
streaming Whether replies appear bit by bit as the model writes them, instead of all at once when finished. On by default with the mode derived from the channel: a channel that can edit its own messages (Telegram today) streams in "draft" mode when this key is unset, every other configured channel in "block" mode. "draft" streams into a live message that seals into section-shaped cards as it grows. "block" sends each completed chunk of the model’s output as its own message (works on every channel). On a channel that cannot edit, an explicit "draft" is ignored at runtime, and fermix doctor’s channel streaming check warns; the same check notes when an explicit value overrides a different derived default (telegram=block (explicit; unset derives draft)), so a hand-written key never hides a newer default silently. "off" restores the single final message. Any other value stops config load, naming the valid values. Streaming is a no-op on a provider that does not stream.

Per-platform credential keys:

Platform Keys
Telegram bot_token
WhatsApp app_secret, verify_token (webhook); access token and phone number id via env vars
Slack bot_token, signing_secret
Discord bot_token, bot_user_id
Signal account, cli_path

After a normal fermix setup, channel secrets are stored in the OS keychain and these keys hold the @keyring sentinel or are absent. See auth and secrets and ingress and trust.

The Telegram bot_token is deliberately not read from the environment: a single exported TELEGRAM_BOT_TOKEN would collide across instances, so the token lives only in each install’s config.toml (keychain-backed). Run a second instance under its own FERMIX_HOME with its own bot token.

[fermix_channels.telegram]
enabled = true
owner_user_id = "123456789"
command_allowlist = ["987654321"]

Use /whoami from the target account to discover the platform user id to place in owner_user_id or allowed_user_ids.

[fermix_channels.acp]

The surface that lets an ACP client — the Zed editor, a Buzz harness — drive this daemon over a local socket. It is a channel, but not a platform: it has no credentials, no owner id, and no allowlist, because only the user account running the daemon can open the socket.

Key Default Notes
enabled true Whether the daemon accepts ACP clients. Set false to turn the surface off; the listener reflects the change on the next daemon restart.

enabled is the only key this section accepts — anything else written here is dropped when the config is read. The socket path is not a setting: it is always <FERMIX_HOME>/acp.sock.

[fermix_channels.acp]
enabled = false

[mcp.servers.<name>]

Outbound MCP server definitions. Each named sub-table configures one MCP server that Fermix can invoke as an external tool source.

Key Notes
command Executable to run.
args Argument list.
env Static environment variables for the subprocess.
pass_env List of host env var names to forward to the subprocess.

Per-tool policy sub-blocks: [mcp.servers.<name>.tools.<tool_name>] accepts policy_class and hidden_from_agent to control sandbox policy and visibility per tool.

[mcp.inbound]

Inbound MCP surface configuration. Currently not wired into the supervision tree. See MCP.

Environment variables

Secrets are normally stored in the OS keychain after fermix setup completes. The API-key and channel-token variables below are for headless or container deployments that inject secrets through the environment. The FERMIX_* operational variables are always environment-only knobs.

Variable Required Description
OPENAI_API_KEY When the openai provider is configured, or for Realtime voice OpenAI API key. Realtime voice reuses this key. openai_codex uses OAuth, not this key.
ANTHROPIC_API_KEY When the Anthropic provider is configured Anthropic API key.
XAI_API_KEY When the xAI provider is configured xAI (Grok) API key.
OPENROUTER_API_KEY When the OpenRouter provider is configured OpenRouter API key.
MISTRAL_API_KEY When the Mistral provider is configured Mistral API key.
MEETINGS_ZOOM_CLIENT_SECRET When Zoom RTMS uses an environment-supplied secret Zoom client secret. Other Zoom values remain in [fermix_core.meetings].
OLLAMA_BASE_URL When the Ollama provider is configured Base URL of the Ollama OpenAI-compatible endpoint (default http://localhost:11434/v1).
XAI_BASE_URL No Override the xAI API base URL.
FERMIX_ANTHROPIC_AUTH_MODE No Overlay the Anthropic auth mode (api_key or oauth).
FERMIX_XAI_AUTH_MODE No Overlay the xAI auth mode (api_key or oauth).
WHATSAPP_ACCESS_TOKEN If WhatsApp is enabled WhatsApp Cloud API access token.
WHATSAPP_PHONE_NUMBER_ID If WhatsApp is enabled WhatsApp phone number id.
WHATSAPP_VERIFY_TOKEN If WhatsApp is enabled Webhook verification token.
WHATSAPP_APP_SECRET If WhatsApp is enabled Webhook HMAC signing secret.
DISCORD_BOT_TOKEN If Discord is enabled Discord bot token.
DISCORD_BOT_USER_ID If Discord is enabled Discord bot user id.
SLACK_BOT_TOKEN If Slack is enabled Slack bot token.
SLACK_SIGNING_SECRET If Slack is enabled Slack request signing secret.
SIGNAL_ACCOUNT If Signal is enabled Signal phone number or account identifier.
SIGNAL_CLI_PATH No Override the signal-cli executable path (also settable via config.toml).
FERMIX_HOME No Override the config and workspace root directory. Default: ~/.fermix.
FERMIX_TRACE_DIR No Override the trace output directory. Default: $FERMIX_HOME/traces.
FERMIX_LOG_FILE No Override the log file path. Default: $FERMIX_HOME/logs/fermix.log.
FERMIX_HTTP_BIND No Phoenix endpoint bind address. Default: 127.0.0.1 (loopback only). Set to 0.0.0.0 to accept network connections; doing so also exposes the setup surface, so place it behind a trusted network.
FERMIX_REALTIME_ENABLED No Enable the Realtime voice companion mode.
FERMIX_REALTIME_PROVIDER No Realtime provider. Only openai is supported.
FERMIX_REALTIME_MODEL No Override the OpenAI Realtime model.
FERMIX_REALTIME_VOICE No Override the Realtime voice name.
FERMIX_REALTIME_MAX_SESSION_MINUTES No Per-session voice duration cap.
FERMIX_REALTIME_MAX_COST_CENTS No Per-session estimated cost cap in cents. Short alias.
FERMIX_REALTIME_MAX_ESTIMATED_COST_CENTS_PER_SESSION No Long-form alias for FERMIX_REALTIME_MAX_COST_CENTS.
FERMIX_REALTIME_PERSIST_TRANSCRIPTS No Persist final voice transcripts as local memory text.
PORT No Phoenix HTTP port. Default: 4030.
PHX_HOST No Public hostname for generated URLs. Default: localhost.
FERMIX_TRACE_CONTENT No Force trace content (prompt/response bodies) capture on (1) or off (0), independent of Opik. On by default in normal runtime; set 0 to suppress bodies.
FERMIX_OPIK_ENABLED No Enable Opik trace export; FERMIX_TRACE_CONTENT=0 suppresses exported bodies as well as local trace bodies.
FERMIX_OPIK_BASE_URL No Opik server base URL.
FERMIX_OPIK_PROJECT No Opik project name. Default: fermix.
FERMIX_OPIK_API_KEY No Opik API key (never written into an installed service unit).

Provider selection, model, reasoning effort, and the primary flag are deliberately not environment-settable — they are owned by config.toml. Environment variables override config.toml values at runtime (layer 3 of the precedence chain). In development and test runs, credential variables default to empty strings.

Next steps