--- title: "Server Configuration" description: "Server-owned settings.toml sections, CLI overrides, and environment variables" --- ## Config file `fabro server start` reads `~/.fabro/settings.toml` by default. This is the same file schema used by the CLI. On a same-machine setup, the CLI and server share one `settings.toml`. On a remote deployment, the server machine has its own `settings.toml`, and the client machine keeps a separate local `settings.toml` for CLI-only values such as `[cli.target]`. Fabro only reads `settings.toml`. Older `server.toml`, `user.toml`, and `cli.toml` filenames are no longer part of the supported config surface. ### Which sections are server-owned | Scope | Examples | |---|---| | Server-owned (runtime-only from local `settings.toml`) | `[server.listen]`, `[server.api]`, `[server.web]`, `[server.auth]`, `[server.storage]`, `[server.artifacts]`, `[server.slatedb]`, `[server.scheduler]`, `[server.logging]`, `[server.integrations]`, `[features]` | | Shared run defaults (layered through `.fabro/project.toml`/`workflow.toml`) | `[run.model]`, `[run.prepare]`, `[run.sandbox]`, `[run.checkpoint]`, `[run.inputs]`, `[run.pull_request]`, `[run.git]`, `[run.hooks]`, `[run.agent]` | The CLI-only `[cli.*]` sections (including `[cli.target]`) belong in the client machine's `settings.toml`. They tell CLI commands how to reach a server. The server process does not read `[cli.*]` for its own binding or routing. Fabro does not terminate inbound TLS directly. Bind `[server.listen]` to a Unix socket or plain TCP port, and terminate HTTPS or mTLS at a reverse proxy, load balancer, or platform ingress in front of Fabro. Use `[server.api].url` and `[server.web].url` for those external HTTPS URLs. ### Full reference ```toml title="settings.toml" _version = 1 [server.listen] type = "tcp" address = "0.0.0.0:3000" [server.api] url = "https://fabro.example.com/api/v1" [server.web] enabled = true url = "https://fabro.example.com" [server.auth] methods = ["dev-token", "github"] [server.auth.github] allowed_usernames = ["alice", "bob"] [server.integrations.github] app_id = "123456" client_id = "Iv1.abc123" [server.integrations.github.webhooks] strategy = "tailscale_funnel" [server.storage] root = "/var/lib/fabro" [server.artifacts] provider = "s3" prefix = "artifacts" [server.artifacts.s3] bucket = "my-fabro-data" region = "us-east-1" [server.slatedb] provider = "s3" prefix = "slatedb" disk_cache = true [server.slatedb.s3] bucket = "my-fabro-data" region = "us-east-1" [server.scheduler] max_concurrent_runs = 8 [server.logging] level = "info" # Run defaults — applied to every run unless overridden by workflow/project config [run.model] name = "claude-sonnet-4-5" provider = "anthropic" fallbacks = ["gemini", "openai"] [[run.prepare.steps]] script = "npm install" [run.sandbox] provider = "daytona" [run.sandbox.daytona] auto_stop_interval = 60 [run.sandbox.daytona.labels] team = "platform" [run.checkpoint] exclude_globs = ["**/node_modules/**", "**/.cache/**"] [run.inputs] default_branch = "main" [run.git.author] name = "fabro-bot" email = "fabro-bot@company.com" [features] session_sandboxes = true ``` ### CLI overrides Several `settings.toml` settings can be overridden via `fabro server start` flags: | Flag | Default | Description | |---|---|---| | `--bind` | Resolved `[server.listen]`, falling back to `~/.fabro/fabro.sock` when `[server.listen]` is absent | Address to bind: `IP` or `IP:port` for TCP, or a path for Unix socket | | `--web` | enabled | Enable the embedded web UI, browser auth routes, and web-only helper endpoints | | `--no-web` | disabled | Disable the embedded web UI, browser auth routes, and web-only helper endpoints | | `--foreground` | — | Run in the foreground instead of daemonizing | | `--model` | — | Override default LLM model | | `--provider` | — | Override default LLM provider | | `--sandbox` | — | Override default sandbox provider | | `--max-concurrent-runs` | `5` | Maximum concurrent run executions | | `--config` | `~/.fabro/settings.toml` | Path to server config file | CLI flags take precedence over `settings.toml` values. See [Run Configuration — Precedence](/execution/run-configuration#precedence) for the full resolution order. ### `[server.web]` section Control the embedded SPA and browser-oriented routes. | Key | Description | Default | |---|---|---| | `enabled` | Serve the embedded SPA, `/auth/*`, and the web-only helper endpoints under `/api/v1` | `true` | | `url` | Required single canonical origin for the server. The browser UI and `/auth/*` routes use this origin, and it must be an absolute `http(s)` URL. | `http://localhost:3000` | When `enabled = false`, the server still exposes the machine API and `/health`, but `/`, `/auth/*`, SPA client routes, `/api/v1/auth/me`, `/api/v1/setup/*`, and `/api/v1/demo/toggle` all return `404`. `server.web.url` is not a secondary web host. Fabro supports a single public origin for API and web traffic. In local development that can be plain HTTP such as `http://localhost:3000` or `http://127.0.0.1:3000`. In production, operators are responsible for terminating HTTPS upstream. ### `[server.auth]` section Configure how users authenticate with the server. | Key | Description | Default | |---|---|---| | `methods` | Ordered list of enabled bootstrap methods: `dev-token`, `github` | `["dev-token"]` | When `"dev-token"` is enabled, the API accepts `Authorization: Bearer fabro_dev_...` and the login page can authenticate with the dev token directly. When `"github"` is enabled, browser users can sign in with GitHub OAuth and receive a session cookie. ### `[server.auth.github]` section GitHub-specific auth policy. | Key | Description | |---|---| | `allowed_usernames` | GitHub usernames allowed to complete OAuth login | The GitHub OAuth client ID still lives under `[server.integrations.github].client_id`. ### `[server.slatedb]` section Configure the embedded SlateDB key-value store used for run event storage. | Key | Description | Default | |---|---|---| | `provider` | Object store backend: `local` or `s3` | `"local"` | | `prefix` | Key prefix within the object store | `""` | | `flush_interval` | How often to flush the write-ahead log | `"1ms"` | | `disk_cache` | Enable a local disk cache for object store reads | `false` | When `disk_cache = true`, Fabro creates a cache directory at `/cache/slatedb` and configures SlateDB to cache object store bytes on local disk (16 GB max, 4 MB parts). This significantly reduces read latency and costs for S3-backed deployments. A warning is emitted if enabled with `provider = "local"` since the disk cache adds overhead when the object store is already local. ```toml title="settings.toml" [server.slatedb] provider = "s3" disk_cache = true [server.slatedb.s3] bucket = "{{ env.SLATEDB_BUCKET }}" region = "us-east-1" ``` The browser install wizard's `Object store` step manages both `[server.slatedb]` and `[server.artifacts]` together. `Local disk` uses the detected local object-store root, defaulting to `/objects`, with fixed prefixes `slatedb` and `artifacts`. `AWS S3` writes one shared bucket with the same fixed prefixes. ```toml title="Local disk object store" [server.artifacts] provider = "local" prefix = "artifacts" [server.artifacts.local] root = "/var/lib/fabro/objects" [server.slatedb] provider = "local" prefix = "slatedb" [server.slatedb.local] root = "/var/lib/fabro/objects" ``` The wizard only covers AWS S3 bucket/region plus one of: - runtime credentials already supplied by the deployment environment - manually-entered `AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY` Advanced S3-compatible settings such as custom `endpoint` or `path_style` remain a manual configuration path. If you need MinIO, R2, or another S3-compatible backend, configure `[server.slatedb]` and `[server.artifacts]` directly in `settings.toml`. The runtime still honors those hand-edited values even though the browser wizard does not manage them. ### Run defaults The `[run.*]` sections in `settings.toml` act as defaults for every run. On a same-machine setup, `settings.toml` is the shared machine-default layer under `workflow.toml` and `.fabro/project.toml`. On a remote setup, the client bundles workflow, project, and user config into the run manifest. The server then layers those bundled client configs over its own local defaults for run-shaped fields. Server-owned values like `[server.storage]`, `[server.api]`, `[server.web]`, `[features]`, and `[server.scheduler]` always come from the server machine's own `settings.toml` or `fabro server start` flags. Merge rules follow the normative matrix: `[run.inputs]` replaces wholesale, `[run.sandbox.env]` and `[run.sandbox.daytona.labels]` merge by key, `[run.prepare.steps]` replaces whole-list, and `[[run.hooks]]` merge by optional `id`. Most other fields use "higher-precedence wins" field-wise merging. ### `[server.logging]` section Configure the server log level and destination. | Key | Description | Default | |---|---|---| | `level` | Log level: `error`, `warn`, `info`, `debug`, `trace` | `"info"` | | `destination` | Where server logs are written: `file` (rotated daily under `/logs/`) or `stdout` | `"file"` | Level precedence: `FABRO_LOG` env var > `--debug` flag > `[server.logging].level` > `"info"`. Destination precedence: `FABRO_LOG_DESTINATION` env var > `[server.logging].destination` > `"file"`. `stdout` is incompatible with daemon mode — use `fabro server start --foreground` (which is what container images do). The CLI has its own `[cli.logging]` section. ### `[run.git.author]` section Customize the git author identity used for checkpoint commits. When not set, defaults to `fabro` / `fabro@local`. | Key | Description | Default | |---|---|---| | `name` | Git author name | `"fabro"` | | `email` | Git author email | `"fabro@local"` | ### `[server.integrations.github]` section Configure GitHub integration auth. `strategy = "token"` is the default and uses a stored `GITHUB_TOKEN` from the vault (with `GH_TOKEN` as a fallback). `strategy = "app"` enables the GitHub App flow and browser OAuth; webhook delivery is configured separately under `[server.integrations.github.webhooks]`. ```toml title="settings.toml" [server.integrations.github] strategy = "token" ``` For GitHub App mode, set `strategy = "app"` and include `app_id`, `client_id`, and `slug`. Webhook delivery is configured under `[server.integrations.github.webhooks]`: ```toml title="settings.toml" [server.integrations.github] strategy = "app" app_id = "123456" client_id = "Iv1.abc123" slug = "fabro-app" [server.api] url = "https://fabro-api.example.com" [server.integrations.github.webhooks] strategy = "server_url" ``` Fabro always serves the GitHub webhook handler at `POST /api/v1/webhooks/github` when `GITHUB_APP_WEBHOOK_SECRET` is configured. The `strategy` field controls how Fabro exposes that route and whether it mutates the GitHub App webhook URL on startup: - `strategy = "server_url"`: recommended for production or any deployment with a stable public API URL. Fabro sets the GitHub App webhook URL to `/api/v1/webhooks/github` on startup. Requires `server.api.url` and `GITHUB_APP_WEBHOOK_SECRET`. - `strategy = "tailscale_funnel"`: opt-in for Tailscale-hosted machines without a stable public URL. Fabro runs `tailscale funnel `, exposes the main server on that Funnel URL, and best-effort updates the GitHub App webhook URL to `/api/v1/webhooks/github`. Requires a TCP listener and `GITHUB_APP_WEBHOOK_SECRET`. - `strategy` unset: Fabro still accepts signed webhook deliveries on `/api/v1/webhooks/github` when the secret is present, but it does not run `tailscale funnel` and does not update the GitHub App webhook URL. Incoming webhooks are authenticated only by GitHub's `X-Hub-Signature-256` HMAC signature, not by Fabro's bearer/session auth. ### `[run.checkpoint]` section Configure checkpoint behavior for all runs. | Key | Description | |---|---| | `exclude_globs` | Glob patterns for files to exclude from checkpoint commits (for example, `["**/node_modules/**"]`) | `exclude_globs` replaces across layers — the highest-precedence layer wins wholesale. See [Run Configuration — Checkpoint](/execution/run-configuration#runcheckpoint) for per-run configuration. ### `[features]` section Toggle experimental or opt-in features. All features default to `false`. | Key | Description | |---|---| | `session_sandboxes` | Enable session sandboxes in the web UI | The same `[features]` section can be set in `.fabro/project.toml` (project-level) to enable features per-project. ## Secrets and environment variables Fabro splits secrets into two scopes: - Server runtime secrets live in `/server.env` and resolve with precedence `process env -> server.env`. - Workflow-visible secrets live in `/vaults/default/secrets.json` (the vault). Anything stored in the vault may be used by workflows. For the auth model above, the main server runtime secrets are: - `SESSION_SECRET` when the web UI is enabled - `FABRO_DEV_TOKEN` when `"dev-token"` auth is enabled - `GITHUB_APP_CLIENT_SECRET` when `"github"` auth is enabled - `AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY` when the install wizard or a manual config uses static S3 object-store credentials Fabro no longer auto-loads `.env` files. Provider API keys are required for the models you want to use; everything else is optional. ### LLM provider keys Fabro's built-in provider access resolves these from `process env -> vault`. | Variable | Provider | |---|---| | `ANTHROPIC_API_KEY` | Anthropic (Claude) | | `OPENAI_API_KEY` | OpenAI (GPT) | | `GEMINI_API_KEY` or `GOOGLE_API_KEY` | Google (Gemini) | | `KIMI_API_KEY` | Kimi | | `ZAI_API_KEY` | Zai (GLM) | | `MINIMAX_API_KEY` | Minimax | | `INCEPTION_API_KEY` | Inception (Mercury) | ### Sandbox and tools | Variable | Description | |---|---| | `DAYTONA_API_KEY` | Daytona cloud sandbox API key | | `BRAVE_SEARCH_API_KEY` | Brave Search API key (for the `web_search` tool) | ### Server authentication Fabro resolves these from `process env -> server.env`. | Variable | Description | |---|---| | `SESSION_SECRET` | Session encryption secret (64-character hex string) | ### Object store runtime secrets (optional) Fabro resolves these from `process env -> server.env`. | Variable | Description | |---|---| | `AWS_ACCESS_KEY_ID` | Static AWS access key ID for S3-backed `[server.slatedb]` / `[server.artifacts]` | | `AWS_SECRET_ACCESS_KEY` | Matching static AWS secret access key | The browser install wizard can write these into `server.env` for the AWS S3 manual-credential path. It does not support manual STS/session-token input; use runtime credentials instead for ECS, EC2 instance profiles, IRSA, or web-identity flows. For the narrowest production policy, scope access to one bucket and the `slatedb/` and `artifacts/` prefixes with `s3:ListBucket` plus `s3:GetObject`, `s3:PutObject`, and `s3:DeleteObject`. Prefer a dedicated IAM user or role for Fabro instead of reusing broad AWS credentials. If `POST /install/finish` fails after mutating `server.env`, the error response may include `leftover_env_keys` or `removed_env_keys`. For AWS object-store keys, treat `removed_env_keys` as informational: the prior managed key path was already cleared, so complete the retry before restart. If `leftover_env_keys` includes `AWS_ACCESS_KEY_ID` or `AWS_SECRET_ACCESS_KEY`, retry the install or remove the managed object-store lines from `server.env` before abandoning the host. Rotation is not required solely because finish failed after an atomic `0600` rewrite, but it remains the fallback if you no longer trust the host boundary. ### GitHub integration (optional) | Variable | Description | |---|---| | `GITHUB_TOKEN` | GitHub personal access token, stored by `fabro install` when `strategy = "token"`. Also accepts `GH_TOKEN` as a fallback. | ### GitHub App extras (optional) Fabro resolves these from `process env -> server.env`. | Variable | Description | |---|---| | `GITHUB_APP_CLIENT_SECRET` | GitHub App client secret | | `GITHUB_APP_WEBHOOK_SECRET` | GitHub App webhook secret | | `GITHUB_APP_PRIVATE_KEY` | GitHub App private key (base64-encoded) | ### Slack integration (optional) | Variable | Description | |---|---| | `FABRO_SLACK_APP_TOKEN` | Slack App-level token | | `FABRO_SLACK_BOT_TOKEN` | Slack Bot token | ### Logging | Variable | Default | Description | |---|---|---| | `FABRO_LOG` | `info` | Log level: `error`, `warn`, `info`, `debug` | | `FABRO_LOG_DESTINATION` | `file` | Server log destination: `file` or `stdout` (containers default to `stdout`) |