Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Settings Layering

Caliban merges configuration from five file/CLI scopes and then applies an environment layer on top. Knowing the merge order lets you predict which value wins when the same key appears in multiple places.

The five scopes

PriorityScopeDescription
1 (highest)CLI--settings <FILE|JSON> overlay injected above local
2Local.caliban/settings.local.toml in the workspace
3Project.caliban/settings.toml in the workspace
4UserOS user-config directory (see File Locations)
5 (lowest)ManagedSystem-wide directory set by an operator

Higher priority always wins for scalar values. The CLI scope is a virtual overlay — it has no on-disk file.

flowchart LR
    M["Managed\n(lowest)"] --> U["User"]
    U --> P["Project"]
    P --> L["Local"]
    L --> C["CLI --settings"]
    C --> E["Environment layer\n(highest)"]
    E --> EFF["Effective\nSettings"]

The environment layer

A small, fixed set of CALIBAN_* variables folds into the merged settings after the five scopes have been merged, so for these keys env wins over every file scope and over --settings. The point is to let an operator configure a container without generating a settings file.

VariableSettings key
CALIBAN_OUTPUT_STYLEoutput_style
CALIBAN_DEFAULT_PERMISSION_MODEpermissions.default_mode
CALIBAN_STORAGE_SUBSTRATEstorage.substrate
CALIBAN_STORAGE_REMOTE_URLstorage.remote.url
CALIBAN_STORAGE_REMOTE_TOKEN_ENVstorage.remote.token_env

A blank or whitespace-only value is ignored, so the file setting stands. The layer also applies in --bare mode, which otherwise loads no settings files. Each applied override is attributed in caliban config print under _env_overrides, so you can tell an env-sourced value from a file-sourced one.

This list is closed and enforced: a CI lint (scripts/lint-env-registry.sh) fails the build if any other code reads one of these variables ad hoc, so the aggregation point above stays the single place they are honored. Other CALIBAN_* variables documented in Environment Variables are read directly by their own subsystems and are not part of this layer.

Deep-merge semantics

Scalars use highest-wins: the value from the highest-priority scope that defines the key is used; lower scopes are ignored for that key.

Arrays and maps have richer rules:

Key(s)Merge behaviour
permissions.allow, .ask, .denyConcatenated in priority order (lower scopes first, higher appended); duplicates dropped
permissions.rulesConcatenated in priority order; source order within each scope is preserved
hooks.<Event>Concatenated
mcp_servers.<name>Deep-merged per server; a project scope can add an env key to a user-scope server without redefining the whole entry
envDeep-merged (highest-priority value wins per key)
additional_directories, claude_md_excludesConcatenated
Everything elseHighest-wins scalar

The --settings CLI overlay

--settings injects a virtual scope that sits above Local but below any active managed-block. It accepts either an inline JSON object or a path to a .json or .toml file:

# inline JSON
caliban --settings '{"model": "claude-opus-4-7"}'

# file path
caliban --settings /tmp/ci-overrides.toml

This is the recommended way to supply CI-specific settings without touching scope files.

parent_settings_behavior — managed lockdown

When an operator sets parent_settings_behavior = "block" in the managed scope, the merge order flips: the managed scope moves to the top of the chain and overrides every other scope, including the CLI overlay.

# /etc/caliban/managed-settings.toml
parent_settings_behavior = "block"
model = "claude-haiku-4-7"

With "block" active, users cannot override model from their own settings or from --settings. The value "augment" is the default behaviour (managed sits at the bottom).

Enterprise lockdown

When parent_settings_behavior = "block" is set in the managed scope, user, project, local, and CLI settings for locked keys are ignored. The effective values come from the managed scope for those keys.

Managed lockdown does not cover the environment layer

The reordering above applies to the five file/CLI scopes. The environment layer is applied afterwards, so CALIBAN_OUTPUT_STYLE and CALIBAN_DEFAULT_PERMISSION_MODE still override a managed block — including a managed permissions.default_mode. If you rely on managed settings to pin a permission mode, you must also control the agent's environment; the file scope alone is not sufficient.

--setting-sources — scope filtering

--setting-sources restricts which on-disk scopes are loaded. It accepts a comma-separated list of scope names: managed, user, project, local. The CLI overlay is always applied regardless of this flag.

# Load only user + project scopes (skip local overrides)
caliban --setting-sources user,project

# Pin to project scope only — useful for reproducible CI runs
caliban --setting-sources project

An unknown scope name is a fatal error (exit 78) rather than a silent no-op.

Applying changed settings

Settings are loaded and merged once, at startup. Editing a settings file while caliban is running has no effect on the running process — every key is effectively restart-required. Relaunch caliban to pick up a change.

Live reload is scaffolded but not yet wired

The building blocks for live reload exist in caliban-settings — a file watcher, a ConfigChange hook event, and a per-key restart-impact classification — but the binary does not construct the watcher, so on-disk changes are not re-merged at runtime and the ConfigChange hook does not fire. Until the watcher is wired into startup (it sits behind the settings dependency-inversion work, #539), treat all keys as restart-required.

Inspecting the effective result

Run caliban config print to see the fully-merged settings with per-key scope annotations, without starting a session.