Releases: manuelschipper/nah
Releases · manuelschipper/nah
v0.9.0
Added
- Guarded Codex headless exec —
nah run codex execandnah run codex e
now guard local headless Codex runs with deterministic PreToolUse
enforcement, block unresolved asks by default, disable unsupported headless
tool surfaces, and log headless fallback/sandbox metadata. (nah-936) - First-class Nix packaging — added
default.nix, a flake, and Nix CI so
users can install the fullnahCLI with YAML config and OS keyring support
throughnix profile add github:manuelschipper/nah. Nix and
pip install "nah[config,keys]"are now peer recommended CLI install paths;
the Claude plugin remains a separate Claude-only distribution. Inspired by
PR #82 fromryanswrt. (nah-937) - Trusted container exec unwrapping — opt-in
trusted_containersconfig can
makedocker exec,docker container exec, and simple
docker compose exectransparent for narrow read-like payloads inside exact
trusted identities, while writes, package scripts, network/database/container
actions, unknown tools, credential-marker payloads, and unsupported Docker
flags still ask or block. (nah-924) - Codex authority prompt routing —
nah run codexnow launches Codex with
approval_policy="untrusted"and installs a nah-managed
$CODEX_HOME/rules/nah-authority.rulesfile so Codex-known-safe command
prefixes such ascat,git,rg, and shell wrappers still route through
nah'sPermissionRequestclassifier before execution.nah codex setup
creates or refreshes this authority file,nah codex doctorinspects
approval-memory/MCP drift, andnah codex remove-setupremoves only the
nah-managed setup files. (nah-923) - Codex confirm-edits mode — safe project-local
apply_patchadd/update
edits now allow by default after nah path and content checks, while
nah run codex --confirm-editskeeps those safe edits on Codex's native
approval path for users who want edit confirmations. - Lang-exec LLM review for heredoc scripts — LLM script-veto prompts now
include the full inspected heredoc body for interpreter commands such as
python3 <<'PY', and the review policy now allows plainly read-only local
inspection scripts instead of escalating solely for ordinary config/log/state
reads. - Codex PreToolUse observation for taint tracking —
nah run codexnow
injects CodexPreToolUse,PermissionRequest, andPostToolUsehooks via
the canonicalfeatures.hooksflag. PreToolUse observes routine Bash, MCP,
andapply_patchcalls without LLM review so taint source reads can be
tracked before execution, while PermissionRequest remains the enforcement
hook and PostToolUse confirms execution outcomes. (nah-921) - Runtime-neutral session taint tracking — opt-in
taintmode now tracks
successful sensitive reads across Claude, Codex, and terminal guard sessions,
propagates labels to local writes/repo state, and can audit or enforce
activation/boundary policies without weakening existing nah decisions.
Defaults remain off; terminal guard taint support is audit-only in v1.
(nah-919) - Session provenance guard — opt-in
provenancemode now tracks
successful writes from guarded Claude/Codex runs and can pause later
activation or boundary actions when they operate over session-written files
or repo state.contextpolicies build a bounded session-delta packet for
LLM review; incomplete packets or uncertain reviews remain asks. (nah-929) - Runtime execution outcome logging — nah now records append-only
runtimeandexecutionmetadata for Claude, Codex, and terminal guard
decisions so audit logs can distinguish a pre-execution permission decision
from an observed tool outcome. Claude PostToolUse/PostToolUseFailure and
Codex PostToolUse hooks log successful or failed execution without changing
permission policy, while terminal prompts report denied or approved-to-run
states without claiming process completion. (nah-920) - Non-Git project config with exact-root trust — nah now loads
./.nah.yamlfrom the current directory outside Git while keeping Git-root
config precedence inside repositories. Project config remains tighten-only by
default;nah trust-project/nah untrust-projectmanage exact project
roots whose config may loosen policy and activate projectclassifyrules.
(nah-918)
Fixed
- Agent hook executable transport — Claude direct hooks and Codex hook
overrides now call the installednahexecutable instead of a raw Python
interpreter plus import path. This fixes Nix and wrapper-based installs where
the package is importable through thenahexecutable but not through the
bare interpreter.nah update claudemigrates old direct-hook settings even
when the old shim file is missing. Reported in #83
byryanswrt. (nah-943)
Changed
- Session provenance outside-project identity — session-written files
outside the current project boundary now stay direct-path-only in provenance
state instead of being aggregated under the current repo; exact path
activation can still trigger provenance review, but base outside-project
asks/blocks keep their authority. (nah-939) - Decision prompts no longer include auto-allow hints — ask/block output no
longer appends remediation suggestions such asnah trust,nah allow,
nah allow-path, ornah classify, because misleading shortcuts can loosen
policy in the wrong place. Friendly safety reasons and diagnostic metadata
remain. (nah-935) - Taint boundary sinks — taint tracking now treats network diagnostics,
database reads, browser interaction/navigation/exec, container actions, git
history rewrites, remote agent execution, and agent servers as boundary sinks
by default. Users can tune category membership with
taint.categories.*.add/remove.
Fixed
- Bazel test label classification — local
bazel testand
bazelisk testtarget labels now classify aspackage_run, so valid
Bazel labels such as//pkg:targetno longer pause as unknown commands
or get mistaken for filesystem paths. (#62) jqread-only classification —jqnow classifies as
filesystem_read, so JSON inspection pipelines such as
... --json | jq '.metadata'no longer pause as unknown commands while
sensitive-path reads still stay guarded.- Codex setup command surface —
nah codex setupnow backs up and fixes
supported Codex approval-memory/MCP drift, so the separate pre-v1
nah codex repaircommand has been removed. (nah-925) - YAML
llm.mode: on/offparsing — PyYAML parses unquotedonandoff
as booleans, so nah now accepts booleantrue/falseanywhere it reads LLM
mode, including target overrides and inline--configoverrides. - Codex lifecycle guidance — bare
nah install,nah update, and
nah uninstallnow explain that Codex is session-scoped through
nah run codex, andnah update codexreports that there is no persistent
Codex update target.
v0.8.3
Added
- HTTP and REST API intent classification — visible HTTP API calls now
classify by service intent: GET/HEAD/OPTIONS use context-resolved
service_read, POST/PUT/PATCH useservice_write, DELETE and destructive
paths useservice_destructive, and remote service actions still participate
in network data-flow blocks such ascurl ... | bash. (nah-910) - GraphQL operation intent classification — visible GraphQL operations now
classify by action intent instead of HTTP method alone: queries and
subscriptions use context-resolvedservice_read, mutations use
service_write, destructive mutation names/root fields use
service_destructive, and hidden or ambiguous documents stay on ask paths.
(nah-911) - JSON-RPC and MCP method intent classification — visible JSON-RPC request
bodies now classify by method intent before REST fallback: read-like methods
use context-resolvedservice_read, write-like methods useservice_write,
destructive methods useservice_destructive, and generic MCP tool
invocation stays on an ask path unless a separate trusted tool classifier
handles it. (nah-912) - gRPC CLI method intent classification — visible
grpcurlcalls now
classify by method intent: read-like methods and reflection verbs use
context-resolvedservice_read, write-like methods useservice_write,
destructive methods useservice_destructive, and missing or unknown
methods stay on ask paths. (nah-913) - WebSocket and Socket.IO event intent classification — visible
wscat
andwebsocatcommands now distinguish connection-only traffic from sends,
classify visible event names intoservice_read,service_write, or
service_destructive, parse simple visible Socket.IO42[...]event
packets, and keep opaque sends on ask paths. (nah-914) - SQLite read-only CLI classification — explicit read-only
sqlite3
inspection commands now classify asdb_readfor simpleSELECT, safe
EXPLAIN, safe PRAGMA introspection, and safe dot commands; bare SQLite,
script-fed SQL, mutating SQL, unsafe helpers, and ambiguous forms stay
db_write. (nah-916) - Postgres read-only CLI classification — explicit one-shot
psql
inspection commands now classify asdb_readwhen they set same-invocation
PGOPTIONStodefault_transaction_read_only, disable psql startup files,
and use a narrow read-only SQL allowlist; bare, script-fed, mutating, or
ambiguous Postgres commands stay on existingdb_writeask paths. (nah-bqe)
Fixed
- Package script argument boundary classification —
npm run <script> --,
pnpm run <script> --,bun run <script> --, and explicit package exec
payloads no longer treat child arguments such as-g,--global, or
--targetas package-manager global install flags; malformed or
package-owned global flags still ask. (nah-917) - Curl host extraction skips body and option values — curl/wget-style host
detection now ignores option values such as JSON bodies, config files, cert
paths, and headers before selecting the actual request URL. (nah-909)
v0.8.2
Changed
- Codex runtime simplified around workspace-write —
nah run codexnow
uses one protected local interactive preset: Codexworkspace-write,
on-request, user approvals, nahPermissionRequesthooks, and preflight.
Normal Codex UI flags still pass through, while sandbox/approval overrides
remain rejected because nah owns that safety boundary. (nah-908)
Removed
- Removed Codex flow/edit mode surface — removed nah-owned
--flow,
--auto-edits,--no-sandbox, explicit--sandbox, and hook-side safe
apply_patchauto-allow behavior fromnah run codex. Safe project edits
should flow through Codexworkspace-write; riskyapply_patchpermission
requests still ask or block after nah path/content checks. (nah-908)
v0.8.1
Changed
- Project license restored to MIT — current source snapshots and future
releases are MIT licensed again. The already-publishedv0.8.0package
metadata briefly reflected the short-lived license transition before this
reversal.
v0.8.0
Breaking
- Target-first install/update/uninstall commands —
nah install,nah update, andnah uninstallnow require an explicit target instead of defaulting to Claude Code. Usenah install claude,nah update claude, andnah uninstall claudefor direct Claude hooks; shell and provider targets use the same shape. Barenah installexits nonzero with a guided target list, and the old--agentlifecycle shape is no longer the documented product surface. (nah-882)
Added
- Human-friendly safety explanations — terminal guard prompts, Claude Code permission reasons,
nah test, and compactnah logoutput now show shortnah paused:/nah blocked:messages such as “this can rewrite Git history” while preserving the technicalreason, action type, hints, and JSON/log diagnostics alongside a newhuman_reasonfield. (nah-884) - Opt-in bash and zsh terminal guard — added
nah install bashandnah install zshto protect interactive shell sessions with managed rc-file snippets that classify complete single-line commands before execution. The guard supports status/doctor diagnostics, prompt-on-ask behavior, fail-closed handling for unsupported multiline/here-doc/continuation input, explicit bypass vianah-bypass <command>orNAH_TERMINAL_BYPASS=1, and terminal decision logging for blocks, denied asks, confirmed asks, bypasses, and errors while keeping allowed terminal commands out of the nah log by default. (nah-882) - Target-aware dry runs and config overrides — added
nah test --target <target>and--json, plus target-scoped config undertargets.<target>for runtime-specific policies. Bash and zsh targets default to LLM mode off even when a global provider is configured, unless explicitly enabled under their target override. (nah-882) - Codex native hook support — added
nah run codexfor local interactive Codex sessions using CodexPermissionRequesthooks instead of shell-wrapper prompts. The runner injects nah-owned hook, sandbox, approval, and dynamic-MCP-disabling overrides, rejects unsafe Codex launch modes, routes Bash and MCP permission requests through nah classification, and addsnah codex doctor/nah codex repairto block and repair remembered Codex approvals or MCP approval modes that could bypass nah. (nah-897, nah-898) - Guarded Codex edit auto-allow — added
nah run codex --auto-editsto auto-allow safe project-localapply_patchadd/update edits after nah path, content, and LLM checks, while default sessions still ask and dangerous patches remain blocked or ask-only. (nah-904) - Codex flow mode — added
nah run codex --flowas a shortcut for no Codex filesystem sandbox plus safeapply_patchauto-allow. Use--no-sandboxseparately when you want to remove the Codex sandbox without also auto-accepting edits. (nah-906) - MCP threat-model audit coverage — added
mcp_permissionstonah audit-threat-model, covering core MCP permission/runtime tests for Claude Code matcher registration, global-only MCP classification, wildcard safety, database/browser MCP action typing, Codex MCP PermissionRequest hooks, and Codex MCP approval-mode preflight/repair. README and public docs now report 1,807 category coverage hits across 13 tested danger classes. (nah-905) - OS keyring-backed LLM secrets — added
nah key set,nah key status,nah key import-env, andnah key rmfor storing optional LLM
provider keys outside the process environment. Keyring support remains an
optionalkeysextra, with env-var fallback for existing setups. Thanks
@ZhangJiaLong90524 for the initial PR and direction.
(#65, nah-889)
Changed
- License transition for future releases — future source snapshots and
releases are fair-source under FSL-1.1-MIT and convert to MIT two years after
they are made available. Versions and source snapshots made available under
MIT before this transition, includingv0.7.1and earlier tagged releases,
remain MIT. - Agent session launchers use
nah run— the one-shot Claude Code launcher
is nownah run claude, matchingnah run codex. Legacynah claudeexits
with a pointer to the new command. - README and public docs rebranded for coding agents — refreshed README, site install/CLI/privacy/how-it-works/getting-started docs, and package metadata around “Context aware safety guard for coding agents” instead of a Claude-only positioning, with the user terminal guard treated as an optional bonus. Updated examples to current
nah paused:/nah blocked:copy and documented Codex doctor/repair plusnah log --llm. (nah-899) - Terminal guard documented as opt-in shell protection — README, install docs, configuration docs, privacy copy, and lifecycle target help now describe bash/zsh terminal protection as opt-in per shell without preview labeling.
- Minimal taxonomy profile deprecated —
profile: minimalnow warns and behaves likeprofile: full, leavingfullandnoneas the two supported profile shapes. The old minimal classification data has been removed so custom classifiers are not evaluated against a weaker built-in baseline. - Install lifecycle targets stay runtime-only — removed the unreleased
nah install openrouter/nah uninstall openrouterconvenience path so lifecycle commands only install or remove nah from guarded runtimes (claude,bash,zsh). LLM providers remain configured through global config. (nah-882 follow-up) - Install docs now start with a chooser — README and site install docs now separate the Claude Code plugin, PyPI CLI/direct-hook, terminal guard, config extra, and LLM provider configuration more clearly, and stale setup examples now use explicit target-first lifecycle commands instead of bare
nah install. (nah-885)
Fixed
- Windows drive-letter paths route through trusted paths —
nah trust C:/Projectsandnah trust D:\worknow write totrusted_pathsinstead of
being mistaken for network hosts inknown_registries. Thanks @enermark.
(#69) - Sparse environments keep useful log user attribution — structured
decision logs now fall back fromUSERtoLOGNAME,USERNAME, and finally
getpass.getuser()before leaving the user field empty. - Codex flow edit approval race — safe
apply_patchedits innah run codex --flownow trust direct Codex patch payloads and retry the Codex transcript briefly when direct patch text is unavailable, fixing cases where the PermissionRequest hook fell back to the native edit prompt before nah could inspect the patch. (nah-907) - Quoted output text no longer becomes a fake redirect — Bash classification
now preserves>characters that appear inside quoted or backslash-escaped
printf/echotext before redirect decomposition, so prose such as
<key>or->no longer prompts as a filesystem write while real unquoted
redirects to sensitive paths still block. (nah-902) - Claude launcher rejects unsafe bypass modes —
nah run claudenow refuses
--dangerously-skip-permissions,--enable-auto-mode, and
--permission-mode bypassPermissionsbecause those modes can run tool calls
outside the guarded permission path. - Shell control-flow bodies are classified by payload —
for ...; do ...; done,while ...; do ...; done, andif ...; then ...; finow expose their executable inner commands to the classifier instead of stopping at reserved words likefor,do, anddone. Literalforitem lists are expanded into the loop body so safe batch GitHub/GitLab CLI API reads can allow while sensitive paths still block; dynamic loop values and control-flow body command substitutions fail closed. Tracks #78. - Unwrapped shell bodies mirror top-level variable expansion —
bash -c 'BAD=/etc/shadow; rm "$BAD"'and control-flow variants now apply the same intra-chain$VARexpansion used for top-level Bash commands, closing a sensitive-path bypass inside shell wrappers. - GitLab API form writes ask cleanly —
glab api --form ...now classifies asnetwork_write, including multipart file-upload forms, andgh/glabAPI prompt copy no longer mistakes field values such asfile=@image.pngfor network hosts. - Bash terminal ask denials clear the prompt line — denied ask decisions now cancel and return to an empty prompt instead of restoring the same command line and making the shell look stuck. (nah-882 follow-up)
- Terminal guard reload hint replaces active snippets —
nah install bash/nah update bashnow print a reload command that clears terminal guard environment and replaces the shell, so an already-running shell can load the updated guard without running startup files inside the old in-memory Readline hook. (nah-882 follow-up) - Bash terminal guard preserves normal prompt redraws — bash now filters the Readline buffer and lets Bash execute accepted commands normally, instead of running commands inside the Readline callback. Confirmed commands run through normal Bash execution, preserving shell state such as
cdandsource. (nah-882 follow-up) nah updateno longer looks like a project file write —nah install/nah updatenow classify as nah lifecycle commands instead of treating target names such asbashorupdateas filesystem paths like~/bashor~/updatewhen the terminal guard runs outside a Git project. (nah-882 follow-up)- Bash rc reloads replace the active guard — sourcing
.bashrcin an already-guarded bash shell now refreshes nah's active function and key bindings instead of skipping the snippet becauseNAH_TERMINAL_GUARD_ACTIVEwas already set. The original pre-nah binding metadata is still captured only once for diagnostics. (nah-882 follow-up) - Bash ask prompts use the hidden decision helper — bash ask d...
v0.7.1
Added
- Official Claude plugin marketplace submission prep — polished the generated Claude Code plugin metadata with repository, author URL, and discovery keywords, rewrote the copied plugin README so release artifacts no longer describe themselves as local-only scaffolds, and added an Anthropic marketplace submission packet covering source refs, runtime behavior, data handling, trust/safety notes, and validation commands. Tests now assert the generated plugin and marketplace artifacts keep this submission-ready metadata and avoid regressing to local-only wording. (nah-883)
v0.7.0
Added
- Local Claude Code plugin distribution prep — added a local-only plugin scaffold and
scripts/build_claude_plugin.pyartifact builder that copies the stdlib-only nah runtime into an ignoreddist/claude-plugin/nahdirectory, generates Claude hook matchers from the canonical agent matcher list, and keeps hook commands rooted at${CLAUDE_PLUGIN_ROOT}with no package-manager or network bootstrap. CLI install/update/uninstall/nah claudepaths now detect enabled nah plugins, avoid silent mixed plugin/direct-hook setups, and preserve plugin-managed state during uninstall. (nah-879) - Claude Code plugin marketplace artifact generation —
scripts/build_claude_plugin.pycan now generate and freshness-check a full self-hosted marketplace root atdist/claude-marketplace, with.claude-plugin/marketplace.jsonpointing to the generated self-containedplugins/nahartifact instead of the source template. Added plugin beta install docs covering opt-in plugin mode, legacy direct-hook migration, rollback, and the intentional absence of bundled PyYAML. (nah-880) - Automated Claude plugin marketplace releases — the existing tagged release workflow now builds, freshness-checks, release-verifies, and Claude-validates the generated marketplace tree before publishing PyPI, then publishes that exact marketplace artifact to the
claude-marketplacebranch with immutableclaude-plugin-vX.Y.Ztags. Added release scripts and tests so plugin metadata, source tag, package version, changelog, and bundled runtime cannot silently drift. (nah-881)
v0.6.4
Fixed
- Conservative kubectl read classification with global flag support —
kubectl -n <ns> logs ...,kubectl --namespace=<ns> get pods, and other known low-risk Kubernetes inspection commands now classify ascontainer_readinstead of falling through tounknown. The classifier strips recognized kubectl global flags before matching subcommands, while malformed flags, mutations, exec/copy/port-forward paths, detailed object dumps (-o yaml/json), secrets, configmaps, service accounts, and custom resources remain on theunknownask path. Tracks #67, superseding the broad prefix-table approach from #51 and the global-flag stripping branch #68. - Explicit-delimiter
misewrappers preserve payload classification —mise exec -- <cmd>,mise x -- <cmd>, andmise watch -- <cmd>now classify and resolve context from the command after--, so safe Git/GitHub CLI reads allow, script and inline-code inspection use the inner payload, and unknown tools launched throughmisestill ask. Redirected literal content is inspected through the wrapper while preserving the outer redirect target guard. (nah-878) - GitHub CLI API reads no longer look like script execution —
gh api ...now uses a full-profile flag classifier instead of the genericlang_exectable entry, so read-only API calls such asgh api repos/owner/repo/contributors --jq lengthclassify asgit_safeand no longer ask withscript not found: .../api. POST-like methods, request bodies, implicit POST field flags, typed--field key=@filepayloads, and--inputstay on the existingnetwork_writeask path, whilegh extension execremainslang_exec. (nah-32c) - Direct script arguments no longer resolve as script paths —
nahnow treatstokens[0]as the inspected script for direct script invocations such as./bin/release.sh 2.0.0 prerelease --label rc, instead of scanning positional arguments and asking onscript not found: <project>/2.0.0. Missing direct scripts still fail closed, but the prompt now names the missing script rather than the first argument. Reported in #70; PR behavior integrated from #72 by @srgvg. (nah-877) - Windows hook shim and update compatibility — the generated
nah_guard.pyshim now includes an explicit UTF-8 source cookie and treats old non-UTF-8 hook files as stale during update, rewriting them safely instead of crashing while checking for identical content.nah updatenow handles both current string-style Claude hook matchers and legacy object-style{"tool_name": [...]}matchers, preserves object-style entries when present, and creates a missinghooks.PreToolUselist before adding new tool matchers. Reported in #58 by @zacbrown.
v0.6.3
Added
- Wildcard support in
classifyentries — classify entries now accept a trailing*wildcard on the last token.mcp__github*matches every tool under the github MCP server, letting one line cover a whole MCP server instead of enumerating each tool. Exact entries always beat wildcard entries at equal prefix length, so a specific override still wins over a server-wide rule. Invalid patterns (leading*, mid-string*, bare*, multi-*) are rejected atnah classifywrite time and skipped with a stderr warning if they appear in hand-edited YAML. FD-024 semantics — implicit prefix matching remains forbidden, wildcards must be written explicitly — are preserved. Requested in #76 (nah-875)
Fixed
- Atomic config writes —
_write_configinsrc/nah/remember.pynow writes to a sibling temp file andos.replaces it over the target. Previously it calledopen(path, "w")which truncates the file to zero bytes before writing; concurrent Claude Code sessions calling_read_configduring that window could observe an empty file, parse it as{}, and later persist a single rule as the whole config — a full config wipe was reported in production. The fix resolves symlinks on the target (preserving dotfile-managed links), preserves the file's existing mode (or defaults to0o644), writes with explicit UTF-8 encoding, fsyncs the tempfile before rename, and fsyncs the parent directory on POSIX as a durability hedge. All six_write_configcall sites (write_action,write_classify,write_trust_host,write_allow_path, etc.) inherit the fix without modification. Lost-update races where two writers both persist stale state are explicitly deferred — that requires advisory file locking. Reported by @0reo (#66, nah-876) - Intra-chain
$VARexpansion before sensitive-path checks — Bash classification now propagates literal env assignments across&&/||/;stages and expands$NAME/${NAME}in later consumer tokens, soBAD=/etc/shadow && cat "$BAD"blocks where it previously allowed. Pipe|clears the var map (subshell boundary); unsafe RHS values ($, backticks, command substitution) are never propagated; the executed command string is never mutated. Covers bare andexport NAME=valueassignment forms. Bypass identified by srgvg (#74, nah-874)
v0.6.2
Added
- Default-config dry runs —
nah test --defaultsnow ignores user/project config and uses packaged defaults for one dry-run classification, keeping/nah-demobase battery results stable under customized local configs while preserving--configfor explicit variants (nah-jpv)
Fixed
find -execshell-wrapper classification — Bash classification now unwrapsfind -exec/-execdir/-ok/-okdirpayloads through the same inner-command pipeline as directsh -candbash -lc, so hidden network access andcurl | shcomposition no longer collapse to project-local filesystem paths while safe grep and project-local cleanup still allow (#52, nah-871)- Shell comment prefix bypass — Bash command classification now treats top-level newlines as command separators and strips shell comments before per-stage tokenization, so comment-prefixed commands such as
# note\ncat /etc/shadowno longer collapse toALLOW/empty commandwhile quoted hashes and heredoc content remain intact (#71, nah-870)