Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 9 additions & 6 deletions docs/deployment-guides/config-json.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,26 @@ icon: "file-code"

---

## Two Configuration Modes
## Configuration Sources

Bifrost supports **two mutually exclusive modes**. You cannot run both at the same time.
Bifrost stores runtime configuration in a config database by default, so settings can be edited through the Web UI or API. You can also provide a `config.json` file to seed or reconcile that database at startup. To run with only `config.json`, set `config_store.enabled: false`.

| Mode | When | Behaviour |
| Setup | When | Behaviour |
|------|------|-----------|
| **Web UI / database** | No `config.json`, or `config.json` with `config_store` enabled | Full UI available, configuration stored in SQLite or PostgreSQL |
| **File-based (`config.json`)** | `config.json` present, `config_store` disabled | UI disabled, all config loaded from file at startup, restart required for changes |
| **Web UI / database** | No `config.json` | Bifrost creates a default SQLite config store and runtime changes are saved through the UI or API |
| **DB-backed `config.json`** | `config.json` exists and `config_store` is omitted or enabled | File-backed sections seed or reconcile the config store at startup; UI/API edits remain available |
| **File-only `config.json`** | `config_store.enabled` is `false` | Config is loaded from file into memory at startup; config-backed UI/API changes are unavailable and file changes require restart |

<Note>
See [Setting Up](/quickstart/gateway/setting-up#two-configuration-modes) for a full explanation of both modes and how `config_store` bootstrapping works.
By default, DB-backed `config.json` uses `source_of_truth: "split"`: unchanged file-backed rows preserve UI/API edits, while changed file-backed rows are applied on the next startup. Use `source_of_truth: "config.json"` only when explicitly present file sections should replace matching DB state. See [Source of Truth & Reconciliation](/deployment-guides/config-json/source-of-truth) for the full rules, including missing-vs-empty section behavior.
</Note>

---

## Minimal Working Example

This example uses file-only configuration for the smallest self-contained setup.

```json
{
"$schema": "https://www.getbifrost.ai/schema",
Expand Down
4 changes: 4 additions & 0 deletions docs/deployment-guides/config-json/governance.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ icon: "shield-check"

The `governance` block lets you seed all governance resources directly in `config.json`. On startup, Bifrost loads these into the configuration store. This is the recommended approach for GitOps workflows where governance state is managed as code.

<Note>
In default split mode, file-backed governance resources seed or update the DB by hash while unrelated DB-only resources are preserved. With `source_of_truth: "config.json"`, only governance sub-sections that are explicitly present in the file are authoritative. Omit a sub-section to leave DB-managed rows alone; set it to an empty array only when you intend to remove stored rows for that sub-section. See [Source of Truth & Reconciliation](/deployment-guides/config-json/source-of-truth).
</Note>

<Note>
**Governance enforcement is always active** in OSS - you do not need a plugin entry to enable it. To require a virtual key on every inference request, set `client.enforce_auth_on_inference: true`. This is the global default, but a more specific inference-auth flag such as `governance.auth_config.disable_auth_on_inference` overrides it; if no specific override is set, `client.enforce_auth_on_inference` applies.
</Note>
Expand Down
4 changes: 4 additions & 0 deletions docs/deployment-guides/config-json/plugins.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ icon: "puzzle-piece"
**Telemetry, logging, and governance are auto-loaded built-ins** - they are always active and configured via the `client` block and dedicated top-level keys, not the `plugins` array.
</Note>

<Note>
In DB-backed deployments, plugin sync depends on the reconciliation mode. Split mode preserves DB plugins unless a file plugin has a higher `version` or changed placement/order. With `source_of_truth: "config.json"`, a present `plugins` array is authoritative; `plugins: []` removes stored opt-in plugins. See [Source of Truth & Reconciliation](/deployment-guides/config-json/source-of-truth).
</Note>

---

## Auto-Loaded Built-ins
Expand Down
4 changes: 4 additions & 0 deletions docs/deployment-guides/config-json/providers.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ icon: "plug"

All providers are configured under `providers` in `config.json`. Each provider entry contains a `keys` array where every key has a `name`, `value`, `models`, and `weight`, plus optional provider-specific config objects.

<Note>
In DB-backed deployments, provider entries from `config.json` are reconciled into the config store at startup. The default `source_of_truth: "split"` mode preserves UI/API edits while matching file-backed providers are unchanged. With `source_of_truth: "config.json"`, a present `providers` section is authoritative and prunes DB-only providers or keys. See [Source of Truth & Reconciliation](/deployment-guides/config-json/source-of-truth).
</Note>

**Supplying credentials:**

Use the `env.` prefix to reference environment variables - never put API keys directly in `config.json`:
Expand Down
24 changes: 24 additions & 0 deletions docs/deployment-guides/config-json/schema-reference.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ This page is a concise reference for every top-level key in `config.json`. Click
| Key | Type | Description | Guide |
|-----|------|-------------|-------|
| `$schema` | string | Schema URL for IDE validation. Set to `"https://www.getbifrost.ai/schema"` | - |
| `version` | integer | Compatibility switch for empty allow-list arrays. Omit for current v2 semantics. | [`version`](#version) |
| `source_of_truth` | string | Startup reconciliation mode for DB-backed `config.json`: `"split"` or `"config.json"` | [Source of Truth](/deployment-guides/config-json/source-of-truth) |
| `encryption_key` | string | Optional AES-256 key (derived via Argon2id). Accepts `env.VAR` prefix and is also read from `BIFROST_ENCRYPTION_KEY`. If omitted, data is stored in plaintext. | [Client](/deployment-guides/config-json/client#encryption-key) |
| `client` | object | Worker pool, logging, CORS, auth enforcement, header filtering, MCP, compat shims | [Client](/deployment-guides/config-json/client) |
| `providers` | object | LLM provider API keys, network settings, concurrency | [Providers](/deployment-guides/config-json/providers) |
Expand Down Expand Up @@ -48,6 +50,28 @@ Omitting `version` uses v2 semantics. Set `"version": 1` only if you are migrati

---

## `source_of_truth`

Controls how `config.json` is reconciled with the config store at startup.

| Value | Behaviour |
|-------|-----------|
| `"split"` *(default)* | File-backed rows seed or update the config store by hash, while unchanged file-backed rows preserve UI/API edits |
| `"config.json"` | Explicitly present file sections are authoritative and replace matching DB state on startup |

Missing and empty sections behave differently when `source_of_truth` is `"config.json"`. A missing section leaves DB rows untouched; a present empty section is authoritative and can prune matching DB rows.

```json
{
"source_of_truth": "config.json",
"plugins": []
}
```

The example above makes the `plugins` section present and empty, so stored plugins are removed on startup. See [Source of Truth & Reconciliation](/deployment-guides/config-json/source-of-truth) for section-by-section behavior.

---

## `client`

Controls the worker pool, logging pipeline, security, and SDK shims. All fields are optional.
Expand Down
117 changes: 117 additions & 0 deletions docs/deployment-guides/config-json/source-of-truth.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
---
title: "Source of Truth & Reconciliation"
description: "How config.json, the config store, split mode, and authoritative file sync interact at startup"
icon: "shuffle"
---

Bifrost can use `config.json` in two different ways:

- As the only runtime configuration source, with `config_store.enabled: false`.
- As a declarative bootstrap and reconciliation source for a SQLite or PostgreSQL config store.

That distinction matters at startup. Bifrost uses `source_of_truth` and per-section reconciliation metadata, including `config_hash` for many config rows, to decide when file-backed configuration should preserve DB edits, update stored values, or prune DB-only rows.

<Note>
`config_hash` is auto-managed. Do not set it manually in `config.json` or API payloads.
</Note>

---

## Configuration Setups

| Setup | Config store | Web UI / API edits | Startup behavior |
|-------|--------------|--------------------|------------------|
| No `config.json` | Default SQLite `config.db` in app-dir | Enabled | Bifrost starts with defaults and stores runtime changes in SQLite |
| `config.json` with `config_store` omitted | Default SQLite `config.db` in app-dir | Enabled | File sections are reconciled into SQLite, then DB state is used at runtime |
| `config.json` with `config_store.enabled: true` | Explicit SQLite or PostgreSQL | Enabled | File sections are reconciled into the configured store, then DB state is used at runtime |
| `config.json` with `config_store.enabled: false` | Disabled | Unavailable for config-backed surfaces | File is loaded into memory at startup; changes require restart |

`source_of_truth` only affects DB-backed reconciliation. In file-only mode there is no config store to reconcile against, so `config.json` is naturally the runtime source.

---

## Default Split Mode

The default mode is:

```json
{
"source_of_truth": "split"
}
```

You can also omit `source_of_truth`; `split` is the default.

In split mode, Bifrost treats `config.json` as a bootstrap and drift-detection source:

1. On first startup, file-backed sections from `config.json` are written to the config store.
2. Stored rows keep reconciliation metadata for the file-backed definition.
3. UI/API edits update the DB state without changing the file-backed definition.
4. On later startups, unchanged file-backed definitions preserve DB edits.
5. If the matching file-backed definition changes, the new file version is applied for that section or entity.

Split mode does not prune DB-only entries just because they are missing from `config.json`. Removing a provider, plugin, MCP client, or governance row from the file leaves the stored row in place; use `source_of_truth: "config.json"` when a present file section should prune DB-only rows.

Use split mode when you want `config.json` to seed or update a deployment while preserving UI/API edits unless the matching file-backed definition changes.

<Warning>
Split mode preserves runtime edits only while the matching file-backed section or entity is unchanged. If you edit that entity in `config.json`, the file version wins on the next startup.
</Warning>

---

## config.json as Source of Truth

Use this only when the file should actively control the matching DB state:

```json
{
"source_of_truth": "config.json"
}
```

In this mode, explicitly present sections in `config.json` are authoritative at startup. Bifrost applies the file values even if the stored `config_hash` still matches, because UI/API edits do not update the file hash.

This is useful for stricter GitOps setups where the DB should converge back to the file after every restart or redeploy.

---

## Missing vs Empty Sections

In split mode, missing sections are left alone. Authoritative mode is stricter: a missing section is not the same as an empty section.

This leaves stored plugins untouched:

```json
{
"source_of_truth": "config.json"
}
```

This makes the `plugins` section authoritative and empty, so DB-only plugins are removed:

```json
{
"source_of_truth": "config.json",
"plugins": []
}
```

The same pattern applies to other supported top-level sections such as `providers`, `mcp`, and governance sub-sections.
Comment thread
greptile-apps[bot] marked this conversation as resolved.

<Warning>
Before using `source_of_truth: "config.json"` in production, check whether your file contains empty arrays or empty objects for sections you do not intend to prune.
</Warning>

---

## Recommended Use

| Goal | Recommended setup |
|------|-------------------|
| Interactive single-node gateway | Omit `config.json`, or use `config.json` with a config store and default `split` mode |
| Bootstrap from file, then allow UI/API edits | DB-backed config store, explicit or default, with `source_of_truth: "split"` |
| Strict GitOps over selected sections | DB-backed config store, explicit or default, plus `source_of_truth: "config.json"` and only the sections you intend to own |
| File-only OSS multinode deployment | `config_store.enabled: false` with a shared `config.json` |

For DB-backed deployments, prefer `split` unless you explicitly want restarts to revert UI/API changes back to `config.json`.
4 changes: 2 additions & 2 deletions docs/deployment-guides/config-json/storage.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ If you use PostgreSQL for any store, the target database must be **UTF8 encoded*
## config_store

<Note>
When `config_store` is disabled (or absent), all configuration is loaded from `config.json` at startup only - the Web UI is disabled and changes require a restart. See [Two Configuration Modes](/deployment-guides/config-json#two-configuration-modes).
When `config_store` is omitted, Bifrost creates a default SQLite config store in the app directory. Set `config_store.enabled` to `false` only when you want file-only configuration with no config-backed Web UI/API edits. See [Source of Truth & Reconciliation](/deployment-guides/config-json/source-of-truth).
</Note>

<Tabs>
Expand Down Expand Up @@ -129,7 +129,7 @@ Use `password_command` for short-lived database credentials such as AWS RDS IAM

### Disabled (file-only mode)

Use this when you want Bifrost to read all configuration from `config.json` only - no database, no Web UI.
Use this when you want Bifrost to read all configuration from `config.json` only - no configuration database and no config-backed Web UI/API edits.

```json
{
Expand Down
2 changes: 1 addition & 1 deletion docs/deployment-guides/helm/governance.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -406,7 +406,7 @@ bifrost:
```

<Note>
In the default split mode, runtime UI and API edits are preserved while the matching Helm-rendered section is unchanged. When Helm changes a section, tier boundaries are replaced from the rendered `config.json`, while keyword lists are merged additively with stored runtime keywords (union with duplicates removed). Use `bifrost.governance.sourceOfTruth: config.json` only when Helm should replace stored governance state.
In the default split mode, runtime UI and API edits are preserved while the matching Helm-rendered section is unchanged. When Helm changes a section, tier boundaries are replaced from the rendered `config.json`, while keyword lists are merged additively with stored runtime keywords (union with duplicates removed). Use `bifrost.sourceOfTruth: config.json` only when Helm should replace stored governance state. See [Source of Truth & Reconciliation](/deployment-guides/config-json/source-of-truth) for the full startup rules.
</Note>

---
Expand Down
1 change: 1 addition & 0 deletions docs/docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -528,6 +528,7 @@
"icon": "file-code",
"pages": [
"deployment-guides/config-json",
"deployment-guides/config-json/source-of-truth",
"deployment-guides/config-json/schema-reference",
"deployment-guides/config-json/client",
"deployment-guides/config-json/providers",
Expand Down
71 changes: 55 additions & 16 deletions docs/quickstart/gateway/setting-up.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -127,11 +127,20 @@ curl -X POST http://localhost:8080/v1/chat/completions \

---

## Two Configuration Modes
## Configuration Modes

Bifrost supports **two configuration approaches**:
Bifrost has two user-facing configuration modes:

**How config and database state interact:**
| Mode | When to use it | Behavior |
|-------|------|----------|
| Web UI / database | You want to manage configuration from the dashboard or management API | Bifrost stores configuration in SQLite or PostgreSQL. If `config.json` is also present, it seeds or reconciles the database at startup. |
| File-only `config.json` | You want declarative, restart-based configuration with no config database | Bifrost loads configuration from the file at startup. Config-backed UI/API edits are unavailable and file changes require restart. |

<Note>
Omitting `config_store` does **not** mean file-only mode. If `config.json` exists and `config_store` is omitted, Bifrost creates the default SQLite config store and treats the file as DB-backed startup configuration. Set `config_store.enabled: false` only when the file should be the only configuration source.
</Note>

**How DB-backed `config.json` interacts with database state:**

- Entities defined in `config.json` are stored with a
content-based hash on first load.
Expand All @@ -141,21 +150,26 @@ Bifrost supports **two configuration approaches**:
`config.json` does not overwrite those database changes, because the content hash is unchanged. If
you edit an entity directly in `config.json`, its hash changes and the file-based definition
overwrites the database copy.
- Set `source_of_truth: "config.json"` only when explicitly present file sections should replace
matching DB state on startup. Missing sections leave DB rows untouched; present empty sections can
prune matching DB rows.

### Mode 1: Web UI Configuration
For the full reconciliation rules, see [Source of Truth & Reconciliation](/deployment-guides/config-json/source-of-truth).

### Mode 1: Web UI / Database Configuration

![Configuration via UI](../../media/ui-config.png)

**When the UI is available:**

- No `config.json` file exists (Bifrost auto-creates SQLite database)
- `config.json` exists with `config_store` configured
- `config.json` exists with `config_store` omitted or enabled

### Mode 2: File-based Configuration
### Optional: DB-backed config.json

<Note>You can view entire config schema [here](https://www.getbifrost.ai/schema)</Note>

**When to use:** Advanced setups, GitOps workflows, or when UI is not needed
**When to use:** Bootstrap from file, then keep runtime UI/API configuration available.

Create `config.json` in your app directory:

Expand Down Expand Up @@ -187,21 +201,46 @@ Create `config.json` in your app directory:
}
```

**Without `config_store` in `config.json`:**

- **UI is disabled** - no real-time configuration possible
- **Read-only mode** - `config.json` is never modified
- **Memory-only** - all configurations loaded into memory at startup
- **Restart required** - changes to `config.json` only apply after restart

**With `config_store` in `config.json`:**
**With `config_store` omitted or enabled:**

- **UI is enabled** - full real-time configuration via web interface
- **Database check** - Bifrost reconciles `config.json` against the config store using a content-based hash per entity
- **Empty DB**: Bootstraps database with `config.json` settings, then uses DB for runtime reads
- **Existing DB**: Compares each entity's file hash to the stored `ConfigHash`. If the hash is unchanged, the DB copy is kept (UI/API edits are preserved). If you edit an entity in `config.json` so its hash changes, the file definition overwrites the DB copy for that entity. Entries added only via the UI/API (not present in `config.json`) are always preserved.
- **Existing DB**: Compares each entity's file hash to the stored `ConfigHash`. In default split mode, unchanged file-backed entities keep the DB copy so UI/API edits are preserved. If you edit that entity in `config.json`, the file definition overwrites the DB copy for that entity.
- **Persistent storage** - all changes saved to database immediately

### Mode 2: File-only config.json

**When to use:** Advanced setups, GitOps workflows, or when UI is not needed.

Set `config_store.enabled` to `false` when `config.json` should be the only configuration source:

```json
{
"$schema": "https://www.getbifrost.ai/schema",
"providers": {
Comment thread
greptile-apps[bot] marked this conversation as resolved.
"openai": {
"keys": [
{
"name": "openai-key-1",
"value": "env.OPENAI_API_KEY",
"models": ["gpt-4o-mini", "gpt-4o"],
"weight": 1.0
}
]
}
},
"config_store": {
"enabled": false
}
}
```

- **Config UI/API edits are unavailable** - no config store is initialized
- **Read-only file** - Bifrost never modifies `config.json`
- **Memory-only config** - settings are loaded into memory at startup
- **Restart required** - changes to `config.json` apply after restart

**The Three Stores Explained:**

- **Config Store**: Stores provider configs, API keys, MCP settings - Required for UI functionality
Expand Down
Loading