Air traffic control for AI coding

Governance: authority, evidence and audit

Decide who may change what, set one policy floor for every repository, record signed evidence on every landing, and show afterwards, from git history alone, which rules every change passed.

  • Signed editor, approver, admin and checker grants, from your identity provider with nool login.
  • [workspace.policy]: an organisation floor every repository can tighten but never weaken.
  • nool policy stats: how often each gate blocks and how many changes pass first time.
  • nool audit verify: PASS/FAIL per control for SOC 2, PCI DSS, HIPAA, GDPR, SOX ITGC and ISO 27001.
nool audit verify --framework soc2

Who may change what: role-gated grants

Once a replica trusts an issuer (`[governance] issuer_keys`, or the organisation keys in an operator-issued licence), it enforces signed grants held in the DAG. After the first grant lands, a file change needs an `editor` or `planner` grant whose scope covers every path, `nool approve` needs `approver`, `nool steer --role <r>` needs `steer:<r>`, and `nool.toml`, the policy set, plugins and governance packs need `admin`. The seal checks, and so does every replica that pulls. With no issuer configured nothing changes, and `nool doctor` notes `GOVERNANCE_UNDECLARED`.

  • Roles: admin, approver, editor, planner, reviewer, coordinator, releaser, auditor, steer:<role> and checker:<assertion>.
  • `checker:<assertion>` makes a key a trusted checker for that assertion; it never stands in for human approval.
  • `nool login [--hub <url>]` signs in through your identity provider (device flow) and lands short-lived grants mapped from its groups; `nool propose` renews them before they expire.
nool admin team add --id <member-pubkey> --role editor --scope 'src/**'
nool admin team add --id <ci-pubkey> --role checker:unit-tests
nool admin team list
nool admin revoke <grant-id>

One organisation, many repositories

A `workspace.toml` above your repositories is the anchor for all of them. `[workspace.governance]` names the organisation and its issuer keys, and `[workspace.policy]` sets the organisation floor: `forbidden_paths` and a minimum completion check. Every repository beneath works under the floor at the seal, at `pre-tool` and in every `nool code` session. A repository can tighten it, never weaken it: a `nool.toml` that asks for less is refused at propose, and `nool doctor` reports `ORG_POLICY_WEAKENED`. The file is honoured only when it belongs to the repository's owner and is not writable by every account.

  • At a workspace root, `nool admin team add` signs one grant and lands it in every repository, and `nool admin revoke <key>` revokes every grant of that key.
  • `nool workspace doctor --recursive` names repositories whose grants are for another organisation; `nool workspace status --hub` reads the hub's organisation rollup (Enterprise).
# workspace.toml
[workspace.governance]
org_id = "acme"
issuer_keys = ["<hex ed25519 key>"]

[workspace.policy]
forbidden_paths = ["secrets/**", "*.pem"]

[workspace.policy.completion]
red_green = "required"

Search across the organisation

`nool query search` searches this repository by default. With a hub configured, `--scope org` searches the hub's index of every repository in the organisation instead. As of 7.24.0 it matches each query term as a prefix and ranks by relevance, so it finds what local search finds: on a labelled set of 25 queries, recall at 5 rose from 0.37 to 0.88. `[hub] search_scope = "org"` makes the organisation the default; an explicit `--scope` always wins.

  • If the hub is unreachable, an explicit `--scope org` exits with the hub's error, while a `search_scope = "org"` default falls back to local search with a warning.
  • `--fuzzy` is not available with `--scope org`; the hub ranks with Postgres full-text search.
nool query search "webhook retry policy" --scope org --limit 10

# nool.toml
[hub]
search_scope = "org"

Configuration changes land alone

A knot that changes `nool.toml` or `workspace.toml` changes nothing else and carries a typed justification, or propose and solidify refuse it with `CONFIG_CHANGE`. A policy pin may land together with the policy files it names. Every governance change is therefore its own reviewable, attributable record.

nool propose --path nool.toml --intent "Require red/green tests" --justification "Q4 quality floor" --solidify

Pin the policy set every replica runs

Installing, uninstalling or pinning a policy lands the policy files and `nool.toml` as one knot, so every replica gets the policy with it (`--no-land` keeps the change in the working tree; `nool admin plugin land` lands a rule file written by hand). `nool admin plugin pin` writes every policy file and its hash to `[aram] policies`: once landed, the gate starts only when the policy directory holds exactly that set, and a clone refuses a knot that does not match. `[policy_bundle]` goes further and resolves a signed, content-addressed bundle before evaluation, failing closed when it cannot.

  • When the working tree's policies differ from the ones landed at HEAD, the landed set runs as well and its denial stands: moving a policy aside does not let a change through.
  • A policy that could not run is recorded Unverifiable, never a pass.
  • A policy's manifest is written under the module's own name (plugin `a-b` builds `a_b.wasm`, so `a_b.toml`). Policies with a hyphenated name installed before 7.24.0 could be refused under declared governance: reinstall them.
nool admin plugin install policies/no-secrets.yaml --policy
nool admin plugin pin

Signed evidence on every landing

Each governed seal leaves a signed gate receipt on the writer's detached chain with every policy's own result, the policy-set digest, the evidence it read and the lease the seal held. A refused proposal leaves one too, naming the refused payload by hash without its content. Attestations from CI, reviewers and fleets (a fleet's reviewer verdicts are attested as `fleet-review`) replicate with their evidence bodies, so a clone can read them.

nool evidence knot <knot-id>
nool evidence knot <knot-id> --format acs

Do the tests constrain the change?

A passing test run proves little if the tests would pass whatever the code did. After a full-mode run passes, `nool propose` re-runs it on a few one-line bugs in the changed lines: a comparison inverted, `&&` and `||` swapped, a boolean flipped, a count off by one, a branch swapped or a return value zeroed. The seal records a `tests_constrain_change` attestation: `pass` when the tests caught every mutant, `fail` with each survivor's file, line and operator, or `error` when nothing could be mutated or the budget ran out. It is never a pass in those cases. Rust, Go, Python, TypeScript and JavaScript are covered.

  • With the obligation set and `enforce = "block"`, a push whose tests could not fail is refused, naming the survivors.
  • `nool attest show <knot>` and `nool evidence knot <knot>` print a failure's detail.
  • This runs at propose on every governed change. `mutation` in `[code.completion]` is the separate check a `nool code` session runs before it declares a goal done.
[reification]
max_mutants = 3              # one ghost run each
mutation_budget_secs = 300

[attestation.obligations]
"src/**" = ["tests_constrain_change"]

Working alone: labelled self-approval

A one-person repository has no second person to clear a checkpoint or an approval request, and a second local key posing as a reviewer would fabricate separation of duties. With `[approval] self_approval = "labelled"` landed, `nool approve` and `nool steer` on your own change are recorded instead of refused, carrying a self-approval marker inside the signed payload. Replicas count such a decision only while the setting stays landed, and only for decisions made after it landed. The default, `false`, refuses the author's own decision and drops it on read.

  • Turning it on is a `nool.toml` change, so it lands alone with a justification.
  • `nool doctor` reports `SELF_APPROVAL_ENABLED` while the setting is landed, with the number of decisions that used it.
  • `nool audit verify` records each one as a `self_approved` fact, and a control that requires independent review reports it unmet.
  • The signer must still hold the role: a reviewer key, or a grant under declared governance.
[approval]
self_approval = "labelled"

How often each gate blocks

Propose and solidify record every gate's verdict against each landing attempt. `nool policy stats` shows, per gate, how often it blocked and how many changes passed it on the first run. `nool doctor` warns `GATE_FIRST_RUN_LOW` when a gate sends most changes back, and `nool insights` and the hub's organisation rollup carry the same counters.

nool policy stats --since 7d
nool policy stats --gate 'policy:*' --json

Prove it afterwards: nool audit verify

`nool audit verify` gives one status per framework control over a range of knots: `PASS`, `FAIL`, `NOT_EVALUATED`, `UNVERIFIABLE`, `NOT_APPLICABLE` or `OUT_OF_SCOPE`. For every knot it re-runs the policy set landed before that knot and reads the reviewer and checker keys from the `nool.toml` landed before it, so an uncommitted edit changes nothing. The verdict comes from git history alone: a fresh clone with Nool's refs fetched reproduces it (`--source git`). Framework packs ship for PCI DSS 4.0.1, SOC 2, the HIPAA Security Rule, GDPR, SOX ITGC and ISO/IEC 27001:2022, and each control names the evidence that satisfies it or says why none can.

nool audit packs --framework pci-dss
nool audit verify --from 7.22.0 --framework soc2
nool audit verify --source git --json --output verdict.json

Revocation and publication

`nool revocation record|status|export` keeps an append-only, hash-chained ledger of compromise and operational revocations of knots, receipts and signers; offline bundle verification reports whether a signer was revoked at admission or at publication and quarantines revoked publications. `[publication] destination` and destination grants (`nool admin team add --destination <dest>`) bind where a change may land, and gate receipts sign the destination. A server-side pre-receive publication gate verifies the evidence bundle for the exact incoming Git object and refuses quarantined admissions; a short-lived signed break-glass covers one exact ref update.

nool revocation record --class compromise --signer-fingerprint <fp> --effective-at 2026-11-01T09:00:00Z --reason "laptop lost"
nool revocation status --signer-fingerprint <fp>