Core concepts
August 12, 2026 · View on GitHub
Effects and coverage
nah lowers visible tool calls into typed invocation, filesystem, Git, network, and system-state effects. Observation resolves cwd, roots, paths, and environment.
Coverage is full when every guard-relevant visible input is preserved; it does
not mean nah understands an opaque program. Unresolved arguments, code, or
fields are partial.
For Bash, nah parses pipelines, control flow, subshells, and redirects into stages and data-flow edges. Unresolved shell state makes the stream partial.
Visible source remains a code-execution effect. Nah's language component is a
bounded Python and JavaScript/TypeScript effect interpreter: maintained grammars
lower to owned HIRs and follow supported runtime semantics without
running code. IPython preprocesses magics. TypeScript and TSX ignore reviewed
type-only syntax and use JavaScript runtime semantics; nah neither type-checks
nor implements the full TypeScript compilation pipeline. Other languages use
narrower reviewed detectors.
Profiles own only proven Node, Deno, Bun, OpenClaw QuickJS, or Prime current-cell APIs. Rebinding or visible mutation removes ownership; hidden state is unknown. Generic JavaScript owns none.
Exact child argv and cwd are nested; missing or non-directory cwd prevents the
child. Unawaited JavaScript applies state only through its first await.
Deno.Command reads options and cwd when consumed.
Only proven Bash enters full Bash lowering. sh gets a portable subset;
dialect-sensitive state, redirects, Windows/custom shells, Bun's $, and
bun exec stay partial. Sinks vanish only when proven to throw first.
From each interpreted source, at most 64 modeled language calls enter the public
ActionStream for custom guards, dry-run JSON, and records. Saturation makes
coverage partial. Built-ins continue on a per-source language-safety projection
capped at 256 calls and 4,096 flows, so later modeled danger can still block.
Fail-closed records these bounds as language-call-limit or
language-safety-limit analysis refusals.
Verdicts and failures
block— an active guard or structural self-protection found definite danger.delegate— nothing blocked; the runtime keeps control.
Evaluation failure is diagnostic, not a third verdict. By default it adds no
finding. --fail-closed blocks explicit failures/refusals, not ordinary
uncertainty. See nah docs security.
nah never approves. Delegation returns control to the runtime's permission or execution behavior; nah is neither an approval UI nor a sandbox.
Guards
A guard blocks a narrow danger such as remote content flowing into execution, destructive Git, or sensitive-path access. Guards compose by union: any may block, and none may approve.
An activated custom guard answers block or abstain. Abstain is no finding,
not approval. Failure or invalid output adds a typed failure only.
Definite evidence may block a partial stream; uncertainty alone never blocks.
Run nah docs guards for the catalog and tested examples.
Trust and activation
User guards require activation. Project guards require trust plus activation; nah does not read manifests before trust. Activation pins the manifest, executable, and data. Changed or missing bytes do not run and add a failure.
Before trust, .nah/project.toml may enable built-ins but cannot disable guards
or execute code. Agents may edit inert proposals; a human performs trust and
activation out of band. nah blocks understood intercepted attempts to cross
that boundary or disable active wiring.
nah nap starts a 10-minute, user-global maintenance window: plain nap pauses
self-protection; --all pauses every non-permanent layer. Nap-state protection
remains. See nah docs configuration and nah docs security.
Audit records
Live decisions attempt a redacted audit append; failure does not change the
verdict. Records name the runtime (unknown for nah decide). nah why <id>
explains one; nah log lists recent decisions, --blocked lists blocks, and
--json emits JSON Lines.