# Settings, Feature Flags, Build Channels & Extension Points

> The operational boundaries of the codebase: the settings system and schema, runtime feature gating via warp_features, release-channel binaries and Docker packaging, and the extension surfaces (MCP servers, BYO CLI agents) to inspect next.

- Repository: warpdotdev/warp
- GitHub: https://github.com/warpdotdev/warp
- Human wiki: https://grok-wiki.com/public/wiki/warpdotdev-warp-a55b6d0c09b5
- Complete Markdown: https://grok-wiki.com/public/wiki/warpdotdev-warp-a55b6d0c09b5/llms-full.txt

## Source Files

- `crates/settings/src/manager.rs`
- `crates/settings/src/schema.rs`
- `crates/warp_features/src/lib.rs`
- `app/src/features.rs`
- `app/src/bin/channel_config.rs`
- `CONTRIBUTING.md`

---

<details>
<summary>Relevant source files</summary>
The following files were used as context for generating this wiki page:
- [crates/settings/src/manager.rs](crates/settings/src/manager.rs)
- [crates/settings/src/schema.rs](crates/settings/src/schema.rs)
- [crates/settings/src/lib.rs](crates/settings/src/lib.rs)
- [crates/warp_features/src/lib.rs](crates/warp_features/src/lib.rs)
- [app/src/features.rs](app/src/features.rs)
- [app/src/bin/channel_config.rs](app/src/bin/channel_config.rs)
- [app/src/bin/stable.rs](app/src/bin/stable.rs)
- [app/src/bin/generate_settings_schema.rs](app/src/bin/generate_settings_schema.rs)
- [app/Cargo.toml](app/Cargo.toml)
- [crates/warp_core/src/channel/mod.rs](crates/warp_core/src/channel/mod.rs)
- [crates/warp_core/src/channel/state.rs](crates/warp_core/src/channel/state.rs)
- [crates/warp_core/src/channel/config.rs](crates/warp_core/src/channel/config.rs)
- [crates/warp_cli/src/agent.rs](crates/warp_cli/src/agent.rs)
- [docker/linux-dev/Dockerfile](docker/linux-dev/Dockerfile)
- [CONTRIBUTING.md](CONTRIBUTING.md)
</details>

# Settings, Feature Flags, Build Channels & Extension Points

This page documents the *operational boundaries* of the Warp codebase: the levers that decide what a given binary does, who is allowed to turn them, and where third parties plug in. Four subsystems cooperate. The **settings system** owns user-tunable, persisted configuration and a generated JSON Schema for the TOML settings file. The **feature-flag system** (`warp_features`) owns runtime gating of in-progress functionality. **Build channels** (`warp_core::channel`) decide which server endpoints, telemetry, and flag sets a compiled binary ships with, and how that binary is packaged. Finally, the **extension surfaces** — MCP servers and BYO CLI agent harnesses — are the points where Warp's behavior is widened by code and configuration that live outside Warp itself.

These four are deliberately layered. A *compile-time* cargo feature decides whether code is built at all; a *channel* (selected by which binary target runs) decides which endpoints and flag baselines apply; a *runtime feature flag* decides whether a built path is live; and a *setting* decides how a live feature behaves for one user. Understanding the direction of this dependency chain is the key mental model for the whole boundary layer.

```mermaid
flowchart TD
    subgraph BuildTime["Build time (Cargo)"]
        CF["cargo features<br/>app/Cargo.toml"]
        RB["release_bundle feature"]
    end
    subgraph Entry["Channel binary targets (app/src/bin/*)"]
        STABLE["stable.rs / preview.rs / dev.rs<br/>local.rs / oss.rs / integration.rs"]
        CCFG["channel_config.rs<br/>load_config! macro"]
    end
    subgraph Runtime["Runtime state (warp_core::channel)"]
        CS["ChannelState<br/>channel + additional_features + ChannelConfig"]
        CONF["ChannelConfig<br/>server_config, oz_config,<br/>telemetry/autoupdate/crash/mcp_static"]
    end
    subgraph Gating["Gating layer"]
        FF["FeatureFlag (warp_features)<br/>FLAG_STATES + USER_PREFERENCE_MAP"]
        SETT["SettingsManager (crates/settings)<br/>public TOML + private native store"]
    end
    subgraph Ext["Extension surfaces"]
        MCP["MCP servers<br/>mcp_static_config / FileBasedMcp"]
        HARN["BYO CLI harnesses<br/>warp_cli::agent::Harness"]
    end

    CF --> STABLE
    RB --> CCFG
    STABLE --> CS
    CCFG --> CONF
    CONF --> CS
    CS -->|additional_features + RELEASE_FLAGS| FF
    CF -->|cfg!(feature=...)| FF
    FF -->|gates| SETT
    CONF --> MCP
    FF --> MCP
    FF --> HARN
```

Sources: [app/src/features.rs:8-13](app/src/features.rs#L8-L13), [crates/warp_core/src/channel/state.rs:26-65](crates/warp_core/src/channel/state.rs#L26-L65), [app/Cargo.toml:16-54](app/Cargo.toml#L16-L54)

## The Settings System

A *setting* is a typed, persisted, user-facing value. Settings are registered against a central `SettingsManager` keyed by a string **storage key**, and split into two backends: **public** settings live in the user-editable TOML settings file (when the `SettingsFile` flag is enabled) and **private** settings always live in the platform-native store (UserDefaults on macOS, a JSON file on Linux, the registry on Windows). The newtype wrappers `PublicPreferences` and `PrivatePreferences` enforce this split and keep the raw backend `pub(crate)`, so external code must go through the typed groups produced by `define_settings_group!` rather than touch preferences directly.

```rust
// crates/settings/src/lib.rs:66-90 (abridged)
/// Public settings (those marked `private: false` in `define_settings_group!`)
/// are stored in the user-visible settings file (TOML) when the `SettingsFile`
/// feature flag is enabled, otherwise in the platform-native store.
pub struct PublicPreferences(Box<dyn UserPreferences>);
```

`SettingsManager` stores, per storage key, a `SettingsInfo` record (sync behavior, supported platforms, serialized defaults, TOML key, table depth, privacy) plus a set of closures: `update_fn`, `clear_fn`, `load_fn`, `equals_fn`, and `is_syncable_fn`. The `equals_fn` exists because settings cannot be compared as raw JSON — types like `HashSet` serialize to arrays with no defined order — so equality is delegated to the setting's own value semantics. Cloud sync precedence is captured by `SyncToCloud`, and `sync_regardless_of_users_syncing_setting` distinguishes settings that sync even when the user has disabled syncing.

| `SettingsManager` responsibility | Method | Notes |
|---|---|---|
| Register a setting + its closures | `register_setting` | Called by the `define_settings_group!` expansion |
| Read current local value | `read_local_setting_value` | Routes private→private store, public→TOML when `SettingsFile` is on |
| Apply an update | `update_setting_with_storage_key` | `from_cloud_sync` flag distinguishes origin |
| Hot-reload from disk | `reload_all_public_settings` → `load_setting` | Loads into memory without writing back, avoiding file-watcher loops |
| Validate file values | `validate_all_public_settings` | Read-only deserialization check on startup |
| Emit defaults for the file | `default_values_for_settings_file` | Public settings only, with hierarchy + table depth |

Sources: [crates/settings/src/manager.rs:32-139](crates/settings/src/manager.rs#L32-L139), [crates/settings/src/manager.rs:224-250](crates/settings/src/manager.rs#L224-L250), [crates/settings/src/manager.rs:315-406](crates/settings/src/manager.rs#L315-L406), [crates/settings/src/lib.rs:66-128](crates/settings/src/lib.rs#L66-L128)

### Hot reload and validation

`reload_all_public_settings` is the heart of the live-edit experience for the TOML file. It reads every non-public-excluded setting from a freshly-reloaded preferences store, then applies each through `load_setting`, which updates in-memory state **without** persisting — this is the explicit mechanism for not fighting the file watcher with a write-back loop. Keys present in the file load with `explicitly_set = true`; absent keys reset to their serialized default with `explicitly_set = false`. A setting that fails to load is "re-inhibited" (`inhibit_writes_for_key`) so a user's broken-but-fixable value is not overwritten, and its key is returned to the caller for error reporting.

Sources: [crates/settings/src/manager.rs:315-375](crates/settings/src/manager.rs#L315-L375)

## The Settings Schema

Warp ships a JSON Schema for its settings file so editors can validate and autocomplete `settings.toml`. The schema is built from an `inventory`-collected registry: each setting registered via the macros emits one `SettingSchemaEntry` through `submit_schema_entry!`. An entry carries the storage key, description, TOML hierarchy, privacy flag, an optional gating `FeatureFlag`, and function pointers for the type schema and default values.

```rust
// crates/settings/src/schema.rs:25-46 (abridged)
pub struct SettingSchemaEntry {
    pub storage_key: &'static str,
    pub description: &'static str,
    pub hierarchy: Option<&'static str>,
    pub is_private: bool,
    /// If Some, the setting is only included in the schema when the flag
    /// is active for the target build channel.
    pub feature_flag: Option<warp_features::FeatureFlag>,
    // ...schema_fn, default_value_fn, file_default_value_fn, max_table_depth
}
inventory::collect!(SettingSchemaEntry);
```

The `generate_settings_schema` binary iterates that registry. It is **channel-aware**: `active_flags_for_channel` maps a channel name to flag lists (`stable` → `RELEASE_FLAGS`; `preview` → `RELEASE_FLAGS + PREVIEW_FLAGS`; `dev` → all four lists), and any setting whose `feature_flag` is not active for the requested channel is skipped — as are all private settings. The generator nests settings into TOML section hierarchies (`ensure_hierarchy`), prefers the file-format default over the serde default, and strips type-derived numeric metadata (`minimum`/`maximum`/`format` that schemars infers from Rust primitives) because those leak implementation bounds rather than semantic ones.

Sources: [crates/settings/src/schema.rs:7-99](crates/settings/src/schema.rs#L7-L99), [app/src/bin/generate_settings_schema.rs:87-107](app/src/bin/generate_settings_schema.rs#L87-L107), [app/src/bin/generate_settings_schema.rs:174-219](app/src/bin/generate_settings_schema.rs#L174-L219)

## Runtime Feature Gating: `warp_features`

`warp_features` defines a single large `FeatureFlag` enum (hundreds of variants, each documented inline) and resolves each flag's state from three layers of backing storage. State lives in fixed-size atomic arrays sized by `cardinality::<FeatureFlag>()`, so a lookup is an `O(1)` indexed atomic read with no allocation.

`FeatureFlag::is_enabled()` resolves with a strict precedence: a **thread-local test override** wins first, then a **user preference** (the `USER_PREFERENCE_MAP` tri-state, allowing explicit opt-in/opt-out), then the **global flag state** (`FLAG_STATES`), defaulting to `false`. In debug builds, calling `is_enabled()` before `mark_initialized()` panics, guaranteeing flags are never read before the channel wires them up.

```text
FeatureFlag::is_enabled() resolution order
┌─────────────────────────────┐
│ test override (thread-local)│  ← override_enabled(), test-util only
└──────────────┬──────────────┘
               │ None
┌──────────────▼──────────────┐
│ USER_PREFERENCE_MAP (TriState)│ ← set_user_preference(): explicit opt in/out
└──────────────┬──────────────┘
               │ Unset
┌──────────────▼──────────────┐
│ FLAG_STATES (AtomicBool[])  │ ← set_enabled(): global, from channel init
└──────────────┬──────────────┘
               │ default
            false
```

Flags are grouped into rollout tiers as `&[FeatureFlag]` constants: `DEBUG_FLAGS`, `DOGFOOD_FLAGS` (internal team, en route to Preview), `PREVIEW_FLAGS` (Friends of Warp; also enabled in dogfood), and `RELEASE_FLAGS` (every release build but `WarpLocal`). `RUNTIME_FEATURE_FLAGS` enumerates flags that may flip at runtime when `RuntimeFeatureFlags` is set. The `flag_description` method intentionally returns text only for Preview-exclusive flags, to control what surfaces in the Preview changelog.

Sources: [crates/warp_features/src/lib.rs:881-996](crates/warp_features/src/lib.rs#L881-L996), [crates/warp_features/src/lib.rs:1051-1054](crates/warp_features/src/lib.rs#L1051-L1054), [crates/warp_features/src/lib.rs:1139-1198](crates/warp_features/src/lib.rs#L1139-L1198)

### How flags become enabled at startup

`app/src/features.rs` is the bridge between compile-time configuration and runtime state. `init_feature_flags` calls `enabled_features()` and globally enables each returned flag, then marks the system initialized. `enabled_features()` unions three sources: the running channel's `additional_features`, `RELEASE_FLAGS` when the build is a release bundle, and a long list of flags each guarded by a `#[cfg(feature = "...")]` cargo feature. The cargo-feature layer is what makes a flag's code *exist* in a build; the channel layer is what turns baseline release features *on*.

```rust
// app/src/features.rs:15-24 (abridged)
fn enabled_features() -> HashSet<FeatureFlag> {
    let mut flags = ChannelState::additional_features();
    if ChannelState::is_release_bundle() {
        flags.extend(RELEASE_FLAGS);
    }
    flags.extend([ /* #[cfg(feature = "...")] FeatureFlag::... */ ]);
    flags
}
```

Sources: [app/src/features.rs:6-30](app/src/features.rs#L6-L30), [app/src/features.rs:494-497](app/src/features.rs#L494-L497)

## Build Channels and Release Binaries

A **channel** identifies which build is running. `Channel` enumerates `Stable`, `Preview`, `Dev`, `Local`, `Oss`, and `Integration`, and encodes their policy differences directly: `is_dogfood` (Dev/Local only), `allows_server_url_overrides` (only the internal channels honor `--server-root-url` and `WARP_*` env overrides, so shipped builds cannot be redirected), `cli_command_name` (e.g. `oz`, `oz-preview`, `warp-oss`), and a per-channel `url_scheme` (`warp`, `warppreview`, `warpdev`, …).

| Channel | Dogfood | Server-URL overrides | CLI command | URL scheme |
|---|---|---|---|---|
| `Stable` | no | no | `oz` | `warp` |
| `Preview` | no | no | `oz-preview` | `warppreview` |
| `Dev` | yes | yes | `oz-dev` | `warpdev` |
| `Local` | yes | yes | `oz-local` | `warplocal` |
| `Oss` | no | no | `warp-oss` | `warposs` |
| `Integration` | no | yes | `oz-integration` | `warpintegration` |

The channel and its endpoints are held by `ChannelState`, a process-global behind a `Mutex`, combining the active `Channel`, an `additional_features` set, and a `ChannelConfig`. `ChannelConfig` groups all the externally-facing endpoints and credentials: `WarpServerConfig` (server root, RTC websocket, session sharing, Firebase auth key), `OzConfig` (ambient-agent dashboard URL), and **optional** `telemetry_config`, `autoupdate_config`, `crash_reporting_config`, and `mcp_static_config`. The optionality is meaningful: a build such as OpenWarp intentionally ships with `telemetry_config: None`, and `is_telemetry_available()` lets the UI hide controls that would otherwise have no effect.

Sources: [crates/warp_core/src/channel/mod.rs:9-74](crates/warp_core/src/channel/mod.rs#L9-L74), [crates/warp_core/src/channel/state.rs:26-89](crates/warp_core/src/channel/state.rs#L26-L89), [crates/warp_core/src/channel/state.rs:183-197](crates/warp_core/src/channel/state.rs#L183-L197), [crates/warp_core/src/channel/config.rs:7-52](crates/warp_core/src/channel/config.rs#L7-L52)

### One binary target per channel

`app/Cargo.toml` declares a distinct `[[bin]]` for each channel (`oss`, `local`, `integration`, `stable`, `dev`, `preview`) plus the `generate_settings_schema` helper, with `autobins = false` so only these explicit targets compile and `default-run = "warp-oss"`. The comment in the manifest notes the binaries are otherwise identical to the main binary; the difference is icon, app name, and channel-specific config such as flag overrides. Each channel `main` is a thin wrapper that installs the channel state then calls `warp::run()`:

```rust
// app/src/bin/stable.rs:12-19
fn main() -> Result<()> {
    ChannelState::set(ChannelState::new(
        Channel::Stable,
        channel_config::load_config!("stable"),
    ));
    warp::run()
}
```

The `load_config!` macro (shared via `#[path = "channel_config.rs"]`) decides *where* the channel's JSON config comes from based on the `release_bundle` cargo feature. In a release bundle the config is embedded at compile time via `include_str!(env!("OUT_DIR")/<channel>_config.json)`; otherwise `load_config_from_generator` shells out at runtime to a `warp-channel-config` binary expected on `PATH`, passing `--channel`, `--target-family`, and `--target-os`. A missing generator prints actionable guidance (`./script/install_channel_config`).

Sources: [app/Cargo.toml:16-54](app/Cargo.toml#L16-L54), [app/src/bin/stable.rs:1-19](app/src/bin/stable.rs#L1-L19), [app/src/bin/channel_config.rs:25-101](app/src/bin/channel_config.rs#L25-L101)

### Docker packaging

Container assets are split by purpose under `docker/` (plus packaging helpers under `.github/actions/`). `docker/linux-dev/Dockerfile` builds a Debian-`sid` development environment: it installs build toolchain and X11/Vulkan runtime libraries, `protobuf-compiler` (needed to build the `warp-proto-apis` MAA crates), the GitHub and gcloud CLIs, a `dev` user, rustup pinned to the repository's `rust-toolchain.toml`, and an SSH server exposed on port 22 so a host can connect to the containerized build. This is an environment image for building/running Warp on Linux, not a distribution artifact of Warp itself.

Sources: [docker/linux-dev/Dockerfile:1-81](docker/linux-dev/Dockerfile#L1-L81)

## Extension Surfaces

The boundary layer also defines where outside code extends Warp. Two surfaces matter most.

**MCP (Model Context Protocol) servers.** Warp supports user-configured MCP servers as a tool/resource source for the agent. Channel-level OAuth credentials for providers that lack dynamic client registration are baked into `ChannelConfig::mcp_static_config` as `McpStaticConfig { providers: Vec<McpOAuthProviderConfig> }`, looked up at runtime by client ID or issuer (`mcp_oauth_provider_by_client_id` / `mcp_oauth_provider_by_issuer`). The feature is gated by several flags — `McpServer` (v0), `McpOauth`, `FileBasedMcp` (`.mcp.json` files discovered at repo roots), `MCPGroupedServerContext`, and `McpDebuggingIds` — and surfaced through dedicated settings UI under `app/src/settings_view/mcp_servers/`.

**BYO CLI agent harnesses.** A *harness* selects which engine executes an agent run. `warp_cli::agent::Harness` is a `clap` `ValueEnum` whose variants are `Oz` (Warp's built-in MAA, the default), `Claude`, `OpenCode`, `Gemini`, `Codex`, and a non-selectable `Unknown` for forward compatibility with newer servers. External harnesses are reached via the `--harness` flag, gated by the `AgentHarness` feature flag, and each third-party CLI has its own install/notification flag (`OpenCodeNotifications`, `CodexNotifications`, `GeminiNotifications`, all layered on `HOANotifications`). This is the BYO/BYOK-friendly seam: any agent CLI a contributor uses is a portable, provider-neutral plug-in point rather than a hardwired dependency — a model the contribution guide reinforces by stating you can "use **any coding agent**."

```rust
// crates/warp_cli/src/agent.rs:131-155 (abridged)
pub enum Harness {
    #[default] Oz,            // Warp's built-in MAA infrastructure
    Claude,                   // delegate to `claude` CLI
    OpenCode,                 // delegate to `opencode` CLI
    Gemini,                   // delegate to `gemini` CLI
    Codex,                    // delegate to `codex` CLI
    Unknown,                  // forward-compat, never CLI-selectable
}
```

Sources: [crates/warp_core/src/channel/config.rs:125-144](crates/warp_core/src/channel/config.rs#L125-L144), [crates/warp_core/src/channel/state.rs:357-377](crates/warp_core/src/channel/state.rs#L357-L377), [crates/warp_features/src/lib.rs:263-264](crates/warp_features/src/lib.rs#L263-L264), [crates/warp_features/src/lib.rs:670-671](crates/warp_features/src/lib.rs#L670-L671), [crates/warp_cli/src/agent.rs:128-198](crates/warp_cli/src/agent.rs#L128-L198), [CONTRIBUTING.md:119-125](CONTRIBUTING.md#L119-L125)

## Where to Look Next

The contribution model in `CONTRIBUTING.md` ties these boundaries together operationally: feature work proceeds through readiness labels and `specs/`, agent-readable context ships under `.agents/skills/`, and review is automated by the Oz agent. Concretely, to extend the boundary layer you would: add a `FeatureFlag` variant plus its `#[cfg(feature = "...")]` wiring in `app/src/features.rs`; register a setting through `define_settings_group!` so it joins both `SettingsManager` and the schema registry; add a channel endpoint to `ChannelConfig`; or register a new MCP server / agent harness. The strict layering — cargo feature → channel → flag → setting — means a change is usually localized to exactly one of these tiers, and the schema generator's channel-scoped flag filtering ensures the published settings surface always matches what a given release can actually do.

Sources: [CONTRIBUTING.md:16-55](CONTRIBUTING.md#L16-L55), [crates/settings/src/schema.rs:25-48](crates/settings/src/schema.rs#L25-L48), [crates/warp_core/src/channel/config.rs:7-28](crates/warp_core/src/channel/config.rs#L7-L28)
