# Setup

> How fermix setup configures the daemon on first run and on subsequent reconfiguration, including the browser wizard, terminal wizard, non-interactive flags, and what gets written to FERMIX_HOME.

`fermix setup` is the one command you run to configure Fermix the first time. It installs and starts the background service (the daemon), then opens the browser setup wizard via a short-lived, single-use `/setup?t=...` URL at `http://127.0.0.1:4030` (default port `4030`; override with `--port` or the `PORT` environment variable). The browser wizard is the default only when you are running the packaged standalone binary on a host with a display. On a headless host (a machine with no graphical browser, such as a server) or a source build, setup falls back automatically to a terminal prompt sequence; `--cli` forces that path on any host, and supplying any answer flag (such as `--openai-api-key`) also runs the terminal path.

## What setup does

On first run, `fermix setup`:

1. Installs the OS service unit (the file that tells your operating system how to run Fermix in the background: launchd on macOS, systemd on Linux) and starts the daemon.
2. Opens a short-lived one-time `/setup?t=...` URL in the default browser (or runs the terminal wizard).
3. Walks through provider selection, [realtime voice](/docs/realtime-voice), channel onboarding, voice-note transcription, plugin integrations, web search and image-generation (media) backends, sandbox mode (the limits on what files and commands the agent can touch), memory tuning, and personalisation. That is the browser wizard's full set of groups; the terminal wizard prompts for a narrower set (see [answer groups](#answer-groups)).
4. Writes `config.toml` and seeds the prompt files and workspace directories under `FERMIX_HOME`.

Running it again with no arguments is safe and repeatable: the wizard checks what is already answered and only prompts for missing values. It also restores any missing prompt files.

## Flags

| Flag | Description |
|------|-------------|
| `--web` | Force the browser wizard; cannot be combined with `--cli`, `--terminal`, or `--no-service`. On a headless host it prints an SSH port-forward hint instead of opening a browser |
| `--cli` / `--terminal` | Force the terminal wizard even on a graphical host |
| `--no-service` | Configure without installing or starting the OS service unit |
| `--no-browser` | Print the setup URL instead of opening it |
| `--port N` | Port for the web setup page (default `4030`) |
| `--user` / `--system` | Service scope: per-user (the default) or system-wide |
| `--rotate-token` | Rotate the durable setup token before minting the launch URL, invalidating any previously issued token |
| `--print-state` | Print the current readiness report and exit without prompting |
| `--reconfigure` | Re-run the core prompts even if already answered: provider, model, reasoning effort (how hard the model thinks before answering — asked only for providers that support it), the Codex `fast` toggle, the realtime enable toggle and its follow-up questions, and the owner user ID of every enabled channel |
| `--migrate-secrets` | Move any plaintext secrets remaining in `config.toml` into the OS keyring (your operating system's secure credential store) |
| `--import-codex` | Import OpenAI OAuth tokens from an existing Codex CLI install (`~/.codex`); an interactive setup also offers this automatically when Codex credentials are found and no provider is configured yet |
| `--skip-probe` | Skip the live provider check that a completed setup normally ends with |
| `--provider NAME` | Choose the primary provider non-interactively (`openai_codex`, `openai`, `anthropic`, `xai`, `openrouter`, `mistral`, `ollama`) |
| `--default-model MODEL` | Set the default model non-interactively |
| `--reasoning-effort LEVEL` | Set reasoning effort non-interactively (`none`, `low`, `medium`, `high`, `xhigh`, `max`) |
| `--openai-api-key KEY` | Pass the OpenAI API key non-interactively |
| `--telegram-bot-token TOKEN` | Pass the Telegram bot token non-interactively |

Every provider secret (`--anthropic-api-key`, `--xai-api-key`, `--openrouter-api-key`, `--mistral-api-key`, `--ollama-base-url`) and the per-channel and realtime answers follow the same `--<key> <value>` pattern. Use these for scripted or container installs where the interactive wizard cannot run; any answer flag switches setup to the terminal path.

## What gets written

Setup writes to `FERMIX_HOME` (default `~/.fermix`). Override the location with the `FERMIX_HOME` environment variable before running setup.

| Path | Purpose |
|------|---------|
| `$FERMIX_HOME/config.toml` | Typed snapshot of all wizard answers |
| `$FERMIX_HOME/auth.json` | OAuth token store for all providers and plugins |
| `$FERMIX_HOME/setup-token` | Durable local secret protecting the web setup page (never appears in URLs) |
| `$FERMIX_HOME/memory.db` | Durable memory database |
| `$FERMIX_HOME/daemon.sock` | Control socket for the CLI |
| `$FERMIX_HOME/realtime.sock` | Realtime voice socket |
| `$FERMIX_HOME/workspace/` | Default sandbox workspace root |
| `$FERMIX_HOME/bootstrap/` | Seeded prompt files (`IDENTITY`, `FERMIX`, `SOUL`, `REALTIME`) |
| `$FERMIX_HOME/memory/` | Seeded memory files (`USER.md`, `MEMORY.md`) |
| `$FERMIX_HOME/skills/` | Local skill directory |
| `$FERMIX_HOME/plugins/` | Installed plugin store |
| `$FERMIX_HOME/browser/` | Browser profile for the browser tool |
| `$FERMIX_HOME/journals/` | Journal storage |
| `$FERMIX_HOME/realtime/` | Realtime voice session data |
| `$FERMIX_HOME/traces/` | Structured JSONL trace output |
| `$FERMIX_HOME/logs/` | Rotating daemon log |
| `$FERMIX_HOME/grants/` | Persisted sandbox path grants |

## Answer groups

The wizard collects answers across these groups. All answers are persisted to `config.toml`; secrets are handed off to the OS keyring (see [auth and secrets](/docs/auth-and-secrets)).

The browser wizard has a tab for every group. The terminal wizard prompts only for Provider, Realtime, Channels, Compaction, Memory, and Personalisation. It can also set Transcription and Media, but only from answer flags (`--transcription-backend`, `--transcription-model`, `--transcription-api-key`, `--image-backend`, `--image-model`, `--google-api-key`); there are no web-search, plugin, or sandbox flags, so those three groups are configured from the browser wizard or by editing `config.toml`.

| Group | Keys |
|-------|------|
| Provider | Provider selection, `default_model`, `reasoning_effort`, and the provider's `api_key` (e.g. `openai_api_key`). The first provider you configure becomes the primary automatically; after that, saving another provider's settings adds it as a fallback — promoting it is a separate, explicit "Set primary" action (or the `--provider` answer) |
| Realtime | `realtime_enabled`, `realtime_api_key`, `realtime_voice`, `realtime_max_session_minutes`, `realtime_max_cost_cents`, `realtime_persist_transcripts`, plus two answers only the web wizard sets (they have no CLI flag): `realtime_model` (`gpt-realtime-2.1-mini`, `gpt-realtime-2.1`, `gpt-realtime-2`) and `realtime_reasoning_effort` (`minimal`, `low`, `medium`, `high`, `xhigh` — the realtime vocabulary has no `none` or `max`; default `low`) |
| Channels | Per-channel `bot_token`, `signing_secret`, `owner_user_id`, `app_secret`, and related fields |
| Transcription | `transcription_backend` (`openai`/`xai`/`deepgram`), `transcription_model` (the model list is per-backend; switching backend snaps the model to that backend's default — the SpaceXAI (`xai`) backend is modelless and carries no model), and `transcription_api_key` (stored under the selected backend's key slot). OpenAI and SpaceXAI reuse the chat-provider key when no transcription key is set, but only where that provider is itself configured with an API key — a SpaceXAI Grok subscription/OAuth token is not accepted for speech-to-text; Deepgram always needs its own key. Also settable non-interactively with `--transcription-backend`, `--transcription-model`, and `--transcription-api-key` |
| Plugins | Integration install and auth (OAuth connect or static `api_key`); managed from the web Plugins page or the `fermix plugins` CLI rather than typed answer keys. The Plugins page also hosts the Computer Use card (experimental, off by default; see [tool reference](/docs/tool-reference)) |
| Search | `web_search_backend` plus the chosen backend's key (`brave_api_key`, `exa_api_key`, `tavily_api_key`, `parallel_api_key`, `perplexity_api_key`, `firecrawl_api_key`); the default DuckDuckGo backend needs no key |
| Media | `image_backend`, `image_model`, and the image API key (`google_api_key` for the Google backend; OpenAI and xAI reuse the chat provider key). `image_backend` also accepts `openai_codex` ("OpenAI Codex (ChatGPT)"), which 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 |
| Sandbox | `sandbox_mode` (`strict`/`standard`/`open`), the command profile (`bare`/`assistant`/`extended`), and the environment-variable passthrough allowlist |
| Compaction (summarizing old messages to free up space when a conversation gets long) | `compaction_threshold` |
| Memory (how often background memory review runs) | `review_interval_hours` (blank keeps the 24-hour default) |
| Personalisation | `bot_name` (the assistant's own name — persists to `[fermix_core.agent].name`, the source of truth that seeds `IDENTITY.md`; blank keeps the current name `fermix`), `user_name`, `timezone`, `communication_style` |

## Config precedence

Runtime configuration resolves in this order, later entries winning:

1. Compile-time defaults (`config/config.exs`)
2. Persisted setup state (`config.toml`)
3. Environment variable overrides applied at boot (`config/runtime.exs`)

See [configuration](/docs/configuration) for the full `config.toml` reference.

## Secret storage

Secrets in `config.toml` are stored as the placeholder value `@keyring` (a marker that means "the real value lives in the keyring, not in this file"). When saving, the wizard auto-stores plaintext secrets to the OS keyring when a writer is available (macOS `security`, or the Linux Secret Service via `secret-tool`; account `fermix`, service `fermix:<ENV>`). Without a writer, unchanged plaintext is kept with a warning and only new secrets fail.

The optional `[fermix_core] profile` key (default `general`) namespaces keyring entries so two installs on the same host do not overwrite each other's secrets. Set it before running `fermix setup`: secrets are written under the profile in effect at save time, and changing the profile on a populated install orphans the old entries.

If a key is still plaintext after initial setup, the wizard prints a warning. Run `fermix setup --migrate-secrets` to move it to the keyring.

See [auth and secrets](/docs/auth-and-secrets) for the full keyring configuration.

## Boot report

After saving, setup checks whether a daemon restart is required for the changes to take effect. A provider change, for example, requires a restart because the agent reads those settings once at startup. How the restart happens depends on which wizard you used:

- **Web wizard**: when a saved change needs a restart, an "Apply & restart" button appears on the save notice and on the Doctor tab. Clicking it restarts the daemon from the page.
- **Terminal wizard**: once setup is complete, it finishes by installing (or reconciling) and restarting the OS service itself, printing `Fermix is running (user service).` — no separate restart step is needed. If service activation is skipped (`--no-service`, or a non-packaged build), it prints the manual steps instead.

To restart the daemon manually at any time, run `fermix restart`.

When setup is complete, the terminal wizard also ends with a live check against the chosen provider (`auth probe: <provider>/<model> responded in Nms`), so a bad key or rejected token surfaces immediately rather than on your first message. `--skip-probe` skips it. If a Codex OAuth token is rejected, setup offers to re-run the ChatGPT login on the spot.

## Web wizard

The wizard runs as a web form at `http://127.0.0.1:4030`. It uses the same answer keys and the same validation logic as the terminal wizard. The setup page is protected so only a local session can open it: the `fermix setup` command is the only thing that mints the short-lived, single-use `/setup?t=...` launch URL (valid for 15 minutes, consumed on first open). The status dashboard links to a bare `/setup`, which is refused without a valid token, so a visitor to the dashboard cannot open the wizard — even on a network-exposed daemon. The long-lived setup token is never placed in a URL. Use the web wizard for initial setup when a browser is available, or return to it later to update specific groups.

The wizard is organized into tabs: **Provider**, **Realtime**, **Channels**, **Transcription**, **Plugins**, **Search**, **Media**, **Sandbox**, **Memory**, **Personalization**, and **Doctor**.

The Provider tab renders one card per provider (Primary, Fallback, or Not configured). Picking a card loads that provider into the configuration form. Saving configures that provider: the first provider you configure becomes primary automatically; after that, a newly saved provider joins the fallback chain, and a configured non-primary card shows a "Set primary" button that flips the primary flag without re-entering credentials. Each provider offers an OAuth-vs-`api_key` picker where both are supported (xAI and Anthropic). A provider connected via OAuth whose stored token has gone stale (for example after long disuse) shows a "Reconnect needed" badge instead of "Connected". The primary provider's pane also carries a **Sub-agent model** dropdown — the model delegated sub-agent workers run on, defaulting to "Same as main model". It lists only the primary provider's models (sub-agents run on the primary provider unless `subagent_provider` is set in `[fermix_core.routing]`); a leftover pin from a different provider shows as "Same as main model" and self-heals on the next save. Provider changes require a daemon restart to take effect.

The Plugins tab mirrors the plugin catalog (install, enable, and connect integrations — see [plugins](/docs/plugins)) and also hosts the **Computer Use** card. Computer use is experimental and off by default: enabling it downloads the native computer-use helper (a checksum-verified binary; Apple Silicon Macs and Linux x86_64 only) and flips the feature flag, which applies on the next daemon restart.

The Doctor tab runs final checks: readiness, provider and channel probes, an "Auth tokens" line that flags stored OAuth tokens needing reconnection (`Reconnect needed: <names>`), and a **Computer use** section that probes the helper's Screen Recording and input-control permissions — on macOS, a **Grant macOS permissions** button appears when a grant is missing, raising the system prompts up front instead of on the first screenshot.

## Reconfiguring

To revisit provider and model choices after initial setup:

```bash
fermix setup --reconfigure
```

For non-interactive reconfiguration, combine `--reconfigure` with inline flags:

```bash
fermix setup --reconfigure --openai-api-key sk-...
```

The terminal wizard restarts the OS service itself when it finishes, so reconfigured values normally apply immediately. If you need to restart manually (for example after editing `config.toml` by hand), run:

```bash
fermix restart
```

## Headless and container installs

On hosts with no browser, setup detects the headless environment and runs the terminal wizard automatically. To use the web wizard from a headless host anyway (for example a server you reach over SSH), pass `--web`: setup starts the service, prints the launch URL, and shows an SSH port-forward hint (`ssh -L 4030:127.0.0.1:4030 user@host`) so you can open the page from your own machine.

To suppress interactive prompts entirely, supply all required values as flags:

```bash
fermix setup --cli --no-browser \
  --openai-api-key "$OPENAI_API_KEY" \
  --telegram-bot-token "$TELEGRAM_BOT_TOKEN"
```

For container deployments that inject secrets through environment variables, skip the wizard entirely and rely on runtime env overrides (see the environment variable table in [configuration](/docs/configuration)).

## Idempotency

`fermix setup` with no flags is safe to run at any time. It seeds only what is missing and does not overwrite answers already in `config.toml`. After a package-manager upgrade (`brew upgrade fermix`), re-running `fermix setup` reconciles the OS service unit if the new binary would write a different one, so the running daemon picks up path or template changes without a manual `fermix service install`.

## Related pages

- [Installation](/docs/installation): Homebrew and source-build instructions.
- [Configuration](/docs/configuration): Full `config.toml` key reference and environment variables.
- [Auth and secrets](/docs/auth-and-secrets): Keyring integration, `@keyring` sentinel, and secret migration.
- [Channels](/docs/channels): Per-channel token and owner configuration.
- [Providers and models](/docs/providers-and-models): Provider selection, fallback chains, and model pinning.
