Configuration overview
The five config layers, the sparse-file trap that bricks the CLI, and how to write config safely.
Write config with grith init or grith config set. Hand-writing a short config file has a
failure mode that stops the CLI dead - see the sparse-file trap below.
grith config prints the effective configuration, every key, after all layers are applied.
$ grith config
2026-08-24T17:05:10.719720Z INFO grith: grith starting version="0.3.1"
[general]
log_level = "info"
audit_dir = "~/.local/share/grith/audit"
plan_tier = "community"
update_check = true
audit_sync = true
profile_update_check = true
onboarded = false
exec_notice_seen = falseLayers
Five layers, applied in order. Later wins.
| # | Layer | Where |
|---|---|---|
| 1 | Base defaults | config/default.toml, compiled into the binary. Required - grith will not start without it. |
| 2 | User config | ~/.config/grith/config.toml |
| 3 | Project config | ./.grith/config.toml, relative to the directory you run the command in |
| 4 | Explicit file | --config <path>, available on every subcommand |
| 5 | Environment | GRITH_* variables. See Environment variables. |
--log-level is applied after all five and is the only CLI flag that behaves as a config
layer. --config and --project are inputs to the layering, not values in it.
⚠️The user path is literal
The user config path is $HOME/.config/grith, resolved from $HOME directly. It does not honour
XDG_CONFIG_HOME. Licence, credentials, provider keys and the persistent allowlist use a
different resolver that does.
The sparse-file trap
Every layer is deserialised into the config struct and then deep-merged, so a file that omits
a key still contributes that key's Rust default - which is not always the value
default.toml ships. A two-line overlay is enough to break the CLI:
$ cat config.toml
[general]
log_level = "debug"
$ grith --config config.toml config
Error: configuration invalid:
supervisor.default_profile must be set in TOML or via GRITH_SUPERVISOR_PROFILEdefault.toml sets default_profile = "generic"; the struct default is the empty string,
which validation rejects. Five keys differ this way, and all five shift the moment any user,
project or --config file exists:
| Key | Ships as | After any overlay file |
|---|---|---|
supervisor.default_profile | "generic" | "" - fatal |
supervisor.max_concurrent_sessions | 4 | 64 |
llm.openai.model | "gpt-4o-mini" | "gpt-4o" |
llm.anthropic.model | "claude-sonnet-4-5-20250514" | "claude-sonnet-4-20250514" |
llm.openrouter.model | "anthropic/claude-sonnet-4-5-20250514" | "auto" |
Both safe routes write a complete file. grith init writes the whole of default.toml,
comments intact, to ~/.config/grith/config.toml - unconditionally, with no backup and no
merge. grith config set loads the effective config, changes one key and writes it all
back; comments are lost. If you do hand-write a file, keep supervisor.default_profile in it
and check the result with grith config.
What grith config set accepts
A fixed whitelist of keys, not arbitrary dotted paths:
$ grith config set supervisor.trust.restrict_to_workspace true
2026-08-24T17:05:10.733695Z INFO grith: grith starting version="0.3.1"
Error: configuration error: unknown config key: supervisor.trust.restrict_to_workspaceThe grith config reference lists the keys worth knowing about.
Everything else - all of
[supervisor.coverage], [supervisor.trust], [audit], [reputation], [proxy.filters.*],
[server.tls] - is edited in the file. There is no grith config reload, get, list,
edit or validate; set is the only subcommand.
When a change takes effect
Config is read at process start, so a change reaches a running daemon on restart:
$ grith daemon stop && grith daemon startThe dashboard writes two proxy thresholds and auto_open_dashboard through
PUT /api/config. That updates the file; it does not mutate the live proxy, and its own
response says the thresholds apply after a restart. Filter toggles saved from the dashboard
persist to the file but do not change which filters the daemon registers.
Validation aborts the command rather than degrading quietly - a threshold pair the wrong way
round, a log level outside trace|debug|info|warn|error, a provider outside
ollama|openai|anthropic|openrouter, server.port = 0, or a blank default_profile:
$ grith --config bad.toml config
Error: configuration invalid:
proxy.auto_allow_threshold (9) must be less than auto_deny_threshold (8)Where to go next
[supervisor]- syscall coverage, workspace trust, DNS[proxy]- thresholds and filter switches[audit]- what is recorded and for how long- Filter config files - the data files filters load
- Environment variables