002 · Config
Configuration (spec_config_002)
| Status | Implemented. Reverse-engineered from 53f8c53 (2026-09-24) and kept with the code since; its paths and names checked 2026-09-27 (//tools/specs). |
| Source | pkg/config/config.go, features.go, locale.go, agentmodels.go, modelsettings.go, providers.go; pkg/secrets; template and key commands in apps/cli/configcmd.go, configkeys.go |
| Tests | pkg/config/*_test.go, pkg/secrets/secrets_test.go, apps/cli/configkeys_test.go, apps/service/internal/server/config_test.go |
| Related | spec_cli_020, spec_models_015 |
One TOML file, .env.toml, loaded through github.com/rrmcguinness/modenv (hierarchical load plus secret decryption), configures everything. The file is trusted input: it can set API keys, base URLs and auto-approval, so only trusted locations are read.
- CFG-01 The config directory is, in order:
--config DIR,$MODENV_PREFIX,~/.blitz. The current working directory and the workspace are never consulted implicitly; a project.env.tomlis ignored unless the user passes--config .. Planned (decision 2026-09-26): a restricted project layer,.blitz/settings.toml, with trust pinned to a content hash and never carrying credentials or endpoints — spec_parity_027 §2.4. User configuration keeps this rule. - CFG-02
LoadsetsMODENV_PREFIXto the chosen directory and callsmodenv.Loadonly when<dir>/.env.tomlexists; otherwise defaults apply. - CFG-03 Environment fallbacks are applied after the file:
LLM_PROVIDER;GEMINI_API_KEYthenGOOGLE_API_KEY(only if no Gemini key);OPENAI_API_KEY(if empty);OPENAI_BASE_URL,OPENAI_MODEL(override);ANTHROPIC_API_KEY(if empty);BLITZ_MODEL,BLITZ_AGENT,BLITZ_AGENCY(override);BLITZ_LOG_LEVEL;BLITZ_TELEMETRY(1/true/yes/onenables,0/false/no/offdisables). - CFG-04
ExpandHomeexpands only~and~/…;~useris left unchanged. - CFG-05 Workspace-relative search paths (skills, agents) are resolved against the workspace, not the process CWD; the project’s agents and skills load as prompt text, and its skills’ scripts run only once the project’s settings are trusted (spec_project_config_031). Agent search paths are
~/.blitz/agentsand.agents/agents(./agentsuntil 2026-10-01).blitz.trust_workspace(only from the user’s own settings) is deprecated: it trusts the project’s settings for each run without recording it.
| Section | Key | Default | Notes |
|---|---|---|---|
[blitz] |
default_agent |
blitz |
|
default_model |
"" |
overrides llm.<provider>.model |
|
agency_level |
high |
low|medium|high|extreme |
|
temperature / max_tokens |
0.2 / 65536 |
global generation settings (max_tokens was 8192 until 2026-10-01: with thinking counted against it, long replies such as a file were cut off). One default for every model: when a provider rejects it as above the model’s maximum and says what that is (Anthropic, OpenAI, Gemini), the request goes again at that maximum, which the model keeps (outputcap.go) |
|
permission_mode |
auto (unset; bypass with auto_approve = true) |
auto|default|accept-edits|plan|dont-ask|bypass; bypass needs the OS sandbox; a workspace’s own settings may set it (spec_approvals_005 APR-14) |
|
auto_approve |
false |
older spelling of permission_mode = "bypass" |
|
goal_max_continues |
20 | how often a /goal sends the agent on before stopping (spec_sessions_017 SES-19) |
|
plan_review |
agent-decides |
always (every prompt is planned for approval first), agent-decides (the agent may call enter_plan_mode), never (spec_workspace_018 WS-45) |
|
trust_workspace |
false |
Deprecated: trusts the project’s settings for each run (CFG-05) | |
[llm] |
provider |
gemini |
gemini|anthropic|openai|ollama|bedrock|azure|vertex-anthropic |
max_retries |
3 |
0 disables |
|
stall_timeout_seconds |
600 |
||
fallback_models |
[] |
provider/model or bare model |
|
[llm.gemini] |
api_key, api_key_command, api_key_ttl (5m), model, auth (api_key, adc or oauth; a key, when there is one, comes first: spec_models_015), project_id, location |
model gemini-4-argon (gemini-3.8-flash until 2026-10-13); auth api_key |
adc: spec_models_015 |
[llm.openai] |
api_key, api_key_command, api_key_ttl, base_url, model |
https://api.openai.com/v1, gpt-4o |
shared by ollama |
[llm.anthropic] |
api_key, api_key_command, api_key_ttl, auth (api_key, oauth or adc), profile, project_id, location, model, base_url, fallbacks |
claude-opus-5, default; auth api_key |
oauth, adc: spec_models_015 |
[llm.bedrock] |
region, profile, model |
Claude on Amazon Bedrock (spec_models_015 §3) | |
[llm.azure] |
resource, base_url, anthropic_base_url, api_key, auth (api_key or entra), model |
OpenAI models and Claude on Azure (spec_models_015 §3) | |
[skills] |
enabled, paths, [skills.policy] |
on; ~/.blitz/skills, ./skills, .agents/skills |
see spec_skills_013 |
[workers] |
enabled, paths, notify, notify_on, [workers.policy] |
on; workers; none; failed, limited |
see spec_workers_023 |
[tools] |
shell_timeout_seconds 120, max_file_size_bytes 10 MiB, workspace_dir ., auto_approve_commands false, uc_tools_dir, approvals_file ~/.blitz/approvals.json, max_parallel 8, max_background_agents 4, background_agent_timeout 30m, background_agent_max_turns 50, background_agent_max_cost_usd 0 (none) (spec_background_agents_032) |
||
[session] |
storage_dir ~/.blitz/sessions, auto_save true |
||
[permissions] |
allow, ask, deny lists of rules; read_only_defaults; [permissions.auto] model, environment (the auto mode’s reviewer, APR-14a) |
[]; true; "" |
spec_approvals_005 APR-15; a workspace’s lists add to the global ones (PermissionsConfig.Merge); not the same as sandbox.commands.allow (an allow-list) |
[sandbox] |
allowed_paths, read_only_paths, blocked_paths (defaults below), shell_writable_paths (~/.cache, ~/go/pkg/mod, ~/.npm), shell auto, allow_network true, scrub_env (defaults below) |
see spec_filetools_006, spec_shell_007 | |
[sandbox.commands] |
allow, deny (defaults below), auto_approve |
||
[ui] |
markdown true, spinner true, terminal_title true, history_file ~/.blitz/history, history_size 1000, diff_lines 120, theme auto, locale en-US, locales_dir ~/.blitz/locales, style (none), notify_after 30, notify both, task_alerts notify, status_line (none), editor emacs, keybindings ~/.blitz/keybindings.toml |
style: spec_memory_012 MEM-40 |
|
[images] |
enabled true, dir ~/.blitz/images, max_dimension 1568, max_input_mb 20, retain_days 30 |
||
[pdf] |
page_size auto (a4, letter; auto: Letter where the locale’s country uses it) |
the export_pdf tool: spec_filetools_006 FS-70 |
|
[audio] |
model (none: no speech), voice, speakers (two {name, voice}), max_chars 9000 |
generate_audio: spec_models_015 MDL-90; audio.model and audio.voice can be set with SetConfigValue |
|
[memory] |
enabled true, files ["AGENTS.md","CLAUDE.md","GEMINI.md","BLITZ.md"], local_files ["CLAUDE.local.md","BLITZ.local.md"], rule_dirs [".blitz/rules",".agents/rules",".claude/rules"], global ~/.blitz/BLITZ.md, global_rules ~/.blitz/rules, max_bytes 32 KiB, auto true (the agent’s notes) |
see spec_memory_012 | |
[context] |
compaction true, token_threshold 120000, retain_events 20 |
||
[audit] |
enabled true, dir ~/.blitz/audit |
||
[log] |
level info, dir ~/.blitz/logs, retain_days 14 |
||
[telemetry] |
enabled false, endpoint, capture_content false |
||
[checkpoints] |
enabled true, max_bytes 64 MiB |
||
[web] |
enabled true, allow_domains, deny_domains, allow_private false, max_bytes 2 MiB, timeout_seconds 20, search_provider, search_api_key, search_url, search_model, search_max_results |
||
[browser] |
enabled true, path (found), visible false, allow_local false, allow_scripts false, width 1280, height 800 |
never from a project; see spec_web_008 §5 | |
[plugins] |
enable, disable (installed plugins by name; disable wins) |
a project may disable, and enable once trusted; see spec_plugins_033 | |
[network] |
ca_file (PEM certificate authorities to trust besides the system’s) |
proxies from HTTPS_PROXY/NO_PROXY; never from a project (spec_models_015 MDL-54) |
|
[lsp.<language>] |
command, extensions, disabled |
built in: go, typescript, python, rust | never from a project (spec_filetools_006 FS-61) |
[hooks] |
pre_tool, post_tool, prompt_submit, session_start, session_end, stop, post_tool_failure, subagent_start, subagent_stop, pre_compact, post_compact, notification, permission_request arrays of {match, if, type, command, args, url, headers, allowed_env_vars, prompt, model, timeout_seconds, fail_closed} |
spec_hooks_010 | |
[[mcp.servers]] |
name, command, args, env, url, headers, tools, auto_approve, sandbox, prefix, agents, timeout_seconds, disabled |
stdio or streamable HTTP (no SSE); env and headers are plain text, masked in logs and config show |
|
[pricing."<model>"] |
input_per_mtok, output_per_mtok, cached_input_per_mtok, cache_write_per_mtok |
built-ins below | |
[search_pricing."<provider>"] |
per_1k_queries |
google 14 |
spec_web_008 WEB-15; not from a project |
[agent_models] |
agent = "provider/model" |
||
[model_settings."<model>"] |
temperature, max_tokens, top_p, seed, reasoning_effort, thinking_budget |
reasoning_effort: minimal|low|medium|high|max; thinking_budget ≥ 0 |
Built-in lists:
-
Blocked paths:
.env,.env.local,.env.*.local,.env.toml,.env.*.toml,*.pem,*.key,*.p12,id_rsa*,id_ecdsa*,id_ed25519*,~/.ssh,~/.aws,~/.gnupg,~/.config/gcloud,~/.azure,~/.kube,~/.docker/config.json,~/.netrc. -
Scrubbed env:
*_API_KEY,*_API_TOKEN,*_SECRET,*_SECRET_KEY,*_ACCESS_KEY,AWS_SESSION_TOKEN,GOOGLE_APPLICATION_CREDENTIALS,MODENV_*. -
Denied commands:
sudo *,su *,doas *,shutdown *,reboot *,halt *,mkfs *,mkfs.*,diskutil erase*. -
Pricing (USD per 1M tokens, input/output/cached/cache-write):
gemini-3.8-flash0.75/3.75/0.075 (introductory until 2026-12-31; 1.50/7.50/0.15 from 2027-01-01, see CFG-13),gemini-4-argon0.75/3.75/0.075 (the default since 2026-10-13; a placeholder at 3.8 Flash’s price until Argon’s is published),gemini-2.5-pro1.25/10/0.31,gpt-4o2.50/10/1.25,claude-opus-55/25/0.50/6.25,claude-sonnet-52/10/0.20/2.50,claude-haiku-4-51/5/0.10/1.25. -
CFG-10
Config.ModelName()returnsblitz.default_modelif set, else the active provider’smodel(openaiandollamashare[llm.openai]). -
CFG-11 Setting a list (e.g.
blocked_paths,commands.deny) replaces the default list. -
CFG-13 Announced price changes are data, not reminders:
PriceChangeslists{model, from (UTC), price}, andDefaultPricingAt(t)applies every change in effect att(the latest per model) to a copy ofDefaultPricing.DefaultConfiguses the prices in effect when the configuration loads, so a long-running service picks up a change at its next restart.[pricing]entries still override. -
CFG-12
SkillPolicy.Problems()reports:min_hitl_tieroutside 0–3; unknownsandbox; unknownnetwork;network_allowwithoutnetwork = "allowlist"; unknown languages (onlypython,typescript, both allowed by default); negativemax_timeout_seconds.
Front ends change a few settings and persist them without rewriting the file.
- CFG-20
editConfigFile(dir, edit, verify)reads<dir>/.env.toml(missing = empty), applies a line-based edit, refuses the result if it is not valid TOML or failsverify(re-decoded value must equal the intended one), then writes atomically (temp file + rename) preserving the existing mode (new files 0600, dir 0700). - CFG-21 Only the affected line changes: comments, ordering and encrypted values elsewhere are kept. Replacing a key preserves its trailing comment. A missing key is inserted after the table’s last non-blank line; a missing table is appended.
- CFG-22 Keys are written bare when they match
[A-Za-z0-9_-]+, else quoted. - CFG-23 Editors:
SaveUILocale([ui] locale),SaveAgentModel([agent_models] <agent>; empty ref removes the line),UnpinAgentModel(removes the line, or with masking writes<agent> = "", which a workspace file uses to mask a global pin; the overlay merges[agent_models]agent by agent),SaveModelSettings([model_settings."<model>"]: set keys written, unset removed, table header dropped when only blanks/comments remain, avoiding a double blank line). - CFG-24 Persisted edits always target the default config dir (
ConfigDir("")), and the in-memory change applies even if saving fails (the failure is reported, not fatal).
- CFG-30 Keys and ranges:
temperature[0, 2];top_p(0, 1];max_tokensinteger ≥ 1 and ≤ 2³¹−1;seedany int32. Empty value clears. Unknown keys are errors listing the known keys. Floats are always written with a decimal point.
Added 2026-09-27 (owner’s decisions: keys in the OS keychain; a workspace’s own settings kept in ~/.blitz, never in the workspace; the desktop app edits them through forms and as text).
- CFG-40 A workspace’s own settings are
<config dir>/workspaces/<base>-<8 hex>/.env.toml, where<base>is the directory’s name (characters outside[A-Za-z0-9._-]as-) and the hex the first 4 bytes of the SHA-256 of its absolute, symlink-free path (WorkspaceSettingsDir). CFG-01 stands: nothing is read from the workspace. - CFG-41
LoadWorkspace(dir, workspace)loads the global file, then the workspace’s file over it (every key it sets wins; lists replace), then resolves secrets (CFG-43), then applies the environment (CFG-03). The service’s opener and the CLI (--dir, else the current directory) use it;Loadis the global scope alone. - CFG-42 Secrets are kept by
pkg/secrets: the macOS Keychain (through/usr/bin/security, the secret passed on stdin, hex-encoded, never in arguments), the Secret Service on Linux (secret-tool, when one answers on the session bus), else an owner-only file (<config dir>/secrets.toml, 0600 in a 0700 directory), all under the service namedev.blitz. Names areglobal/llm.<provider>.api_keyandworkspace/<settings dir name>/llm.<provider>.api_key. - CFG-43 A settings value
keychain:<name>refers to a stored secret. Loading replaces the providers’api_keyreferences with the secrets (and decodes modenv’sxor:values from a workspace file); a missing or unreadable secret leaves the key empty, so the environment, then the model’s own error, takes over.xor:is obfuscation with a default key, not encryption. - CFG-44
Describe(dir, workspace)reports a scope without its keys: the file,llm.providerandblitz.default_modelas the scope sets them, where keys are kept, and per provider (gemini,anthropic,openai) the key’s source —keychain,plain,obfuscated,environment,inherited(a workspace using the global key) ornone— whether a referenced secret is missing,base_urlandmodel, and how it signs in as the scope sets it:auth, withproject_idandlocation(adc) orprofile(oauth). - CFG-45 Changes, each through CFG-20’s editor, in the scope’s file:
SetAPIKeystores the key and writes the reference;SecureAPIKeymoves a plain or obfuscated key from the file to the store;RemoveAPIKeyremoves the reference and the stored secret (a workspace then inherits);SetValuesets one ofllm.provider,blitz.default_model,llm.<provider>.model,llm.{anthropic,openai}.base_url,llm.gemini.{auth,project_id,location},llm.anthropic.{auth,profile,project_id,location}(""removes it, so a workspace follows the global setting; anauththe provider doesn’t take is refused). Others are refused.SetAuthsets how a provider signs in (ProviderAuth:api_key;adc, Gemini and Anthropic, with its project and location;oauth, Anthropic only, with its profile), removing the fields left empty; back toapi_keyremoves onlyauth.SetProvidersaves the provider form as one change:llm.provider,blitz.default_model(""removes either) and, if given, how the provider signs in (ProviderAuth) and a new key for it (stored, and referred to), all checked before anything is written (an unknown provider, a key or sign-in for none or forollama, or a key with a sign-in other thanapi_key, is refused) and the file written once. - CFG-45a 2026-10-04 (the owner’s request: OAuth among every provider’s sign-in options, as well as keys). The sign-in methods besides an API key (
AuthMethods): Geminiadcandoauth(a Google account, on Vertex AI, withproject_idandlocation), Anthropicoauth(anantprofile,profile) andadc, Bedrockoauth(AWS IAM Identity Center,profile;api_keyis access keys), Azureoauthandentra(the same: Entra ID). OpenAI and Ollama take a key only.SetAuthandSetValue("llm.<provider>.auth")take them,blitz config set-auth <provider> <method>too (bedrock and azure included), and the runtime builds Gemini’soauthasadcand Azure’s asentra;blitz auth loginsigns in (spec_service_021 SVC-56). Every existing value keeps its meaning. - CFG-46
WriteSettingsFilereplaces a scope’s file with text only ifCheckSettings(CFG-48) finds no error; it returns its warnings, as “line N: …”, for settings the configuration doesn’t know (likely typos) and for API keys written as plain or obfuscated text.ReadSettingsFilereturns the text (""when there’s none). - CFG-47 The CLI:
blitz config permissions(list;allow|ask|deny <rule>,remove <rule>,defaults on|off|inherit,check <rule> [sample];ScopePermissions,AddPermissionRule,RemovePermissionRule,SetReadOnlyDefaults),blitz config keys,set-key <provider>(the key from stdin, without echo on a terminal),remove-key <provider>,secure-key <provider>,set-auth <provider> <api_key|adc|oauth>(--project,--location;--profile;keysshows the sign-in);--workspace(-w) works on the workspace’s settings. The service’sConfigService(spec_service_021) offers the same to the desktop app and reloads open workspaces. - CFG-48
CheckSettings(text)checks a settings file without saving it and returns its problems, by line, errors first on a line. Errors: TOML syntax (the parser’s line and column; an error at the end of the last line is reported on it), a value of the wrong type (<key>: incompatible types…, at its line), an invalid permission rule (at the first line holding it). Warnings: settings the configuration doesn’t know (at the line setting them, or their table’s header, following[table],[[array]]headers and dotted keys), API keys written as plain or obfuscated text, and skill policy settings it can’t honour (SkillPolicy.Problems, at[skills.policy]). Line 0 means the file as a whole. - CFG-49
Reference()lists every setting: each table and each leaf ofConfig, in declaration order, keyed as written in a file (llm.gemini.model;<agent>,<model>,<variable>, else<name>, for map keys;name[]for arrays of tables), with its type, its default fromDefaultConfigas TOML (the home directory as~; none for API keys, which may come from the environment, nor for maps and arrays of tables) and its description: the doc comment of its field, else the field’s line comment, else the comment of the group of fields it is in (no blank line between; not for tables), with the table type’s own doc comment added for tables. The descriptions are read from the declaring files, embedded in the binary (config.go,features.go,modelsettings.go), so they can’t drift from the code; a test fails if any setting has none.