Working with sensitive data¶
nah protects obvious sensitive access immediately. Taint tracking and session provenance handle the harder cases: what happens after sensitive data was read, and what happens after the agent wrote code or files.
Start in audit mode. It records what policy would have applied without changing runtime decisions.
Start with immediate protection¶
nah already checks sensitive paths, written content, and dangerous command composition before a tool call runs.
For most teams, the first shared project rule is small:
# .nah.yaml
sensitive_paths:
.env: ask
.env.production: block
terraform.tfvars: block
actions:
git_history_rewrite: block
db_write: ask
network_write: ask
This catches direct reads and writes. For example:
nah test "cat .env"
nah test "git push --force"
nah test --tool Read --path .env.production
Use Sensitive paths for the full path configuration reference, and Content inspection for write-content patterns.
Enable audit mode¶
Taint and provenance are session-level layers. Enable them in your personal global config first:
# ~/.config/nah/config.yaml
taint:
mode: audit
provenance:
mode: audit
Then run normal guarded sessions:
nah run claude
nah run codex
Inspect what nah observed:
nah config show
nah log
nah log --asks
nah log --blocks
Audit mode is the right first step because it shows the real workflow and the policies that would have applied before you add friction.
Track sensitive reads with taint¶
Taint tracking remembers successful reads from sensitive sources. Later, if the same session executes code, contacts a network, writes to a database, pushes to git, opens browser state, or uses another boundary action, nah can add review.
Add labels for the data classes you actually care about:
# ~/.config/nah/config.yaml
taint:
mode: audit
sources:
- paths: [".env*", "secrets/**", "config/prod/**"]
labels: [secret, prod_config]
- paths: ["customers/**/*.csv"]
labels: [customer_data]
policies:
default:
activation: audit
boundary: ask
unknown: ask
secret:
activation: audit
boundary: ask
git_remote_write: block
customer_data:
activation: ask
boundary: block
unknown: ask
Use labels that match your operational language. secret, prod_config, and
customer_data are easier to review in logs than one generic sensitive bucket.
A blocked source access does not taint the session. nah only tracks sources that were allowed and, for runtimes with post-tool hooks, confirmed as executed.
See Taint tracking for propagation, category, and policy details.
Review agent-written code with provenance¶
Session provenance tracks files and repo state written during the guarded run. It is useful for agent workflows where the risky moment is not the write, but the later execution or externalization of what the agent wrote.
Start with context review for activation and selected boundary actions:
# ~/.config/nah/config.yaml
provenance:
mode: audit
policies:
activation: context
boundary: ask
lang_exec: context
package_run: context
git_remote_write: context
git_history_rewrite: block
network_write: block
db_write: block
With that policy, a guarded run can write files normally in audit mode. When it later tries to run the generated script, run a package command in the changed repo, push to git, or perform a write-shaped network action, nah records the provenance policy that would have applied.
context does not mean automatic allow. It means nah builds a bounded
same-session delta for review when provenance is enforcing. If the context is
incomplete, the reviewer is unavailable, or the reviewer is uncertain, the
decision remains a human review.
See Session provenance for review limits, LLM behavior, and runtime details.
Choose activation and boundary policy¶
Use activation policy for actions that execute code or agent behavior inside the current environment. Common examples are language execution, package scripts, and local agent execution.
Use boundary policy for actions where data, code, state, or effects leave the current controlled environment. Common examples are network writes, git remote writes, database writes, service changes, containers, browser state, and remote agent execution.
Good starting defaults:
taint:
mode: audit
policies:
default:
activation: audit
boundary: ask
unknown: ask
provenance:
mode: audit
policies:
activation: context
boundary: ask
Then tighten specific labels or action types when the logs show a real risk:
taint:
policies:
customer_data:
boundary: block
provenance:
policies:
network_write: block
db_write: block
Keep trust in the right file¶
Use project config for rules everyone on the repo should share:
# .nah.yaml
sensitive_paths:
.env.production: block
actions:
git_history_rewrite: block
db_write: ask
Use global config for machine-specific trust and richer data-flow policy:
# ~/.config/nah/config.yaml
trusted_paths:
- ~/work/shared-scratch
db_targets:
- host: localhost
database: dev_app
schemas:
- public
llm:
mode: on
providers: [openrouter]
openrouter:
key_env: OPENROUTER_API_KEY
taint:
mode: audit
provenance:
mode: audit
Project .nah.yaml files can tighten policy by default. Do not put personal
trusted paths, provider keys, registries, or local database targets in the repo.
If you want a repo to define richer project-specific policy, review it first and then trust the project:
nah trust-project
See Using nah as a team for the full project vs global config workflow.
Move from audit to enforce¶
After a few real sessions, inspect the log:
nah log --asks
nah log --blocks
nah log --json
Move only the workflows you understand from audit mode to enforce mode:
taint:
mode: enforce
policies:
default:
activation: audit
boundary: ask
unknown: ask
customer_data:
activation: ask
boundary: block
provenance:
mode: enforce
policies:
activation: context
boundary: ask
network_write: block
db_write: block
For unattended agents, set an ask fallback so unresolved reviews fail closed:
targets:
codex:
ask_fallback: block
claude:
ask_fallback: block
Use presets when you want a temporary stricter mode instead of a permanent global default:
presets:
sensitive-work:
taint:
mode: enforce
provenance:
mode: enforce
targets:
codex:
ask_fallback: block
Run with the preset:
nah run codex --preset sensitive-work
nah run claude --preset sensitive-work
Verify the rollout¶
Check the effective config:
nah config path
nah config show
nah config show --preset sensitive-work
Run a few dry-run classifications for immediate policy:
nah test "cat .env"
nah test "git push origin main"
nah test --tool Read --path .env.production
Then verify session-level behavior from real guarded sessions:
nah log
nah log --asks
nah log --blocks
nah log --llm
For teams, start with audit mode and a small .nah.yaml. Add labels and
enforcement only after the log shows repeated, understandable patterns.
Limitations¶
Taint tracking is not byte-level data-flow analysis. It records session labels, target identities, propagation, and later sink actions.
Session provenance is not a proof that generated code is safe. It reviews the bounded same-session delta before later activation or boundary actions.
Terminal Guard taint is audit-only in v1. Codex enforcement depends on the local interactive hook surface. Claude Code and Codex both rely on the runtime events they expose to nah.
Keep normal least-privilege practices in place. nah reduces accidental and agent-driven unsafe flows; it does not replace secrets hygiene, scoped credentials, test databases, or review of production deploy paths.