Export and verify a run
Check a run export offline and read the limits of its record.
An auditor needs to check what an agent did and what the record leaves out.
Export a finished run, then check the file offline with oxagen verify.
Only an organization Owner can export. The auditor needs no Oxagen account.
Oxagen locks the export with its private key. Locked means any change after that point shows. Anyone with the matching public key can check the lock. Compare the key ID with one you obtain from the operator through a separate channel. A key shipped inside a file does not establish who made the file.
Export
- Open the Run page and choose Export, or run
oxagen run export <run-id>. The run must have ended and its record must be locked. - Read the status with
oxagen run export-status <export-id>. Wait forready. A failed export reports its error. - Run
oxagen run download <export-id>. The CLI writes a ZIP and checks its SHA-256 hash against the recorded hash. A hash is a short code made from the content. Any change gives a different code. - Give the ZIP to the auditor. A download link also works without sign-in for 15 minutes. Reading the status again creates a new link.
Verification
Install the CLI:
curl -fsSL https://docs.oxagen.sh/install.sh | shThe installer downloads an executable when one is available. Its npm fallback needs Node.js 22.15 or newer. Node.js 23 needs version 23.8 or newer. You can also install the npm package directly:
npm install -g @oxagen/cliCheck the ZIP or the directory you extracted it into:
oxagen verify export.zip
oxagen verify ./export
oxagen verify export.zip --jsonThe check reads local files and makes no network call. It sends no usage telemetry and writes nothing to your home directory.
Read the output before accepting the record:
- Frames: each recorded event has a hash check and a link check. The link
places it in its chain. A frame without its original event reports
hash not carried. Its links and the export's locked bytes can still be checked. - Checks: frame counts, Merkle roots (hashes of the frame hashes), exported
content, locks, and key IDs must hold. A
manifestcheck fails when a coverage value inmanifest.jsondiffers from the locked copy or is missing. - Record coverage: each
record coverageline prints the enforcement tier, completeness gaps, and replay grade from the lock. The tier names the recorded enforcement path. The gaps name known missing evidence. The replay grade states what replay the record supports.not recordedmeans the locked grade isnull. - Redactions: the summary lists removed credentials by kind and count, and content the export withholds. It never prints a credential value.
HELD means the checks passed, and the command exits 0. BROKEN names a failed
check and exits 1. JSON output carries the same checks and coverage values.
An empty gaps list means no gaps were declared. It does not prove complete recording.
Lock timing
For a run wrapped on a person's computer, Oxagen makes its lock when it builds
the export. The run export carries no independent timestamp from an outside
authority. Its exported_at field records the export time but is not covered by
the lock.
A passing check detects changes after the lock. It does not prove when the agent acted, that every action was recorded, or that the work was correct. Read the record coverage statement for the recording paths and the actions they cannot see. Observe mode records actions without enforcing rules on them.
Files
| File | Contents |
|---|---|
manifest.json | Format, run ID, export time, key ID, and a list under attempts with each chain's figures and coverage values. |
frames.ndjson | One recorded event per line, with hashes and chain links. |
attestation.json | Oxagen's locks, their public key, and the locked figures. |
redactions.json | Counts of removed credentials and withheld content. |
verify.mjs | A standalone Node.js verifier. |
The lock covers run_id, attempt_id, frame_count, merkle_root,
archive_segment_digest, enforcement_tier, completeness_gaps, and
replay_grade. It uses Ed25519 over JSON in one standard form (RFC 8785).
The verifier compares the manifest's coverage values with these locked values.
The export uses oxagen.run-export/3, or oxagen.run-export/4 when a frame
records content Oxagen removed at ingest. The CLI also reads earlier formats.
Update the CLI if it reports unknown export format.
Content checks
The export names stored bodies by hash and does not carry those bodies.
If you have a body, compare its SHA-256 hash with content.digest.
When Oxagen removed a credential at ingest, compare it with
content.server_redactions.digest instead. That field names the stored bytes
after the removal, while content.digest names what the agent's computer sent.
To test a changed copy, extract the ZIP and edit a frame's event.body in
frames.ndjson. Run oxagen verify ./export again. The frame and exported
content checks fail. Keep the original ZIP for comparison.
Run node verify.mjs <directory> only on an export you made yourself.
A program in a file someone else supplied runs with your rights before it
checks anything. Use your installed oxagen verify for exports from others.
Claim exports
A claim export covers an agent across a time window and uses a separate format. It has its own timestamp, inclusion, gap, access, and recording coverage checks. Those checks do not add an outside timestamp to a run export.
For claim exports, obtain the record keeper's key ID, the Transparency Service's
key ID, and the timestamp authority's root fingerprint through separate channels.
Pass them with --issuer-key, --ts-key, and --tsa-root. Without all required
pins, the verdict is UNPINNED and the command exits 3, even if the other checks hold.