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.
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.| Fact | Value |
|---|---|
| Group | runtime-evidence — Runtime & test evidence custody |
| Stability | experimental |
| Regime | any |
| Reads | nothing itself; its verbs read and write one custody set. |
| Writes | nothing itself; each verb's help names what it writes |
| Network | offline — This command has no network-capable path. |
| Exit codes | 0 a verb ran and returned 0 · 2 no verb given, or the verb refused |
| Machine output | human-only-by-design — group dispatcher: prints usage/guidance only; the machine surfaces are its nested verbs |
Options
| Option | Value | Default | Allowed | Required | Help |
|---|---|---|---|---|---|
-h, --help | (flag) | none | — | no | show this help message and exit |
Examples
Create an empty custody set for one release's runtime evidence.
sbomflow custody init custody-set --set-id bench --release-id 2.4.1Admit a captured trace into the set.
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:00ZRe-hash the set and check its audit chain and seal.
sbomflow custody verify custody-setSee 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.
Admit one artifact or record into a custody set, all or nothing.| Fact | Value |
|---|---|
| Group | runtime-evidence — Runtime & test evidence custody |
| Stability | experimental |
| Regime | any |
| Reads | the artifact (or an existing preserved-artifact record, or an intake manifest) and the set. |
| Writes | custody-set.json (the set's index, with the record and an audit event); custody-operation.json (with --json, the operation result on stdout) |
| Network | offline — This command has no network-capable path. |
| Exit codes | 0 ok · 2 a refused artifact, record or set |
| Machine output | has-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
| Option | Value | Default | Allowed | Required | Help |
|---|---|---|---|---|---|
-h, --help | (flag) | none | — | no | show this help message and exit |
DIR | DIR | none | any | yes | The custody set. |
[ARTIFACT] | [ARTIFACT] | none | any | no | The artifact file to preserve (artifact form). |
--record | RECORD | none | any | no | An existing sbomflow-preserved-artifact-v1 record to admit (record form). |
--original | ORIGINAL | none | any | no | Record form: the artifact the record describes, re-hashed at admission. |
--intake-manifest | FILE | none | any | no | Intake form: an sbomflow-custody-intake-v1 manifest naming each file to add. All of its entries are admitted, or none are. |
--release-id | RELEASE_ID | none | any | no | Artifact form: the release this artifact belongs to. |
--scenario | SCENARIO | none | any | no | Artifact form: the test scenario it was captured in. |
--tool-name | TOOL_NAME | none | any | no | Artifact form: the tool that produced it. |
--tool-version | TOOL_VERSION | none | any | no | Artifact form: that tool's version. |
--notes | NOTES | none | any | no | Artifact form: capture notes. |
--operator | OPERATOR | none | any | no | Artifact form: contact-free identifier of who preserved it. |
--artifact-kind | KIND | none | any | no | Artifact 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-image | PATH | none | any | no | Artifact 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-sha256 | HEX | none | any | no | Artifact 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-id | HEX | none | any | no | Artifact 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-version | TEXT | none | any | no | Artifact 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-hash | ALG:HEX | none | any | no | Artifact 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-session | ID | none | any | no | Artifact form: an id joining several records from one capture, alert or campaign. Contact-free, same rule as --operator. |
--performer | ID | none | any | no | Artifact form: who performed the test, as a lab, team or role id and never a personal contact. Same rule as --operator. |
--independence | RELATIONSHIP | none | same_team, independent_internal, third_party | no | Artifact 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-environment | TEXT | none | any | no | Artifact form: the environment or rig tested on, as you state it (at most 256 characters, no control characters). |
--configuration-file | PATH | none | any | no | Artifact form: a configuration, dictionary or profile recorded by name and digest. Hashed, never parsed. |
--procedure-ref | TEXT | none | any | no | Artifact form: the procedure or test-plan reference followed, as stated (at most 128 characters). Never dereferenced. |
--original-location | URI | none | any | no | Artifact 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-until | DATE | none | any | no | Artifact form: the date you state the original must be kept until, recorded as typed. No retention period is computed. |
--valid-until | DATE | none | any | no | Artifact 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 | — | no | Keep a local copy under originals/ in this set (streamed and checked; never exported). |
--break-stale-lock | (flag) | false | — | no | Artifact form: remove a provably stale set lock left by a crashed command. |
--captured-at | TIMESTAMP | none | any | no | Artifact 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 | — | no | Print the operation result as JSON. |
--as-of | AS_OF | none | any | no | Tool 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.
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:00ZAdmit an existing preserved-artifact record, re-hashing the original it describes.
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:00ZReport the admission as JSON.
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 --jsonRefuse an artifact that states a release the set is not for. Exits 2.
sbomflow custody add custody-set captures/boot-to-idle.psf --release-id 9.9.9See 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.
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.| Fact | Value |
|---|---|
| Group | runtime-evidence — Runtime & test evidence custody |
| Stability | experimental |
| Regime | any |
| Reads | two custody sets or exports, or a set and a release run's preserved-artifacts.json; artifact bytes are never compared. |
| Writes | custody-comparison.json (with --json on stdout, or at --output) |
--output names | one file |
| Network | offline — This command has no network-capable path. |
| Exit codes | 0 compared, or reported NOT COMPARED with the reason · 2 usage error |
| Machine output | has-json — one sbomflow-custody-comparison-v1 document: what two sides record differently, or comparable: false with the reason nothing was compared |
Options
| Option | Value | Default | Allowed | Required | Help |
|---|---|---|---|---|---|
-h, --help | (flag) | none | — | no | show this help message and exit |
A | A | none | any | yes | A custody set, an export directory or an export zip. |
[B] | [B] | none | any | no | The other side: a set, an export directory or an export zip. |
--run | OUTPUT_DIR | none | any | no | Compare A against a release run's preserved-artifacts.json instead of B. |
--output | FILE | none | any | no | Write the comparison document here (file); nothing is written without it. |
--json | (flag) | false | — | no | Print the comparison document as JSON. |
--as-of | AS_OF | none | any | no | Tool 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.
sbomflow custody compare custody-set custody-exportCompare a set against a release run's preserved artifacts.
sbomflow custody compare custody-set --run outA side that is not there is reported as not compared, never as no change.
sbomflow custody compare missing-set custody-exportSee 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.
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.| Fact | Value |
|---|---|
| Group | runtime-evidence — Runtime & test evidence custody |
| Stability | experimental |
| Regime | any |
| Reads | one custody set; original artifact bytes are never exported. |
| Writes | custody-export.json (the manifest, with the index, records, decisions and audit log, into --output); custody-export.zip with --zip |
--output names | a directory |
| Network | offline — This command has no network-capable path. |
| Exit codes | 0 ok · 2 a missing set, or a refused preflight |
| Machine output | has-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
| Option | Value | Default | Allowed | Required | Help |
|---|---|---|---|---|---|
-h, --help | (flag) | none | — | no | show this help message and exit |
DIR | DIR | none | any | yes | The custody set to export. |
--output | OUT | none | any | yes | Where the export is written (directory); it must be absent or empty, and is never a symlink. |
--zip | (flag) | false | — | no | Also write a byte-reproducible custody-export.zip inside OUT. |
--recipient | NAME | none | any | no | Contact-free identifier of who this package was prepared for. Recorded in the manifest; it is not an access control and filters nothing. |
--select-key | KEY | none | any | no | Export 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-session | ID | none | any | no | Export only the rows whose records state this capture session (with --select-key, the rows either selects). A session no row states is refused. |
--key | PEM | none | any | no | Private key to sign the export manifest with (requires --key-id and the sign extra). |
--key-id | ID | none | any | no | Identifier recorded in the signature for the key used (requires --key). |
--dry-run | (flag) | false | — | no | Print what would be exported and the preflight verdict; write nothing. |
--json | (flag) | false | — | no | Print the export manifest as JSON. |
--as-of | AS_OF | none | any | no | Tool 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.
sbomflow custody export custody-set --output custody-export --as-of 2026-06-20T12:00:00ZAlso write the byte-reproducible zip, naming who it is for.
sbomflow custody export custody-set --output custody-export --zip --recipient example-auditor --as-of 2026-06-20T12:00:00ZPrint what would be exported and the preflight verdict, writing nothing.
sbomflow custody export custody-set --output custody-export --dry-runSee 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.
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.| Fact | Value |
|---|---|
| Group | runtime-evidence — Runtime & test evidence custody |
| Stability | experimental |
| Regime | any |
| Reads | one custody set, its index and its audit log. |
| Writes | the 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) |
| Network | offline — This command has no network-capable path. |
| Exit codes | 0 ok · 2 a set already held, a missing or contact-carrying actor, or a missing reason |
| Machine output | has-json — one sbomflow-custody-hold-v1 document: the hold as the set now records it and the audit event that binds it |
Options
| Option | Value | Default | Allowed | Required | Help |
|---|---|---|---|---|---|
-h, --help | (flag) | none | — | no | show this help message and exit |
DIR | DIR | none | any | yes | The custody set. |
--actor | ACTOR | none | any | yes | Named person placing the hold (a contact-free identifier); recorded with it. |
--reason | REASON | none | any | yes | Why the hold is placed, recorded exactly as stated (at most 512 characters). |
--as-of | AS_OF | none | any | no | Pinned timestamp recorded as when the hold was placed (default: now, UTC). |
--json | (flag) | false | — | no | Print the hold as JSON. |
Examples
Place a preservation hold on a custody set, naming who placed it and why.
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:00ZThe same, printing the hold as JSON.
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 --jsonRefuse a second hold on a set that is already held, naming the first. Exits 2.
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.
Create a custody set: the marker, an empty index and the first audit event.| Fact | Value |
|---|---|
| Group | runtime-evidence — Runtime & test evidence custody |
| Stability | experimental |
| Regime | any |
| Reads | nothing but the directory named, which must be empty or absent. |
| Writes | custody-set.json (an empty index, with the marker and the first audit event); custody-operation.json (with --json, the operation result on stdout) |
| Network | offline — This command has no network-capable path. |
| Exit codes | 0 ok · 2 a non-empty directory or a refused identifier |
| Machine output | has-json — one sbomflow-custody-operation-v1 document for the new set |
Options
| Option | Value | Default | Allowed | Required | Help |
|---|---|---|---|---|---|
-h, --help | (flag) | none | — | no | show this help message and exit |
DIR | DIR | none | any | yes | Empty or absent directory for the set (its parent must exist). |
--set-id | SET_ID | none | any | yes | Contact-free set identifier (lowercase letters, digits, . _ -). |
--product | PRODUCT | none | any | no | Product the set is for, as you state it. |
--release-id | RELEASE_ID | none | any | no | Release the set is for; artifacts stating another release are refused. |
--issued-by | NAME | none | any | no | Contact-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-note | RETENTION_NOTE | none | any | no | How long you state the originals must be kept, in your own words (recorded verbatim, never interpreted, compared or checked). |
--json | (flag) | false | — | no | Print the operation result as JSON. |
--as-of | AS_OF | none | any | no | Tool 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.
sbomflow custody init custody-set --set-id bench --release-id 2.4.1Record who issued the evidence and the stated retention, as JSON.
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:00ZRefuse a directory that already holds something. Exits 2.
sbomflow custody init out --set-id benchSee also: sbomflow custody add, sbomflow custody list, sbomflow help goal custody.
sbomflow custody list#
List the artifacts in a custody set.
List a custody set's rows. Lock-free and read-only.| Fact | Value |
|---|---|
| Group | runtime-evidence — Runtime & test evidence custody |
| Stability | experimental |
| Regime | any |
| Reads | one custody set's index; lock-free and read-only. |
| Writes | stdout (one row per artifact; --json prints the index as JSON) |
| Network | offline — This command has no network-capable path. |
| Exit codes | 0 ok · 2 not a usable custody set |
| Machine output | has-json — prints the set index, sbomflow-custody-set-v1, verbatim |
Options
| Option | Value | Default | Allowed | Required | Help |
|---|---|---|---|---|---|
-h, --help | (flag) | none | — | no | show this help message and exit |
DIR | DIR | none | any | yes | The custody set. |
--json | (flag) | false | — | no | Print the set index as JSON. |
--capture-session | ID | none | any | no | Show 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-of | AS_OF | none | any | no | Tool 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.
sbomflow custody list custody-setPrint the set index as JSON.
sbomflow custody list custody-set --jsonOnly the rows of one capture session, saying how many matched.
sbomflow custody list custody-set --capture-session field-2026-06See also: sbomflow custody show, sbomflow custody verify.
sbomflow custody release-hold#
Lift a custody set's preservation hold, naming who lifted it and why.
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.| Fact | Value |
|---|---|
| Group | runtime-evidence — Runtime & test evidence custody |
| Stability | experimental |
| Regime | any |
| Reads | one custody set, its index and its audit log. |
| Writes | the 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) |
| Network | offline — This command has no network-capable path. |
| Exit codes | 0 ok · 2 a set that is not held, or a missing actor or reason |
| Machine output | has-json — one sbomflow-custody-hold-v1 document: the lifted hold as the set now records it and the audit event that binds it |
Options
| Option | Value | Default | Allowed | Required | Help |
|---|---|---|---|---|---|
-h, --help | (flag) | none | — | no | show this help message and exit |
DIR | DIR | none | any | yes | The custody set. |
--actor | ACTOR | none | any | yes | Named person lifting the hold (a contact-free identifier); recorded with it. |
--reason | REASON | none | any | yes | Why the hold is lifted, recorded exactly as stated (at most 512 characters). |
--as-of | AS_OF | none | any | no | Pinned timestamp recorded as when the hold was lifted (default: now, UTC). |
--json | (flag) | false | — | no | Print the hold as JSON. |
Examples
Lift the hold; its record stays in the set, flagged as lifted.
sbomflow custody release-hold custody-set --actor records-team --reason 'The field-return review closed.' --as-of 2026-06-22T12:00:00ZRefuse to lift a hold on a set that is not held. Exits 2.
sbomflow custody release-hold custody-set --actor records-team --reason 'The field-return review closed.' --as-of 2026-06-22T12:00:00ZSee 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.
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.| Fact | Value |
|---|---|
| Group | runtime-evidence — Runtime & test evidence custody |
| Stability | experimental |
| Regime | any |
| Reads | one custody set and the artifact's review key. |
| Writes | preserved-artifact-reviews.json in the set, with an audit event; custody-review-result.json (with --json, the decision result on stdout) |
| Network | offline — This command has no network-capable path. |
| Exit codes | 0 ok · 2 a refused decision: no reviewer, no note, or an unknown key |
| Machine output | has-json — one sbomflow-custody-review-result-v1 document: the key, status, reviewer, operation id and whether a keyed retry converged |
Options
| Option | Value | Default | Allowed | Required | Help |
|---|---|---|---|---|---|
-h, --help | (flag) | none | — | no | show this help message and exit |
DIR | DIR | none | any | yes | The custody set. |
--accept | KEY | none | any | no | Record accepted for this full review key. |
--reject | KEY | none | any | no | Record rejected for this full review key. |
--needs-more-evidence | KEY | none | any | no | Record needs_more_evidence for this full review key. |
--reviewer | NAME | none | any | no | Who decided (required). |
--note | TEXT | none | any | no | The conclusion and its limits (required, non-empty). |
--reviewed-at | T | none | any | no | When the reviewer decided (default: --as-of, else now). |
--idempotency-key | K | none | any | no | Optional 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 | — | no | Print the decision result as JSON. |
--as-of | AS_OF | none | any | no | Tool 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.
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:00ZAsk for more evidence, reporting the result as JSON.
sbomflow custody review custody-set --needs-more-evidence preserved-artifact:sha256:c640b55149337567f8395db0fd66102ecb98006a9762a67cbf62114a6932d7a2 --reviewer lab-lead --note 'Need the matching firmware build id.' --jsonRefuse a decision with no note saying what was concluded. Exits 2.
sbomflow custody review custody-set --accept preserved-artifact:sha256:c640b55149337567f8395db0fd66102ecb98006a9762a67cbf62114a6932d7a2 --reviewer lab-leadSee 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.
Show one row and its records. Lock-free and read-only.| Fact | Value |
|---|---|
| Group | runtime-evidence — Runtime & test evidence custody |
| Stability | experimental |
| Regime | any |
| Reads | one custody set; lock-free and read-only. |
| Writes | custody-show.json (with --json on stdout; text otherwise) |
| Network | offline — This command has no network-capable path. |
| Exit codes | 0 ok · 2 no artifact by that key |
| Machine output | has-json — one sbomflow-custody-show-v1 document: the row with its decision, its records, and the set's decision file state |
Options
| Option | Value | Default | Allowed | Required | Help |
|---|---|---|---|---|---|
-h, --help | (flag) | none | — | no | show this help message and exit |
DIR | DIR | none | any | yes | The custody set. |
KEY | KEY | none | any | yes | A review key, or a unique sha256 prefix of at least 12 lowercase hex characters. |
--json | (flag) | false | — | no | Print the row and its records as JSON. |
--as-of | AS_OF | none | any | no | Tool 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.
sbomflow custody show custody-set c640b5514933The same, by full review key, as JSON.
sbomflow custody show custody-set preserved-artifact:sha256:c640b55149337567f8395db0fd66102ecb98006a9762a67cbf62114a6932d7a2 --jsonSee 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.
Verify a custody set. Lock-free and read-only. Exit 0 when intact, 4 on any
integrity problem (the report names each).| Fact | Value |
|---|---|
| Group | runtime-evidence — Runtime & test evidence custody |
| Stability | experimental |
| Regime | any |
| Reads | one custody set or export, and optionally the originals and an expected seal; lock-free and read-only. |
| Writes | custody-verify.json (with --json on stdout; text otherwise) |
| Network | offline — This command has no network-capable path. |
| Exit codes | 0 intact · 2 not a custody set or export · 4 an integrity problem, each one named |
| Machine output | has-json — one sbomflow-custody-verify-v1 report; exit 4 on any integrity problem |
Options
| Option | Value | Default | Allowed | Required | Help |
|---|---|---|---|---|---|
-h, --help | (flag) | none | — | no | show this help message and exit |
DIR | DIR | none | any | yes | The custody set. |
--originals | DIR2 | none | any | no | Directory to look for originals in, by content (size, then sha256), never by name. |
--expected-seal | SEAL | none | any | no | The 64-hex seal recorded earlier; a different seal is an integrity problem. |
--public-key | PEM | none | any | no | Public 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 | — | no | Also require every original verified and no orphan files. |
--json | (flag) | false | — | no | Print the verification report as JSON. |
--as-of | AS_OF | none | any | no | Tool 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.
sbomflow custody verify custody-setAlso require every original to be found, by content, and no orphan files.
sbomflow custody verify custody-set --originals captures --strictA seal that differs from the one recorded earlier is an integrity problem and exits 4. Exits 4.
sbomflow custody verify custody-set --expected-seal 0000000000000000000000000000000000000000000000000000000000000000See 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.
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.| Fact | Value |
|---|---|
| Group | runtime-evidence — Runtime & test evidence custody |
| Stability | stable |
| Regime | any |
| Reads | the 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. |
| Writes | preserved-artifact.json (into --output-dir, or at --output; with --json on stdout instead) |
--output names | one file |
| Network | offline — This command has no network-capable path. |
| Exit codes | 0 ok · 2 a missing or symlinked artifact, a refused value or a destination collision |
| Machine output | has-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
| Option | Value | Default | Allowed | Required | Help |
|---|---|---|---|---|---|
-h, --help | (flag) | none | — | no | show this help message and exit |
artifact | artifact | none | any | yes | The 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-id | RELEASE_ID | none | any | no | The release this belongs to. Recorded as an ASSERTION a human made, never as a fact read out of the artifact. |
--scenario | SCENARIO | none | any | no | The test scenario or session this artifact came from (an assertion). |
--tool-name | TOOL_NAME | none | any | no | The tool that produced it, as stated by the operator (an assertion). |
--tool-version | TOOL_VERSION | none | any | no | That tool's version, as stated by the operator (an assertion). |
--notes | NOTES | none | any | no | Capture conditions worth recording for whoever reviews this. |
--artifact-kind | KIND | none | any | no | What 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-until | DATE | none | any | no | With --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-image | PATH | none | any | no | The 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-sha256 | HEX | none | any | no | That 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-id | HEX | none | any | no | The 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-version | TEXT | none | any | no | The 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-hash | ALG:HEX | none | any | no | The 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-session | ID | none | any | no | An 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. |
--performer | ID | none | any | no | Who 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. |
--independence | RELATIONSHIP | none | same_team, independent_internal, third_party | no | The 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-environment | TEXT | none | any | no | The environment or rig tested on, as stated by the operator (at most 256 characters, no control characters). |
--configuration-file | PATH | none | any | no | A 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-ref | TEXT | none | any | no | The procedure or test-plan reference followed, as stated (at most 128 characters). SBOMFlow never dereferences it. |
--original-location | URI | none | any | no | Where 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-until | DATE | none | any | no | The 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. |
--root | ROOT | none | any | no | Containment root: refuse an artifact path that resolves outside it, rather than reading something from elsewhere on the host. |
--as-of | AS_OF | none | any | no | When the capture was taken, as you state it (an assertion); never defaulted to the clock, which only says when this command ran. |
--operator | OPERATOR | none | any | no | Who preserved it, as you state it (an assertion); never defaulted to the OS user, who only ran the shell. |
--output | OUTPUT | none | any | no | Write 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-dir | EVIDENCE_DIR | none | any | no | The 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 | — | no | Print JSON to stdout instead. |
Examples
Preserve a captured trace by identity beside a release's evidence.
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:00ZState the tool and the firmware image it was captured on, printing the record.
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 --jsonRecord a core dump in its own record file, with its capture session and who performed the test.
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:00ZRefuse a kind outside the closed vocabulary; nothing is written. Exits 2.
sbomflow ingest-preserved-artifact captures/boot-to-idle.psf --output-dir out --artifact-kind not-a-kindSee 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).
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.| Fact | Value |
|---|---|
| Group | runtime-evidence — Runtime & test evidence custody |
| Stability | stable |
| Regime | any |
| Reads | a Zephyr Twister output directory. |
| Writes | stdout (twister-evidence.json, or to the file --output names) |
--output names | one file (stdout when omitted) |
| Network | offline — This command has no network-capable path. |
| Exit codes | 0 read, including suites that failed, which are recorded · 2 not a Twister output directory |
| Machine output | machine-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
| Option | Value | Default | Allowed | Required | Help |
|---|---|---|---|---|---|
-h, --help | (flag) | none | — | no | show this help message and exit |
path | path | none | any | yes | A twister.json, a twister-out dir, or its parent. |
--output | OUTPUT | none | any | no | Write twister-evidence.json here (file; default stdout). |
Examples
Record a Twister run's suites and their outcomes.
sbomflow twister-evidence twister-outWrite the document to a file.
sbomflow twister-evidence twister-out --output twister-evidence.jsonSee also: sbomflow zephyr-evidence, sbomflow ingest-preserved-artifact, sbomflow help goal firmware.