The pipeline: capture → seal → replay → diff → audit
Architecture, as built › Pipeline
The five verbs are separate nova commands that all work on one capsule
directory. Seal is the exception: it runs automatically at the end of
nova capture when NovaSeal is configured. Any later step can run on another
machine, years later, from a copy of the directory.
sequenceDiagram
autonumber
actor U as User / CI
participant C as nova capture<br/>(CaptureOrchestrator)
participant W as Workload subprocess<br/>(+ sitecustomize hooks)
participant D as Capsule directory
participant S as NovaSeal
U->>C: nova capture -- python agent.py
C->>D: open <ULID>/ (inputs/, outputs/, empty *.jsonl)
C->>W: runner starts the command
W-->>D: model / tool / trace records (hooks)
W-->>C: exit code, stdout, stderr
C->>D: env.lock, redaction-proof.json, replay.yaml, capsule.yaml, lineage.jsonl
C->>D: evidence_digests → capsule.yaml (rewritten)
opt NovaSeal configured
C->>S: seal(manifest)
S-->>D: .seal/manifest.dsse, .tsr, log-entry.json
end
U->>D: nova verify · nova replay · nova diff · nova export-evidence · nova audit1 · Capture: nova capture (works today)
cli/capture.py:capture_cmd builds a capture/orchestrator.py:CaptureOrchestrator
and calls run(). If the warm-capture daemon is reachable and no flags need the
in-process path, the command goes to the daemon instead (see
warm-capture-daemon.md). run() works through these
stages in order:
- Pre-flight gates, before anything is written: required asset status
(
--require-asset-status), deployment environment, A/B variant, session membership. - Open the capsule. It gets a new ULID (
capture/_ulid.py), andcapture/capsule.py:CapsuleWriter.open()creates the directory withinputs/,outputs/and empty call streams. - Start recording.
capture/event_recorder.py:EventRecorderstarts, and the environment snapshot (capture/env.py) is taken on a background thread so it overlaps the workload. - Run the workload. The selected runner (
runners/:localby default; alsodocker,kubernetes,slurm,lsf,pbs) writes thesitecustomize.pyhook loader onto the child'sPYTHONPATH. In the child,capture/hooks/__init__.py:install_allpatches the supported SDKs and HTTP clients. - Redact.
capture/secrets.py:SecretScannerV0.scan_and_redactalways runs, followed by any configured maskers (masking/). Both feedredaction-proof.json. - Write the manifest and lineage. The orchestrator writes
replay.yamlandcapsule.yaml, thenlineage/_writer.py:LineageWriterwriteslineage.jsonland the edges are indexed. - Digest.
evidence_digestsrecords a SHA-256 and size for every evidence file, andcapsule.yamlis rewritten. - Seal, if configured. See step 2 below.
A failed workload still produces a complete capsule with status: failure. If a
NovaFabric component fails, the failure is recorded and the workload continues.
Capture also has proxy paths for clients that cannot be hooked in-process:
nova api-proxy (proxy/api_proxy.py) and nova mcp-proxy
(proxy/mcp_proxy.py).
Details: Run Capsule anatomy.
2 · Seal: automatic when configured (experimental)
capture/orchestrator.py:_seal_capsule runs as the last step of capture, but
only if a NovaSeal configuration exists (NOVAFABRIC_SEAL_CONFIG or
~/.novafabric/novaseal.yaml). Without one, sealing is skipped. A sealing
failure prints a warning and never fails the capture. A seal produces three
files in .seal/:
- a DSSE signature over the manifest,
- an RFC 3161 timestamp token,
- an entry in an append-only Merkle log.
nova seal propose / approve / verify add an optional maker-checker
signature chain on top (cli/seal_propose.py).
Details: Sealing and verification.
3 · Replay: nova replay (works today; intervention experimental)
cli/replay.py:replay_cmd → replay/_engine.py:ReplayEngine.run. The default
mode, mocked, re-runs the captured command with OpenAI and Anthropic responses
served from the record. The other four modes analyse the capsule. Every mode
writes .novafabric/replays/<ulid>/replay_result.yaml. intervention is the
only mode that also writes a new capsule.
Details: Replay modes.
4 · Diff: nova diff A B (works today)
cli/diff.py:diff_cmd → diff/_engine.py:DiffEngine.compare. It takes two
capsule paths or run IDs and compares:
| Facet | How it matches | "Changed" means |
|---|---|---|
| Environment | Fixed keys from env.lock: Python version and interpreter, OS, architecture |
Value differs |
| Model calls | Paired by parent_span_id (diff/_align.py); unpaired calls count as added or removed |
Request model or messages differ, or the response choices differ |
| Tool calls | Paired by (tool_name, arg_hash) |
result differs |
| Outputs | Each file under outputs/ |
SHA-256 differs |
The output format is text, json or github-annotation.
--assert-no-regressions makes the command exit non-zero when the report has
changes, which turns it into a CI gate. nova diff also accepts two
name@version asset references and diffs their specs.
5 · Audit: nova verify, nova export-evidence, nova audit (works today)
"Audit" names three commands that produce or check evidence:
| Command | What it does | Module |
|---|---|---|
nova verify <capsule> |
Checks the seal: DSSE signature, timestamp, Merkle inclusion, manifest binding and per-file digests. Exit 0 or 1. It also verifies an Evidence Bundle .zip by recomputing every artifact digest. |
cli/verify.py |
nova export-evidence <capsule> |
Builds the Evidence Bundle ZIP: the capsule, a lineage subgraph, Ed25519-signed in-toto DSSE attestations (run, redaction, lineage), vendored schemas and a manifest.json |
evidence/bundle.py:EvidenceBundleBuilder |
nova audit map / report / bundle / coverage |
Maps capsule evidence to the controls of a regulatory profile (default nist-ai-rmf) |
cli/audit.py, compliance/audit/ |
The Evidence Bundle is the artifact you hand to a third party. Its
manifest.json contains its own verification recipe: recompute each artifact's
SHA-256, then check the Ed25519 signatures. Any SHA-256 tool and Ed25519
verifier can run that recipe without NovaFabric. A bundle cannot be exported from
a capsule that has no redaction-proof.json.
Read next
- Sealing and verification
- Prove a run to an auditor (tutorial)
- How capture works (tutorial)