Runtime and test evidence custody

Keep traces, dumps and test results by their identity. This page documents every command in the runtime-evidence group, in the order sbomflow help runtime-evidence lists them. Each command's section is generated from the command itself — its summary, description and options from the parser, and its reads, writes, exit codes, examples and see-also from the command catalog — so it says exactly what sbomflow <command> --help says. Every example below is executed offline by the test suite, and every non-zero exit code is either produced by an executed probe or carries a recorded reason why no offline probe can produce it.

See the CLI overview for how the thirteen groups fit together and the command reference for every command's options in one place.

Overview#

sbomflow custody, sbomflow ingest-preserved-artifact and sbomflow twister-evidence keep traces, dumps and test results behind a release by identity. The worked guide is Runtime and test evidence.

Who this is for: test leads, fuzzing engineers, lab and bench engineers, analysis-tool users and QA engineers.

sbomflow custody#

Keep, verify and review runtime and test artifacts in a standalone custody set.

text
A custody set is a directory holding verbatim preserved-artifact records, an
index, a hash-chained audit log and, only when asked, local copies of the
originals. Custody proves byte identity and records what humans stated and
decided; it does not analyse artifacts, prove coverage or test success, or
make any security, safety or conformity claim.
FactValue
Groupruntime-evidence — Runtime & test evidence custody
Stabilityexperimental
Regimeany
Readsnothing itself; its verbs read and write one custody set.
Writesnothing itself; each verb's help names what it writes
Networkoffline — This command has no network-capable path.
Exit codes0 a verb ran and returned 0 · 2 no verb given, or the verb refused
Machine outputhuman-only-by-design — group dispatcher: prints usage/guidance only; the machine surfaces are its nested verbs

Options

OptionValueDefaultAllowedRequiredHelp
-h, --help(flag)none—noshow this help message and exit

Examples

Create an empty custody set for one release's runtime evidence.

sh
sbomflow custody init custody-set --set-id bench --release-id 2.4.1

Admit a captured trace into the set.

sh
sbomflow custody add custody-set captures/boot-to-idle.psf --release-id 2.4.1 --artifact-kind runtime_trace --operator bench-team --as-of 2026-06-20T12:00:00Z

Re-hash the set and check its audit chain and seal.

sh
sbomflow custody verify custody-set

See also: sbomflow custody init, sbomflow custody add, sbomflow ingest-preserved-artifact, sbomflow help goal custody.

sbomflow custody add#

Admit an artifact, or an existing preserved-artifact record, into a custody set.

text
Admit one artifact or record into a custody set, all or nothing.
FactValue
Groupruntime-evidence — Runtime & test evidence custody
Stabilityexperimental
Regimeany
Readsthe artifact (or an existing preserved-artifact record, or an intake manifest) and the set.
Writescustody-set.json (the set's index, with the record and an audit event); custody-operation.json (with --json, the operation result on stdout)
Networkoffline — This command has no network-capable path.
Exit codes0 ok · 2 a refused artifact, record or set
Machine outputhas-json — one sbomflow-custody-operation-v1 document: the outcome and the set's row for the artifact; with --intake-manifest, row is null and intake carries each entry's outcome

Options

OptionValueDefaultAllowedRequiredHelp
-h, --help(flag)none—noshow this help message and exit
DIRDIRnoneanyyesThe custody set.
[ARTIFACT][ARTIFACT]noneanynoThe artifact file to preserve (artifact form).
--recordRECORDnoneanynoAn existing sbomflow-preserved-artifact-v1 record to admit (record form).
--originalORIGINALnoneanynoRecord form: the artifact the record describes, re-hashed at admission.
--intake-manifestFILEnoneanynoIntake form: an sbomflow-custody-intake-v1 manifest naming each file to add. All of its entries are admitted, or none are.
--release-idRELEASE_IDnoneanynoArtifact form: the release this artifact belongs to.
--scenarioSCENARIOnoneanynoArtifact form: the test scenario it was captured in.
--tool-nameTOOL_NAMEnoneanynoArtifact form: the tool that produced it.
--tool-versionTOOL_VERSIONnoneanynoArtifact form: that tool's version.
--notesNOTESnoneanynoArtifact form: capture notes.
--operatorOPERATORnoneanynoArtifact form: contact-free identifier of who preserved it.
--artifact-kindKINDnoneanynoArtifact form: what the evidence IS, as you state it — one of runtime_trace, core_dump, crash_report, device_log, bench_log, network_capture, test_report, test_results, coverage_report, fuzzing_campaign, fuzzing_reproducer, static_analysis_report, penetration_test_report, laboratory_test_report, certification_or_approval_record, conformance_test_report, vulnerability_handling_record, signing_record, provisioning_record, device_state_statement, timestamp_or_transparency_receipt, other. Never inferred from the filename or the bytes; omit it and the record states no kind.
--firmware-imagePATHnoneanynoArtifact form: the firmware image this capture was taken on. Hashed, never parsed; the record keeps the SHA-256 and the file name only. A symlink, device or FIFO is refused. A statement, never an observation.
--firmware-sha256HEXnoneanynoArtifact form: that image's SHA-256 (64 lowercase hex) when you have the digest but not the file. With --firmware-image the two must agree.
--firmware-build-idHEXnoneanynoArtifact form: the image's GNU build id as you state it (even-length, 2 to 128 lowercase hex). A build id is not a checksum.
--firmware-versionTEXTnoneanynoArtifact form: the firmware version the device or build reported, as you state it (at most 128 characters). Never a stand-in for a digest.
--firmware-inner-hashALG:HEXnoneanynoArtifact form: the image hash an MCUboot image carries in its own hash TLV, as a device manager reports it (ALG sha256, sha384 or sha512; lowercase HEX of that length). Not the file's SHA-256.
--capture-sessionIDnoneanynoArtifact form: an id joining several records from one capture, alert or campaign. Contact-free, same rule as --operator.
--performerIDnoneanynoArtifact form: who performed the test, as a lab, team or role id and never a personal contact. Same rule as --operator.
--independenceRELATIONSHIPnonesame_team, independent_internal, third_partynoArtifact form: the tester's relationship to the team that built the thing. Closed set; no certified or accredited value exists. One of: same_team, independent_internal, third_party.
--test-environmentTEXTnoneanynoArtifact form: the environment or rig tested on, as you state it (at most 256 characters, no control characters).
--configuration-filePATHnoneanynoArtifact form: a configuration, dictionary or profile recorded by name and digest. Hashed, never parsed.
--procedure-refTEXTnoneanynoArtifact form: the procedure or test-plan reference followed, as stated (at most 128 characters). Never dereferenced.
--original-locationURInoneanynoArtifact form: where the original is kept, as you state it (s3:, gs:, az:, https:, file: or urn:, or an opaque id; at most 512 characters). Never fetched. Refused with a user name, query, fragment, presigned-URL parameter or absolute host path.
--retain-untilDATEnoneanynoArtifact form: the date you state the original must be kept until, recorded as typed. No retention period is computed.
--valid-untilDATEnoneanynoArtifact form, with --artifact-kind certification_or_approval_record or conformance_test_report: the date the record states it applies until, recorded as typed and compared only with --as-of. Nothing is decided from it.
--keep-original(flag)false—noKeep a local copy under originals/ in this set (streamed and checked; never exported).
--break-stale-lock(flag)false—noArtifact form: remove a provably stale set lock left by a crashed command.
--captured-atTIMESTAMPnoneanynoArtifact form: when the capture was taken, as you state it (an assertion; recorded as captured_as_of, the fact ingest-preserved-artifact --as-of records). Never defaulted to the clock. --as-of stays tool time.
--json(flag)false—noPrint the operation result as JSON.
--as-ofAS_OFnoneanynoTool time: pins generated_at and audit event timestamps, so output is byte-identical across runs (default: now, UTC). It is never a capture time.

Examples

Admit a captured trace, stating what it is and who preserved it.

sh
sbomflow custody add custody-set captures/boot-to-idle.psf --release-id 2.4.1 --artifact-kind runtime_trace --operator bench-team --as-of 2026-06-20T12:00:00Z

Admit an existing preserved-artifact record, re-hashing the original it describes.

sh
sbomflow custody add custody-set --record preserved/boot-to-idle.preserved-artifact.json --original captures/boot-to-idle.psf --as-of 2026-06-20T12:00:00Z

Report the admission as JSON.

sh
sbomflow custody add custody-set captures/boot-to-idle.psf --release-id 2.4.1 --artifact-kind runtime_trace --operator bench-team --as-of 2026-06-20T12:00:00Z --json

Refuse an artifact that states a release the set is not for. Exits 2.

sh
sbomflow custody add custody-set captures/boot-to-idle.psf --release-id 9.9.9

See also: sbomflow custody init, sbomflow custody list, sbomflow ingest-preserved-artifact, sbomflow help goal custody.

sbomflow custody compare#

Compare two custody sets or exports, or a set against a release run's preserved artifacts.

text
Name what two sides RECORD differently about the same artifacts: which review
keys appeared or disappeared, which records, recorded decisions or recorded
original states differ. Each side is detected by the schema it carries — a
custody set, an export directory or zip, or (with --run) a release run's
preserved-artifacts.json. Artifact bytes are never compared and neither side
is re-verified (`custody verify` does that). A side that is absent, unreadable
or not a custody carrier is reported as NOT COMPARED with the reason, never as
no change. Read-only, offline, and it affects no release gate.
FactValue
Groupruntime-evidence — Runtime & test evidence custody
Stabilityexperimental
Regimeany
Readstwo custody sets or exports, or a set and a release run's preserved-artifacts.json; artifact bytes are never compared.
Writescustody-comparison.json (with --json on stdout, or at --output)
--output namesone file
Networkoffline — This command has no network-capable path.
Exit codes0 compared, or reported NOT COMPARED with the reason · 2 usage error
Machine outputhas-json — one sbomflow-custody-comparison-v1 document: what two sides record differently, or comparable: false with the reason nothing was compared

Options

OptionValueDefaultAllowedRequiredHelp
-h, --help(flag)none—noshow this help message and exit
AAnoneanyyesA custody set, an export directory or an export zip.
[B][B]noneanynoThe other side: a set, an export directory or an export zip.
--runOUTPUT_DIRnoneanynoCompare A against a release run's preserved-artifacts.json instead of B.
--outputFILEnoneanynoWrite the comparison document here (file); nothing is written without it.
--json(flag)false—noPrint the comparison document as JSON.
--as-ofAS_OFnoneanynoTool time: pins generated_at and audit event timestamps, so output is byte-identical across runs (default: now, UTC). It is never a capture time.

Examples

Name what a set and its export record differently.

sh
sbomflow custody compare custody-set custody-export

Compare a set against a release run's preserved artifacts.

sh
sbomflow custody compare custody-set --run out

A side that is not there is reported as not compared, never as no change.

sh
sbomflow custody compare missing-set custody-export

See also: sbomflow custody verify, sbomflow custody export, sbomflow help goal custody.

sbomflow custody export#

Package a custody set for a customer or auditor to verify offline.

text
Write a verifiable handover of a custody set: the index, every record the
index names, the recorded decisions and the audit log, with a manifest that
hashes each member and anchors the audit chain head and count. Original
artifact bytes are NEVER exported; the manifest says so, and a recipient
checks the bytes they already hold with `custody verify --originals`. Offline.
The share-boundary preflight refuses secret-shaped content before anything is
written, and nothing is published until the whole package is built.
FactValue
Groupruntime-evidence — Runtime & test evidence custody
Stabilityexperimental
Regimeany
Readsone custody set; original artifact bytes are never exported.
Writescustody-export.json (the manifest, with the index, records, decisions and audit log, into --output); custody-export.zip with --zip
--output namesa directory
Networkoffline — This command has no network-capable path.
Exit codes0 ok · 2 a missing set, or a refused preflight
Machine outputhas-json — one sbomflow-custody-export-v1 manifest: every exported member with its sha256 and size (the generated custody-export-index.md among them), the seal, the audit chain head and count, readable_index, and originals_carried: false

Options

OptionValueDefaultAllowedRequiredHelp
-h, --help(flag)none—noshow this help message and exit
DIRDIRnoneanyyesThe custody set to export.
--outputOUTnoneanyyesWhere the export is written (directory); it must be absent or empty, and is never a symlink.
--zip(flag)false—noAlso write a byte-reproducible custody-export.zip inside OUT.
--recipientNAMEnoneanynoContact-free identifier of who this package was prepared for. Recorded in the manifest; it is not an access control and filters nothing.
--select-keyKEYnoneanynoExport only this artifact (repeatable; a FULL review key, never a prefix). The export carries only the selected rows' records, decisions and audit events, and states how many were left out, never which.
--capture-sessionIDnoneanynoExport only the rows whose records state this capture session (with --select-key, the rows either selects). A session no row states is refused.
--keyPEMnoneanynoPrivate key to sign the export manifest with (requires --key-id and the sign extra).
--key-idIDnoneanynoIdentifier recorded in the signature for the key used (requires --key).
--dry-run(flag)false—noPrint what would be exported and the preflight verdict; write nothing.
--json(flag)false—noPrint the export manifest as JSON.
--as-ofAS_OFnoneanynoTool time: pins generated_at and audit event timestamps, so output is byte-identical across runs (default: now, UTC). It is never a capture time.

Examples

Write a verifiable handover of a custody set.

sh
sbomflow custody export custody-set --output custody-export --as-of 2026-06-20T12:00:00Z

Also write the byte-reproducible zip, naming who it is for.

sh
sbomflow custody export custody-set --output custody-export --zip --recipient example-auditor --as-of 2026-06-20T12:00:00Z

Print what would be exported and the preflight verdict, writing nothing.

sh
sbomflow custody export custody-set --output custody-export --dry-run

See also: sbomflow custody verify, sbomflow custody compare, sbomflow help goal custody.

sbomflow custody hold#

Place a preservation hold on a custody set, naming who placed it and why.

text
Place a preservation hold on a custody set. The hold records who placed it,
when, and the reason exactly as stated, in custody-set.json and as an event in
the set's audit log. It has no expiry: it stays until `custody release-hold`
lifts it. While a set is held, a retention date stated for one of its
artifacts that has been reached is reported beside the hold. It does not
change a row, a record, the seal or a decision, custody deletes nothing
either way, and SBOMFlow does not assess the stated reason. A set already
held is refused, naming the hold. Offline; nothing here affects a release gate.
FactValue
Groupruntime-evidence — Runtime & test evidence custody
Stabilityexperimental
Regimeany
Readsone custody set, its index and its audit log.
Writesthe hold in the set's custody-set.json, and one event in its audit-log.jsonl; custody-hold.json (with --json, the hold on stdout)
Networkoffline — This command has no network-capable path.
Exit codes0 ok · 2 a set already held, a missing or contact-carrying actor, or a missing reason
Machine outputhas-json — one sbomflow-custody-hold-v1 document: the hold as the set now records it and the audit event that binds it

Options

OptionValueDefaultAllowedRequiredHelp
-h, --help(flag)none—noshow this help message and exit
DIRDIRnoneanyyesThe custody set.
--actorACTORnoneanyyesNamed person placing the hold (a contact-free identifier); recorded with it.
--reasonREASONnoneanyyesWhy the hold is placed, recorded exactly as stated (at most 512 characters).
--as-ofAS_OFnoneanynoPinned timestamp recorded as when the hold was placed (default: now, UTC).
--json(flag)false—noPrint the hold as JSON.

Examples

Place a preservation hold on a custody set, naming who placed it and why.

sh
sbomflow custody hold custody-set --actor records-team --reason 'Kept while the 2.4.1 field-return review is open.' --as-of 2026-06-21T12:00:00Z

The same, printing the hold as JSON.

sh
sbomflow custody hold custody-set --actor records-team --reason 'Kept while the 2.4.1 field-return review is open.' --as-of 2026-06-21T12:00:00Z --json

Refuse a second hold on a set that is already held, naming the first. Exits 2.

sh
sbomflow custody hold custody-set --actor records-team --reason 'A second hold.'

See also: sbomflow custody release-hold, sbomflow custody list, sbomflow custody verify, sbomflow store hold.

sbomflow custody init#

Create an empty custody set in an empty or absent directory.

text
Create a custody set: the marker, an empty index and the first audit event.
FactValue
Groupruntime-evidence — Runtime & test evidence custody
Stabilityexperimental
Regimeany
Readsnothing but the directory named, which must be empty or absent.
Writescustody-set.json (an empty index, with the marker and the first audit event); custody-operation.json (with --json, the operation result on stdout)
Networkoffline — This command has no network-capable path.
Exit codes0 ok · 2 a non-empty directory or a refused identifier
Machine outputhas-json — one sbomflow-custody-operation-v1 document for the new set

Options

OptionValueDefaultAllowedRequiredHelp
-h, --help(flag)none—noshow this help message and exit
DIRDIRnoneanyyesEmpty or absent directory for the set (its parent must exist).
--set-idSET_IDnoneanyyesContact-free set identifier (lowercase letters, digits, . _ -).
--productPRODUCTnoneanynoProduct the set is for, as you state it.
--release-idRELEASE_IDnoneanynoRelease the set is for; artifacts stating another release are refused.
--issued-byNAMEnoneanynoContact-free identifier of the organisation that ISSUED the evidence (a test house, a laboratory, a vendor) — not the tool that produced it. Stated, never verified.
--retention-noteRETENTION_NOTEnoneanynoHow long you state the originals must be kept, in your own words (recorded verbatim, never interpreted, compared or checked).
--json(flag)false—noPrint the operation result as JSON.
--as-ofAS_OFnoneanynoTool time: pins generated_at and audit event timestamps, so output is byte-identical across runs (default: now, UTC). It is never a capture time.

Examples

Create an empty custody set for one release's runtime evidence.

sh
sbomflow custody init custody-set --set-id bench --release-id 2.4.1

Record who issued the evidence and the stated retention, as JSON.

sh
sbomflow custody init custody-set --set-id lab --issued-by example-test-house --retention-note 'Keep ten years after the last shipment.' --json --as-of 2026-06-20T12:00:00Z

Refuse a directory that already holds something. Exits 2.

sh
sbomflow custody init out --set-id bench

See also: sbomflow custody add, sbomflow custody list, sbomflow help goal custody.

sbomflow custody list#

List the artifacts in a custody set.

text
List a custody set's rows. Lock-free and read-only.
FactValue
Groupruntime-evidence — Runtime & test evidence custody
Stabilityexperimental
Regimeany
Readsone custody set's index; lock-free and read-only.
Writesstdout (one row per artifact; --json prints the index as JSON)
Networkoffline — This command has no network-capable path.
Exit codes0 ok · 2 not a usable custody set
Machine outputhas-json — prints the set index, sbomflow-custody-set-v1, verbatim

Options

OptionValueDefaultAllowedRequiredHelp
-h, --help(flag)none—noshow this help message and exit
DIRDIRnoneanyyesThe custody set.
--json(flag)false—noPrint the set index as JSON.
--capture-sessionIDnoneanynoShow only the rows whose records state this capture session. The output says how many rows were examined and how many matched, so a filtered listing is never mistaken for a whole set.
--as-ofAS_OFnoneanynoTool time: pins generated_at and audit event timestamps, so output is byte-identical across runs (default: now, UTC). It is never a capture time.

Examples

List the artifacts in a set.

sh
sbomflow custody list custody-set

Print the set index as JSON.

sh
sbomflow custody list custody-set --json

Only the rows of one capture session, saying how many matched.

sh
sbomflow custody list custody-set --capture-session field-2026-06

See also: sbomflow custody show, sbomflow custody verify.

sbomflow custody release-hold#

Lift a custody set's preservation hold, naming who lifted it and why.

text
Lift the preservation hold on a custody set. The hold is not deleted: it stays
in custody-set.json flagged as lifted, with who lifted it, when and the reason
exactly as stated, and the event is appended to the set's audit log. A set
that is not held is refused. Offline; nothing here affects a release gate.
FactValue
Groupruntime-evidence — Runtime & test evidence custody
Stabilityexperimental
Regimeany
Readsone custody set, its index and its audit log.
Writesthe lifted hold in the set's custody-set.json, and one event in its audit-log.jsonl; custody-hold.json (with --json, the hold on stdout)
Networkoffline — This command has no network-capable path.
Exit codes0 ok · 2 a set that is not held, or a missing actor or reason
Machine outputhas-json — one sbomflow-custody-hold-v1 document: the lifted hold as the set now records it and the audit event that binds it

Options

OptionValueDefaultAllowedRequiredHelp
-h, --help(flag)none—noshow this help message and exit
DIRDIRnoneanyyesThe custody set.
--actorACTORnoneanyyesNamed person lifting the hold (a contact-free identifier); recorded with it.
--reasonREASONnoneanyyesWhy the hold is lifted, recorded exactly as stated (at most 512 characters).
--as-ofAS_OFnoneanynoPinned timestamp recorded as when the hold was lifted (default: now, UTC).
--json(flag)false—noPrint the hold as JSON.

Examples

Lift the hold; its record stays in the set, flagged as lifted.

sh
sbomflow custody release-hold custody-set --actor records-team --reason 'The field-return review closed.' --as-of 2026-06-22T12:00:00Z

Refuse to lift a hold on a set that is not held. Exits 2.

sh
sbomflow custody release-hold custody-set --actor records-team --reason 'The field-return review closed.' --as-of 2026-06-22T12:00:00Z

See also: sbomflow custody hold, sbomflow custody list, sbomflow custody verify, sbomflow store release-hold.

sbomflow custody review#

Record a named reviewer's decision about one artifact in a custody set.

text
Record what a named reviewer concluded about the exact bytes of one artifact
in a custody set, and its limits: accepted, rejected or needs_more_evidence.
--reviewer and a non-empty --note are required. The decision is written to
preserved-artifact-reviews.json in the set through the crash-consistent
decision transaction, with an audit event; `sbomflow decisions
transactions|verify-transactions|recover|quarantine --store DIR` work on the
set. A later decision for the same artifact supersedes the earlier one, which
is kept. Offline; no run is needed, and nothing here affects a release gate.
FactValue
Groupruntime-evidence — Runtime & test evidence custody
Stabilityexperimental
Regimeany
Readsone custody set and the artifact's review key.
Writespreserved-artifact-reviews.json in the set, with an audit event; custody-review-result.json (with --json, the decision result on stdout)
Networkoffline — This command has no network-capable path.
Exit codes0 ok · 2 a refused decision: no reviewer, no note, or an unknown key
Machine outputhas-json — one sbomflow-custody-review-result-v1 document: the key, status, reviewer, operation id and whether a keyed retry converged

Options

OptionValueDefaultAllowedRequiredHelp
-h, --help(flag)none—noshow this help message and exit
DIRDIRnoneanyyesThe custody set.
--acceptKEYnoneanynoRecord accepted for this full review key.
--rejectKEYnoneanynoRecord rejected for this full review key.
--needs-more-evidenceKEYnoneanynoRecord needs_more_evidence for this full review key.
--reviewerNAMEnoneanynoWho decided (required).
--noteTEXTnoneanynoThe conclusion and its limits (required, non-empty).
--reviewed-atTnoneanynoWhen the reviewer decided (default: --as-of, else now).
--idempotency-keyKnoneanynoOptional stable key so a retried decision converges to ONE operation. Same key and same decision returns the existing operation; the same key for a different decision is refused. 1-128 chars [A-Za-z0-9._-], scoped to this set.
--json(flag)false—noPrint the decision result as JSON.
--as-ofAS_OFnoneanynoTool time: pins generated_at and audit event timestamps, so output is byte-identical across runs (default: now, UTC). It is never a capture time.

Examples

Accept an artifact, naming who decided and the limits of the conclusion.

sh
sbomflow custody review custody-set --accept preserved-artifact:sha256:c640b55149337567f8395db0fd66102ecb98006a9762a67cbf62114a6932d7a2 --reviewer lab-lead --note 'Trace covers boot to idle; no fault path exercised.' --as-of 2026-06-20T12:00:00Z

Ask for more evidence, reporting the result as JSON.

sh
sbomflow custody review custody-set --needs-more-evidence preserved-artifact:sha256:c640b55149337567f8395db0fd66102ecb98006a9762a67cbf62114a6932d7a2 --reviewer lab-lead --note 'Need the matching firmware build id.' --json

Refuse a decision with no note saying what was concluded. Exits 2.

sh
sbomflow custody review custody-set --accept preserved-artifact:sha256:c640b55149337567f8395db0fd66102ecb98006a9762a67cbf62114a6932d7a2 --reviewer lab-lead

See also: sbomflow custody show, sbomflow review, sbomflow decisions transactions, sbomflow help goal custody.

sbomflow custody show#

Show one artifact in a custody set and its records.

text
Show one row and its records. Lock-free and read-only.
FactValue
Groupruntime-evidence — Runtime & test evidence custody
Stabilityexperimental
Regimeany
Readsone custody set; lock-free and read-only.
Writescustody-show.json (with --json on stdout; text otherwise)
Networkoffline — This command has no network-capable path.
Exit codes0 ok · 2 no artifact by that key
Machine outputhas-json — one sbomflow-custody-show-v1 document: the row with its decision, its records, and the set's decision file state

Options

OptionValueDefaultAllowedRequiredHelp
-h, --help(flag)none—noshow this help message and exit
DIRDIRnoneanyyesThe custody set.
KEYKEYnoneanyyesA review key, or a unique sha256 prefix of at least 12 lowercase hex characters.
--json(flag)false—noPrint the row and its records as JSON.
--as-ofAS_OFnoneanynoTool time: pins generated_at and audit event timestamps, so output is byte-identical across runs (default: now, UTC). It is never a capture time.

Examples

Show one artifact and its records by a unique sha256 prefix.

sh
sbomflow custody show custody-set c640b5514933

The same, by full review key, as JSON.

sh
sbomflow custody show custody-set preserved-artifact:sha256:c640b55149337567f8395db0fd66102ecb98006a9762a67cbf62114a6932d7a2 --json

See also: sbomflow custody list, sbomflow custody review.

sbomflow custody verify#

Re-hash a custody set's records and originals and check its audit chain and seal.

text
Verify a custody set. Lock-free and read-only. Exit 0 when intact, 4 on any
integrity problem (the report names each).
FactValue
Groupruntime-evidence — Runtime & test evidence custody
Stabilityexperimental
Regimeany
Readsone custody set or export, and optionally the originals and an expected seal; lock-free and read-only.
Writescustody-verify.json (with --json on stdout; text otherwise)
Networkoffline — This command has no network-capable path.
Exit codes0 intact · 2 not a custody set or export · 4 an integrity problem, each one named
Machine outputhas-json — one sbomflow-custody-verify-v1 report; exit 4 on any integrity problem

Options

OptionValueDefaultAllowedRequiredHelp
-h, --help(flag)none—noshow this help message and exit
DIRDIRnoneanyyesThe custody set.
--originalsDIR2noneanynoDirectory to look for originals in, by content (size, then sha256), never by name.
--expected-sealSEALnoneanynoThe 64-hex seal recorded earlier; a different seal is an integrity problem.
--public-keyPEMnoneanynoPublic key for an export's optional signature. Without it, and without the sign extra, a signature present is reported present_unverified, never verified.
--strict(flag)false—noAlso require every original verified and no orphan files.
--json(flag)false—noPrint the verification report as JSON.
--as-ofAS_OFnoneanynoTool time: pins generated_at and audit event timestamps, so output is byte-identical across runs (default: now, UTC). It is never a capture time.

Examples

Re-hash the set and check its audit chain and seal.

sh
sbomflow custody verify custody-set

Also require every original to be found, by content, and no orphan files.

sh
sbomflow custody verify custody-set --originals captures --strict

A seal that differs from the one recorded earlier is an integrity problem and exits 4. Exits 4.

sh
sbomflow custody verify custody-set --expected-seal 0000000000000000000000000000000000000000000000000000000000000000

See also: sbomflow custody export, sbomflow custody compare, sbomflow store verify.

sbomflow ingest-preserved-artifact#

Preserve a LOCAL analysis artifact by identity and associate it with a release.

text
Preserve one local analysis artifact (a trace, a core dump, a test
report) by identity: its bytes are digested, and what the operator
states about it is recorded as a statement. Offline; the tool that
produced it is never run, and a record is never a gate input, a VEX
status or an approval. A release run reads the record only when
sbomflow.yaml declares it under preserved_artifacts:; no run searches
a directory for records.
FactValue
Groupruntime-evidence — Runtime & test evidence custody
Stabilitystable
Regimeany
Readsthe artifact, digested; where a registered reader claims its format, the identity fields its header declares, shown and never stored in the record; and what the operator states about it.
Writespreserved-artifact.json (into --output-dir, or at --output; with --json on stdout instead)
--output namesone file
Networkoffline — This command has no network-capable path.
Exit codes0 ok · 2 a missing or symlinked artifact, a refused value or a destination collision
Machine outputhas-json — preserves one operator-selected artifact by identity and records what a human asserted about it; one versioned JSON document on stdout, never parsed, never a gate

Options

OptionValueDefaultAllowedRequiredHelp
-h, --help(flag)none—noshow this help message and exit
artifactartifactnoneanyyesThe file to preserve (read-only; never modified). Its bytes are digested; only the header fields a registered reader names are read, and only to show them.
--release-idRELEASE_IDnoneanynoThe release this belongs to. Recorded as an ASSERTION a human made, never as a fact read out of the artifact.
--scenarioSCENARIOnoneanynoThe test scenario or session this artifact came from (an assertion).
--tool-nameTOOL_NAMEnoneanynoThe tool that produced it, as stated by the operator (an assertion).
--tool-versionTOOL_VERSIONnoneanynoThat tool's version, as stated by the operator (an assertion).
--notesNOTESnoneanynoCapture conditions worth recording for whoever reviews this.
--artifact-kindKINDnoneanynoWhat the evidence IS, as stated by the operator (an assertion), from a closed vocabulary: runtime_trace, core_dump, crash_report, device_log, bench_log, network_capture, test_report, test_results, coverage_report, fuzzing_campaign, fuzzing_reproducer, static_analysis_report, penetration_test_report, laboratory_test_report, certification_or_approval_record, conformance_test_report, vulnerability_handling_record, signing_record, provisioning_record, device_state_statement, timestamp_or_transparency_receipt, other. Never inferred from the filename, the extension or the bytes: omit it and the record states no kind rather than guessing one. A value outside the vocabulary is refused.
--valid-untilDATEnoneanynoWith --artifact-kind certification_or_approval_record or conformance_test_report: the date the record states it applies until, checked by the --as-of validator and recorded as typed. A run compares it with --as-of and decides nothing from it; refused on any other kind.
--firmware-imagePATHnoneanynoThe firmware image this capture was taken on. Its bytes are hashed (never parsed) and the record keeps the SHA-256 and the file NAME only — never the host path. A symlink, a device or a FIFO is refused. This records that you state the capture ran on that image; SBOMFlow does not observe it.
--firmware-sha256HEXnoneanynoThat image's SHA-256, when you have the digest but not the file: 64 lowercase hex characters. Given together with --firmware-image the two must agree, or both values are named and nothing is written.
--firmware-build-idHEXnoneanynoThe image's GNU build id, as stated by the operator: an even-length run of 2 to 128 lowercase hex characters. A leading 0x is refused rather than stripped. A build id is not a checksum — it survives stripping.
--firmware-versionTEXTnoneanynoThe firmware version the device or the build reported, as stated by the operator (at most 128 characters, no control characters). A version string is never a stand-in for a digest.
--firmware-inner-hashALG:HEXnoneanynoThe image hash an MCUboot image carries in its own hash TLV, as a device manager reports it: ALG is sha256, sha384 or sha512 and HEX is lowercase hex of that algorithm's length. Not the file's SHA-256, and never a version string. A run compares it with the hash the declared image states.
--capture-sessionIDnoneanynoAn id joining several records from one capture, alert, campaign or engagement, as the operator states it. Contact-free, same rule as --operator. Nothing groups files on its own.
--performerIDnoneanynoWho performed the test, as a lab, team or role identifier — never a personal contact. Same identifier rule as --operator, for the same reason: this value may leave the company inside a record.
--independenceRELATIONSHIPnonesame_team, independent_internal, third_partynoThe relationship between the tester and the team that built the thing. The set is closed and has no certified or accredited value: SBOMFlow verifies no qualification and will not record a claim it cannot support. One of: same_team, independent_internal, third_party.
--test-environmentTEXTnoneanynoThe environment or rig tested on, as stated by the operator (at most 256 characters, no control characters).
--configuration-filePATHnoneanynoA rig configuration, dictionary or profile to record by NAME AND DIGEST. The file is hashed and never parsed, so what it contains stays yours while a reviewer can still tell it is the file that was used.
--procedure-refTEXTnoneanynoThe procedure or test-plan reference followed, as stated (at most 128 characters). SBOMFlow never dereferences it.
--original-locationURInoneanynoWhere the original is kept, as you state it: an s3:, gs:, az:, https:, file: or urn: location, or an opaque id (at most 512 characters). Never fetched, resolved or opened. Refused when it carries a user name, a query string or fragment, a presigned-URL parameter, or an absolute path on this host.
--retain-untilDATEnoneanynoThe date you state the original must be kept until, checked by the --as-of validator and recorded as typed. SBOMFlow computes no retention period and keeps no original.
--rootROOTnoneanynoContainment root: refuse an artifact path that resolves outside it, rather than reading something from elsewhere on the host.
--as-ofAS_OFnoneanynoWhen the capture was taken, as you state it (an assertion); never defaulted to the clock, which only says when this command ran.
--operatorOPERATORnoneanynoWho preserved it, as you state it (an assertion); never defaulted to the OS user, who only ran the shell.
--outputOUTPUTnoneanynoWrite preserved-artifact.json here (file). Defaults to the output directory when one is given, so the record lands beside the rest of the run's evidence rather than beside the operator's input file. Give each artifact its own path, for example preserved/<scenario>.preserved-artifact.json: a destination that already holds a different record (a regular file, or a symlink to one) is refused (E019) and never overwritten. Re-running with identical inputs succeeds and changes nothing.
--output-dir, --evidence-dirEVIDENCE_DIRnoneanynoThe run's evidence directory (also spelled --evidence-dir). The record is written into it as preserved-artifact.json unless --output names a path, so a second artifact for the same directory needs its own --output.
--json(flag)false—noPrint JSON to stdout instead.

Examples

Preserve a captured trace by identity beside a release's evidence.

sh
sbomflow ingest-preserved-artifact captures/boot-to-idle.psf --output-dir out --release-id 2.4.1 --artifact-kind runtime_trace --operator bench-team --as-of 2026-06-20T12:00:00Z

State the tool and the firmware image it was captured on, printing the record.

sh
sbomflow ingest-preserved-artifact captures/boot-to-idle.psf --release-id 2.4.1 --scenario boot-to-idle --tool-name Percepio-Tracealyzer --tool-version 4.9 --firmware-image demo-firmware/zephyr.elf --as-of 2026-06-20T12:00:00Z --json

Record a core dump in its own record file, with its capture session and who performed the test.

sh
sbomflow ingest-preserved-artifact captures/cc_coredump.dmp --output preserved/cc_coredump.preserved-artifact.json --artifact-kind core_dump --capture-session field-2026-06 --performer lab-7 --independence independent_internal --as-of 2026-06-20T12:00:00Z

Refuse a kind outside the closed vocabulary; nothing is written. Exits 2.

sh
sbomflow ingest-preserved-artifact captures/boot-to-idle.psf --output-dir out --artifact-kind not-a-kind

See also: sbomflow custody add, sbomflow review, sbomflow ingest-codex-security, sbomflow help goal custody.

sbomflow twister-evidence#

Import Zephyr Twister results (twister.json) into deterministic test evidence (offline).

text
Read a Zephyr Twister output directory's twister.json and record each suite
and its outcome as deterministic test evidence. Failed and errored suites are
recorded as they are, not hidden.
FactValue
Groupruntime-evidence — Runtime & test evidence custody
Stabilitystable
Regimeany
Readsa Zephyr Twister output directory.
Writesstdout (twister-evidence.json, or to the file --output names)
--output namesone file (stdout when omitted)
Networkoffline — This command has no network-capable path.
Exit codes0 read, including suites that failed, which are recorded · 2 not a Twister output directory
Machine outputmachine-native — prints its Twister test-result evidence as JSON on stdout by default, no flag needed; --output writes the same document to a file instead

Options

OptionValueDefaultAllowedRequiredHelp
-h, --help(flag)none—noshow this help message and exit
pathpathnoneanyyesA twister.json, a twister-out dir, or its parent.
--outputOUTPUTnoneanynoWrite twister-evidence.json here (file; default stdout).

Examples

Record a Twister run's suites and their outcomes.

sh
sbomflow twister-evidence twister-out

Write the document to a file.

sh
sbomflow twister-evidence twister-out --output twister-evidence.json

See also: sbomflow zephyr-evidence, sbomflow ingest-preserved-artifact, sbomflow help goal firmware.