> ## Documentation Index
> Fetch the complete documentation index at: https://nono.sh/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Audit Trail

> Session tracking, filtering, and compliance reporting

Every `nono run` session is recorded by default. nono records the command that ran, the audit events observed during the session, optional filesystem changes, and network events outside the sandbox in the trusted parent process. This gives you a history of agent activity for debugging, compliance, and forensics.

The details of how that record is protected are explained in the [Security Model](https://nono.sh/docs/cli/internals/security-model.md): the trusted supervisor records the session, the event log is committed with a Merkleized integrity structure, and optional signing lets a key holder attest to the completed session.

## How It Works

> [card] Session Audit
>
> One `nono run` from start to finish.
>
> Answers: "what command ran, when did it start and end, and what did the supervisor record during that run?"

> [card] Audit-Log Integrity
>
> A tamper-evident structure over the recorded audit events for that session.
>
> Answers: "has the recorded audit log been modified since it was written?"

> [card] Filesystem Integrity
>
> Optional hashing of the writable paths that were in scope for the run.
>
> Answers: "what filesystem state was committed before and after the session under the tracked writable roots?"

> [card] Rollback
>
> A stronger recovery layer that stores content-addressable snapshots.
>
> Answers: "can this session's file changes be restored later?"

By default, `nono` records a session audit and protects the audit event log with an integrity layer. Filesystem hashing and rollback storage are optional extra layers.

Each session records:

| Field | Meaning |
| - | - |
| **Command** | The command and arguments, with best-effort redaction for common secret-bearing argv, header, and URL patterns |
| **Timestamps** | Start time, end time, duration |
| **Exit code** | How the process terminated |
| **Audit events** | Session start/end plus supervisor-observed events such as capability decisions and URL opens |
| **Network events** | Proxy audit log (when network proxy is active), with the secure default redaction policy applied to request paths, denial reasons, and upstream URLs |
| **Tracked paths** | Writable policy roots for the session |
| **Merkle roots** | Filesystem-state commitments (when `--audit-integrity` or `--rollback` is active) |
| **Snapshots** | Content-addressable rollback metadata (when `--rollback` is active) |
| **Audit integrity summary** | Hash-chain head and Merkle root for the audit event stream unless disabled with `--no-audit-integrity` |
| **Command policy summary** | Per-command rollup of allowed and denied [Sandboxed Tool Execution](https://nono.sh/docs/cli/features/tool-sandbox.md) decisions (when the profile mediates commands) |

## Modes

> [tab] Default
>
> Records the session and protects the recorded event log with the default integrity layer.
>
> ```bash theme={null}
> nono run --allow-cwd -- my-agent
> ```

> [tab] No Audit
>
> Disables audit recording completely for that session.
>
> ```bash theme={null}
> nono run --no-audit --allow-cwd -- my-command
> ```

> [tab] No Audit Integrity
>
> Keeps the session audit, but skips the tamper-evident integrity layer for the event log.
>
> ```bash theme={null}
> nono run --no-audit-integrity --allow-cwd -- my-agent
> ```

> [tab] Filesystem Integrity
>
> Keeps the session audit, keeps audit-log integrity, and also hashes filesystem state for the tracked writable paths.
>
> ```bash theme={null}
> nono run --audit-integrity --allow-cwd -- my-agent
> ```

> [tab] Rollback
>
> Keeps all of the above and also stores rollback snapshots that can be restored later.
>
> ```bash theme={null}
> nono run --rollback --allow-cwd -- my-agent
> ```

## Terms

Every audited session writes an append-only `audit-events.ndjson` file. In plain terms:

| Term | Meaning |
| - | - |
| **Session** | One audited `nono run` |
| **Audit event** | One recorded fact within that session, such as `session_started`, `session_ended`, a capability decision, or a supervisor-observed URL-open event |
| **Filesystem-state hashing** | Walking the tracked writable paths and hashing the files under them to commit their state before and after the run |
| **Command policy event** | One mediated tool decision recorded by the Capability Broker, carrying the `decision` string that names the lifecycle stage or terminal result |
| **Invocation** | One mediated tool decision that reached a terminal outcome. An invocation that clears an invocation gate and then runs records two events but counts once |

## Integrity Flow

**1. Record events**

The trusted supervisor records audit events during the session into `audit-events.ndjson`.

**2. Commit the event stream**

When audit-log integrity is enabled, nono stores an event count, a hash-chain head over the ordered event stream, and a Merkle root over all recorded event leaves.

**3. Optionally hash filesystem state**

With `--audit-integrity`, nono also walks the tracked writable roots and records pre/post filesystem Merkle roots.

**4. Optionally store rollback data**

With `--rollback`, nono also stores content-addressable snapshots that can be restored later.

## Audit Signing

Signing is a separate layer from audit recording, audit-log integrity, filesystem integrity, and rollback.

It answers a different question:

* audit recording: "what happened?"
* audit-log integrity: "has the recorded audit log changed?"
* filesystem integrity: "what filesystem state was committed?"
* **signing**: "did a holder of this signing key attest to this completed session?"

When you use `--audit-sign-key`, the supervisor signs **once per session**, after the run has finished.

`--audit-sign-key` does **not** sign every event individually. It signs once, at session finalization, over the completed session audit root and session context.

**1. Record the session**

The supervisor records audit events during the run.

**2. Build the final root**

nono computes the session's final audit Merkle root from those events.

**3. Sign once**

When the session ends, the supervisor signs that final audit root plus session context.

**4. Write the attestation bundle**

The resulting DSSE bundle is written into the audit session directory.

To have the supervisor sign the completed session audit record, use `--audit-sign-key`:

```bash theme={null}
# Sign with a trust key from the default keystore
nono run --audit-sign-key default --allow-cwd -- my-agent

# Sign with an explicit secret backend reference
nono run --audit-sign-key op://Development/Nono/audit-key --profile nolabs-ai/claude -- claude
```

The signing key is loaded by the trusted supervisor before sandbox activation. After the session ends, the resulting keyed DSSE bundle is written into the audit session directory as `audit-attestation.bundle`, and a summary is stored in `session.json`.

> [!NOTE]
>
> Command arguments included in session metadata and audit attestations are best-effort redacted before they are written. This protects common forms such as `--token VALUE`, `--api-key=VALUE`, `Authorization: ...`, URL userinfo, and sensitive query parameters, but it is not a complete secret detector. Prefer nono's credential injection features instead of passing secrets on the command line.

### Redaction Policy

The default redaction policy covers common secret-bearing command flags, HTTP headers, URL userinfo, and URL query keys. You can extend it for local tools in `~/.config/nono/config.toml`:

```toml theme={null}
[redaction]
extra_flags = ["--private-token", "--pat"]
extra_headers = ["Private-Token"]
extra_query_keys = ["sig", "signature"]
```

Removing a built-in default is allowed only for explicit debugging:

```toml theme={null}
[redaction]
unsafe_redaction_overrides = true
allow_unredacted_defaults = ["state"]
```

When a run uses a non-default redaction policy, the session audit event log and audit attestation predicate include the policy diff so reviewers can see which names were added or removed.

## Commands

### `nono audit list`

List all recorded sessions, grouped by project directory.

```bash theme={null}
# Show all sessions (grouped by project)
nono audit list

# Show only today's sessions
nono audit list --today

# Filter by date range
nono audit list --since 2026-02-01 --until 2026-02-15

# Filter by command
nono audit list --command claude

# Filter by tracked path
nono audit list --path ~/dev/my-project

# Show only the 10 most recent
nono audit list --recent 10

# Hide the per-session mediated tool summary
nono audit list --no-tools

# Machine-readable output
nono audit list --json
```

Example output:

```
nono 14 command(s)

  ~/dev/sprockets (11 commands)
    20260219-092017-8117  just now  completed  claude
      tools: gh (3 allowed, 1 denied), git (12 allowed)
    20260219-091403-90291  5m ago  completed  claude
    20260218-134433-28210  1d ago  completed  claude
    ...

  ~/dev/widgets (3 commands)
    20260219-100000-1234  2d ago  completed  claude
    20260218-120000-5678  3d ago  completed  my-agent
    ...
```

Filters can be combined:

```bash theme={null}
# Claude sessions from last week that touched the project directory
nono audit list --command claude --path ~/project --since 2026-02-10
```

#### Mediated Tool Summary

Sessions that ran under a profile with [Sandboxed Tool Execution](https://nono.sh/docs/cli/features/tool-sandbox.md) get a second `tools:` line naming each mediated command with its allowed and denied counts. Sessions that ran without mediation print nothing extra, and `--no-tools` suppresses the line entirely.

The line is read from the rollup stored in `session.json`, never from the event log, so listing stays fast no matter how many events a session recorded. A few renderings are worth recognizing:

| Rendering | Meaning |
| - | - |
| `active, no invocations` | Mediation was configured and no mediated command was ever invoked. Distinct from a session that ran without mediation at all, which prints no `tools:` line |
| `N event(s), no completed invocations` | Command policy events were recorded but none reached a terminal decision, which is what a session killed mid-invocation looks like |
| `N unclassified` | A terminal decision nono could not classify as allowed or denied. Read the event itself with `nono audit show` |
| `+N more` | More distinct commands were mediated than the list line names. The full per-command rollup is still in `nono audit list --json` |
| `rollup truncated` | The stored rollup itself dropped commands past its bound, so per-command counts for them are not recoverable from session metadata. The session totals still include them |

`--no-tools` only affects the human-readable listing. `--json` always carries the same data per session as `tool_sandbox_active` and the full `command_policy_summary` object, and `nono audit show --json` reports the underlying `command_policy_events`, one entry per mediated decision.

Those two fields answer different questions, so read them apart:

| Field | True when |
| - | - |
| `tool_sandbox_active` | The session has a rollup at all: mediation was configured, or a mediated command was invoked. Equivalent to `command_policy_summary != null` |
| `command_policy_summary.mediation_active` | Mediation was configured, read from the sandbox runtime audit event. Sessions recorded before macOS emitted that event can report `false` despite having mediated commands |

A session only writes `session.json` when it finalizes, so `nono audit list` and `nono audit show` cover finished sessions. For a session that is still running, [`nono inspect`](https://nono.sh/docs/cli/features/session-lifecycle#mediated-tools-while-the-session-runs) reports the same rollup recomputed from the event log.

> [!NOTE]
>
> Whether mediation was configured is read from the sandbox runtime audit event. Sessions recorded by older nono versions (before macOS emitted that event) that configured mediation but never invoked a mediated command print no `tools:` line, the same as a session that ran without mediation.

### `nono audit show`

Show complete details for a specific session.

```bash theme={null}
# Human-readable output
nono audit show 20260214-143022-12345

# Machine-readable JSON export
nono audit show 20260214-143022-12345 --json
```

The JSON output includes all recorded session metadata, optional filesystem integrity data, optional rollback snapshot data, and exit status. This format is suitable for ingestion by compliance tools or log aggregators.

If the session was signed with `--audit-sign-key`, `nono audit show` also includes:

* the attestation predicate type
* the signer key id
* the attested public key
* the attestation bundle filename

### `nono audit verify`

Verify the recorded audit log, its inclusion in the local audit ledger, and any optional audit attestation.

```bash theme={null}
# Verify session-local audit integrity and ledger inclusion
nono audit verify 20260214-143022-12345

# Also pin the attestation to a specific public key
nono audit verify 20260214-143022-12345 --public-key-file ./audit-signing-key.pub

# Machine-readable output
nono audit verify 20260214-143022-12345 --json
```

When an attestation is present, verification checks:

* the keyed DSSE signature
* the attested Merkle root against the session's stored audit integrity summary
* the session id bound into the attestation predicate
* the provided public key, if `--public-key-file` is supplied

The command policy rollup shown by `nono audit list` is covered by the session digest committed to the ledger, so editing `session.json` to hide a denial changes the digest and fails verification. The rollup is a view over events that remain individually recorded and hash-chained; `nono audit show` is where an operator confirms what the rollup summarizes.

### `nono audit cleanup`

Remove old audit sessions from disk. `--keep`, `--older-than`, and `--max-total-size` can be combined; each filter narrows further which sessions are removed.

```bash theme={null}
# Retain only the 10 newest sessions
nono audit cleanup --keep 10

# Remove sessions older than 30 days
nono audit cleanup --older-than 30

# Remove the oldest sessions until the remaining total is under 500 MB
nono audit cleanup --max-total-size 500

# Preview what would be removed without deleting anything
nono audit cleanup --keep 10 --dry-run
```

`--max-total-size` counts every remaining session's disk usage toward the budget, including sessions spared by `--keep` or `--older-than` — it removes oldest-first until the total is back under the given number of megabytes, or everything removable is gone.

## Use Cases

### Debugging

When an agent produces unexpected results, the audit trail tells you what the supervisor observed during the run and, when rollback was enabled, exactly what files changed:

```bash theme={null}
# What did the last Claude session do?
nono audit list --command claude --recent 1
nono audit show <session-id>
```

### Compliance

For teams that need to demonstrate control over AI agent activity, the audit trail provides:

* Timestamped proof that sandboxing was active
* Structured record of what the supervisor observed
* Tamper-evident integrity metadata for the audit log itself by default
* Optional filesystem-state commitments over tracked writable roots (`--audit-integrity`)
* Optional complete record of filesystem changes with restore capability (`--rollback`)
* Machine-readable JSON export for automated compliance reporting

### Forensics

If something goes wrong, the audit trail helps reconstruct what happened:

```bash theme={null}
# What touched this file recently?
nono audit list --path /etc/config.yaml

# Full details of a suspicious session
nono audit show <session-id> --json
```

## Relationship to Rollbacks

Audit, audit integrity, and rollback are separate layers that share the same session ID:

| Aspect | Audit | Audit Integrity | Rollback |
| - | - | - | - |
| Default | Always on | Event-log integrity on by default; filesystem hashing opt-in via `--audit-integrity` | Opt-in (`--rollback`) |
| Opt-out | `--no-audit` | `--no-audit-integrity` disables event-log integrity | `--no-rollback` |
| Purpose | Record what happened | Detect tampering in the recorded audit log and optionally commit filesystem state | Restore filesystem state |
| Data | Command, timestamps, exit code, audit events, network events, tracked paths, command policy summary, optional audit attestation | Event count, chain head, Merkle root, optional filesystem Merkle roots | Full file content snapshots + Merkle roots |
| Commands | `nono audit list/show/verify/cleanup` | `nono audit show/verify` | `nono rollback list/show/restore/verify/cleanup` |

When rollback is active, it enriches the audit record with snapshots and restore metadata. When filesystem audit integrity is active, `nono audit show` also includes tracked paths and filesystem Merkle roots.

## Limits

The audit trail is intentionally narrow in what it claims to prove.

* The event log is recorded by the trusted supervisor, not by the sandboxed child.
* The default integrity structure protects the audit event stream for a single session and also records that session into the global audit ledger.
* Recording into the global ledger can fail after the command has already exited (for example when the ledger file no longer parses, or the audit directory is unwritable). The session's own directory is still written, but the ledger has no entry for that run, so `nono audit verify` reports it as missing from the chain. The failure is printed on stderr and the run's exit status is downgraded to `1` if the command itself succeeded; a command that failed keeps its own exit status, so a non-zero status is not by itself proof that the run was recorded.
* A ledger that no longer parses stops every later run from being recorded, and there is no repair command. Moving the file aside starts a fresh chain, after which `nono audit verify` no longer finds the sessions the old one held.
* The command policy rollup in `session.json` is a convenience view for listing sessions, not an independent record. It is covered by the session digest committed to the ledger, so editing it fails `nono audit verify`; `nono audit show` remains the per-decision record.
* That rollup names a bounded number of distinct commands. A session that mediates more than the bound drops the per-command counts for the overflow and sets `truncated`; the session totals stay correct and every decision is still in the event log.
* `--audit-sign-key` adds a keyed supervisor-side signature over the session audit Merkle root and session context, but that still depends on trusting the configured signing key and how its public key is distributed.
* Without an external timestamp, transparency log, or other anchor, this remains host-local attestation material rather than a globally witnessed timestamped proof.
* For supervised runs, the supervisor hashes the main executable binary selected for launch and records its canonical path and SHA-256 digest.
* That executable hash is still subject to a small hash-to-exec race window. nono hashes the file before `exec`, but does not yet `fexecve` from the same already-open file descriptor.
* Only the main executable (`argv[0]` after resolution) is hashed. For `bash script.sh`, this commits `/bin/bash`, not `script.sh`, shared libraries, interpreters in a launch chain, or dynamically loaded plugins/modules.
* Direct and monitor-style execution paths do not currently populate executable identity; this metadata is recorded by the supervised path.
* `--audit-integrity` records filesystem-state Merkle roots over tracked writable paths, but it does not capture rollback objects or restore data. Use `--rollback` for that.
* Filesystem-state hashing does not attest the full runtime closure of a process. Shared libraries, interpreters, dynamically loaded plugins, and other runtime dependencies are not committed unless separately recorded.
* `network_events` and rollback metadata are separate recorded structures. They are not a claim of full system provenance or full host compromise detection.
* `--audit-sign-key` signs the main session audit Merkle root, not every possible host-side artifact. It does not sign shared libraries, runtime module loads, or an external timestamp by itself.

## Storage

Audit sessions are stored in `$XDG_STATE_HOME/nono/audit/` (default `~/.local/state/nono/audit/`). Older installs may still have data under `~/.nono/audit/`; nono reads that path with a deprecation warning until v1.0.0. Audit-only sessions are small (`session.json` and `audit-events.ndjson`). Signed sessions also include `audit-attestation.bundle`. Clean them up with `nono audit cleanup`.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.