Configuration Overview¶
nah works out of the box with zero config. When you want to tune it, configuration lives in two places.
File locations¶
| Scope | Path | Purpose |
|---|---|---|
| Global | ~/.config/nah/config.yaml |
Your personal preferences, trusted paths, LLM setup |
| Project | .nah.yaml (in git root) |
Per-project tightening, custom classifications |
nah config path # show both paths
nah config show # display effective merged config
Global vs project scope¶
Global config can do everything -- override policies, add trusted paths, configure LLM, modify safety lists.
Project config can only tighten security by default. It can:
- Add classify entries (commands → action types)
- Escalate action policies (e.g.,
git_write: ask) - Tighten content pattern policies (ask → block)
- Add target-scoped tightening under
targets.<target>
It cannot:
- Relax any policy (lowering strictness is rejected)
- Modify safety lists (
known_registries,exec_sinks, etc.) - Set
trusted_paths,allow_paths, ordb_targets - Configure provider credentials or the global LLM provider cascade
- Change the taxonomy profile
This is the supply-chain safety model: a malicious repo's .nah.yaml can't weaken your protections.
You can explicitly opt out of this model by setting trust_project_config: true
in global config. Only use that for repositories whose .nah.yaml you already
trust, because project config can then loosen policies.
Merge rules¶
When both configs exist, nah merges them with these rules:
| Field | Merge behavior |
|---|---|
profile |
Global only |
trust_project_config |
Global only; when true, project config can loosen policy |
actions |
Tighten-only (project can only escalate strictness) |
classify |
Kept separate (global = Phase 1, project = Phase 3 lookup; project can only tighten overlaps unless trusted) |
sensitive_paths |
Tighten-only unless project config is trusted |
sensitive_basenames |
Global only |
content_patterns |
Project can tighten policies only (add/suppress global-only) |
credential_patterns |
Global only |
known_registries |
Global only |
exec_sinks |
Global only |
decode_commands |
Global only |
trusted_paths |
Global only |
allow_paths |
Global only |
db_targets |
Global only |
llm |
Global only |
targets |
Global can override; project can only tighten unless trusted |
log |
Global only |
active_allow |
Global only |
Quick reference — all config keys¶
| Key | Type | Scope | Docs |
|---|---|---|---|
profile |
full / none |
global | Profiles |
trust_project_config |
bool | global | This page |
classify |
dict of type → prefix list | both* | Custom taxonomy |
actions |
dict of type → policy | both | Action types |
sensitive_paths_default |
ask / block |
both* | Sensitive paths |
sensitive_paths |
dict of path → policy | both | Sensitive paths |
allow_paths |
dict of path → project list | global | Sensitive paths |
trusted_paths |
list of paths | global | Sensitive paths |
known_registries |
list or dict (add/remove) | global | Safety lists |
exec_sinks |
list or dict (add/remove) | global | Safety lists |
sensitive_basenames |
dict of name → policy | global | Safety lists |
decode_commands |
list or dict (add/remove) | global | Safety lists |
content_patterns |
dict (add/suppress) | both | Content inspection |
credential_patterns |
dict (add/suppress) | global | Content inspection |
llm |
dict (mode, providers, eligible, context_chars) |
global | LLM layer |
targets |
dict of target → overrides | both* | This page |
db_targets |
list of database/schema dicts | global | Database targets |
log |
dict (verbosity, etc.) | global | CLI reference |
active_allow |
true, false, or list of tool names |
global | Install |
* classify entries in global config are Phase 1 (checked first, can override built-in). Project entries are Phase 3: they can add new commands and can tighten overlapping built-in classifications, but cannot weaken them unless trust_project_config: true is set globally. sensitive_paths_default in project config can only tighten (ask → block) unless project config is trusted. Target-scoped project overrides follow the same tighten-only rule.
Target overrides¶
Use targets.<target> when a runtime needs different policy from the shared
default. Supported targets are claude, plus the beta terminal-guard targets
bash and zsh.
# ~/.config/nah/config.yaml
actions:
lang_exec: context
git_remote_write: ask
llm:
mode: on
providers: [openrouter]
openrouter:
key_env: OPENROUTER_API_KEY
model: google/gemini-3.1-flash-lite-preview
targets:
claude:
llm:
mode: on
bash:
actions:
network_outbound: ask
llm:
mode: off
terminal:
bypass_env: NAH_TERMINAL_BYPASS
zsh:
actions:
network_outbound: ask
llm:
mode: off
Target overrides can set actions, sensitive_paths_default,
sensitive_paths, content_patterns.policies, and llm.mode /
llm.eligible. Shell-specific options live under targets.bash.terminal and
targets.zsh.terminal.
Bash and zsh are beta terminal-guard targets. They default to LLM mode off even
when global LLM mode is on. Enable terminal LLM review only with an explicit
target override such as
targets.bash.llm.mode: on.
Provider credentials and provider selection stay global-only. Configure LLM
providers directly in global config and store environment-variable names such
as llm.openrouter.key_env, not raw API keys.
YAML format¶
Both config files use standard YAML. If nah detects comments in a file before a CLI write operation (nah allow, nah classify, etc.), it warns you that comments will be removed and asks for confirmation.
Optional dependency: pip install "nah[config]" installs pyyaml. The default
install keeps nah's core hook/classifier stdlib-only for users who want the
smallest supply-chain surface. Install the config extra when you want YAML config
files or commands that write config (nah allow, nah deny, nah classify,
nah trust). With pipx, use pipx inject nah pyyaml.