Compare releases and keep history

Compare releases, keep an index, render dashboards. This page documents every command in the history group, in the order sbomflow help history 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 compare-releases · sbomflow index-releases · sbomflow dashboard · sbomflow portfolio.
  • sbomflow compare-releases --require-both-releases — fail when the comparison did not happen, instead of letting an uncompared run read as "no drift". Every surface that shows a drift count now states whether a comparison occurred at all, and a comparison with a missing side no longer reports the absent release as change.

With a family.json beside the portfolio config, portfolio also reports the latest release per channel|variant across a declared family's member products — the question a multi-variant manufacturer asks. Two members can legitimately claim the same channel|variant: the product name is part of a product's identity, so two products differing only by name are distinct products that share one key. When that happens no winner is picked, because releases of different products are not comparable. The key is left out of the merged map, every contender is listed under latest_by_channel_variant_collisions, and a warning names them.

A release that declares release.applies_to_variants in sbomflow.yaml is listed under each declared variant in releases_by_declared_variant, per product in index-releases and merged per family in portfolio, with applicability: declared, and under no variant it did not declare. A family in family.json may list its variants; the family view then lists each of them, with no release where none declares it, and validate-config, analyze and audit refuse a declared id the family does not list (E010).

  • sbomflow store add <output-dir> — index a release output. A pack that contradicts its own seal is refused. A directory whose run-completion.json no longer agrees with the bytes beside it, or one of whose artifacts is present but will not open, is rejected with the reason named. The unreadable case is refused in its own words: the repair is access, not re-running the analysis, and the two are never merged into one message.

A pack that is merely smaller than the full artifact set is admitted — the library writer and a backfilled declared record both legitimately produce one — but the verdict now travels with it. Every entry records completeness_state (the classifier's own word) and validation (passes, or rejected_by_validate), so a store listing can no longer present an interrupted run exactly as it presents a whole one, which is what it did when nothing was recorded at all.

Re-adding the same pack stays a no-op. Re-adding a different pack that happens to share an identity is refused: content_hash is the digest of evidence-pack.json alone, so a pack that has grown or lost other members matched an indexed entry and was reported Already present. A second, pack-wide digest now decides that question and names the members that differ. An entry indexed before that digest existed carries none, and the result says the comparison did not happen rather than guessing either way.

  • sbomflow store export --output <dir> — copy the whole estate out offline, plus a completeness-manifest.json that reconciles the export against the store's own index: every indexed release appears, exported or not, with a reason when not, alongside the whole-store seal and an explicit list of what the export does not cover. The copy mirrors the store layout, so it re-opens with the ordinary store commands — no import step, no SBOMFlow-only container. Exits non-zero if the estate is not fully accounted for. --dry-run reconciles and reports without writing copies, and reaches the same completeness verdict and exit code as a real export — so you can check whether an estate would travel intact before committing the disk.

A store this run could not enumerate is refused before anything is written. An export replaces whatever is at its destination, and it is assembled from the store index alone — release directories on disk are never consulted. So an index that is absent, will not parse, or records zero releases stops the export rather than producing an empty copy that declares itself complete: the export you already had is worth more than a fresh one that vouches for nothing. The manifest records store_index_state so a receiver can check that precondition rather than infer it, and complete can only be true when that state is read. Repair the index with sbomflow store reindex (see PB-12), confirm with sbomflow store verify, then export again.

Replacing an export never leaves you with neither version. The new copy is built and re-verified in a .<name>.staging directory beside the destination; only then is the previous export moved aside to .<name>.previous, the new one put in its place, and the moved-aside copy discarded. Nothing is deleted until the replacement is in place, so a publish that fails is a refusal (E051) with the previous export exactly where it was, never a destination holding nothing. Each of those moves is a single atomic rename, so the destination is always one whole export and never a mixture of two — but the pair is not atomic, so a machine that dies mid-swap can leave the destination briefly absent with the previous export sitting at .<name>.previous. Both dot-named directories are SBOMFlow's own working copies: rename .<name>.previous back to the destination to recover the export you had, or simply run the export again, which restores it first and then replaces it. Nothing here calls fsync, so what survives a power cut remains the filesystem's business; what is guaranteed is that no export is destroyed before its replacement is in place.

An export is a share boundary, so it runs a preflight first and REFUSES rather than publishing something sensitive. The preflight reads the store's own members before anything is written, so a refusal exports nothing at all and any previous export at that destination is left exactly as it was. It reports the member path and the violation class — never the value. Inspected classes: secret-shaped filenames, private-key material, credential/token material, absolute machine paths, and the same sharing contract the reviewer bundle uses, applied to JSON and JSONL members.

"Absolute machine path" means exactly that. A repository-relative path that merely passes through a directory called home/ or Users/ — web/home/page.tsx — is ordinary evidence and does not refuse the export. Likewise the sharing contract's hostname class is a machine name, not any word that resembles one: a component called node-red or a file called host_config.json travels. A JSON Pointer SBOMFlow itself writes — the /vulnerabilities in the CSAF sentence of an audit with nothing to state, or the /schemaVersion a strict reader names — is not a machine path either, so such a release exports; a host path beside it is still refused. See the redaction contract for the exact shapes.

SBOMFlow will not filter or redact the offending member for you: dropping a record would break the completeness guarantee the export exists to provide, and rewriting stored bytes would invalidate their recorded hashes. Resolve it at the source (remove the file from the release output and re-run store add), then export again.

What a clean preflight does and does not mean. It means those classes were not observed in the members that could be inspected. Members that could not be inspected — binary or non-UTF-8 content, or a structured member that did not parse — are listed and counted separately in the manifest, because "not inspected" is not "clean". It is not proof that an export is free of every possible secret, and it is never a statement about legal safety.

Note that analyzing with an absolute path records that path in the pack as provenance, and the preflight refuses to publish it at the share boundary. Run analyze from inside your workspace with a relative target if the estate is intended to travel.

  • sbomflow store reissue-plan — when store export refuses, diagnose why and plan a safe reissue. Read-only: it writes portable-reissue-plan.json and a rendering, never an export, and never modifies the store. It reads the export's OWN preflight result rather than a second detector, so a plan and an export cannot disagree. Each release is reported portable, requires_reanalysis_input or blocked; each finding names the member relative to its release and the violation class — never the matched value, path, credential or key material, because a diagnosis must not republish what the refusal exists to contain. Next actions are emitted as copy-pasteable commands only where every value is known and safe to print; otherwise they carry <PLACEHOLDERS> plus the exact inputs you must supply, because SBOMFlow does not retain your source checkout and will not invent one. Exits 0 even when work is required — a diagnosis is not a failure.

If the original source or config is gone, a release whose generated evidence carries machine identity cannot be made shareable by rewriting it: the audit log is hash-chained and every stored digest is asserted by the completeness manifest. That evidence stays fully usable locally.

A store this run could not enumerate is refused, not diagnosed. This is the command you reach for when store export already refused, so it runs precisely when the store is in trouble — including when the index is the thing that is missing. Every count and verdict in the plan is folded over the index entries, so an index nobody could read produced zero releases inspected and reissue_needed: false — "all of them can travel, nothing to do", for a store it never opened. It exits non-zero and names the state instead. See PB-12.

  • store verify, store retain, store empty-trash and store reissue-plan refuse an index they could not enumerate. Each folds its verdict over the store index, and an empty fold reads exactly like a healthy one — so a store that had lost its index.json verified clean, retained nothing, reported no release on hold, and needed no reissue. The read is a precondition now, and the refusal names which state it found: absent, unreadable (bytes denied or an I/O error) or malformed (bytes read, not a usable index). An index that was read and honestly records zero releases is the fourth state, empty, and every one of these commands behaves over it exactly as it always did. Repair with sbomflow store reindex — see PB-12.

store empty-trash --apply is the only path in the store that permanently deletes, and whether a staged release is under a preservation hold is recorded only in the index. It therefore cannot tell "no release is held" from "no hold could be read", and treats the second as a refusal: an unread input never widens what an irreversible step may delete. store retain --apply likewise writes nothing at all when the index it would rewrite could not be read — it used to create one asserting the store held zero releases.

  • store query and store timeline refuse the same three states. They only report, so nothing is deleted or overwritten — but "no release matches your filters", "no stored release contains a matching finding" and "not observed in any stored release" are exactly what an operator sees when the index is gone and every pack is still on disk. That last one is decision memory answering the audit-day question, so the reassuring answer is the costly one. They exit non-zero and name the state instead. An index that was read and honestly records zero releases still answers normally.

store list is the exception, and only for absent. It shares its reader with the cross-release context analyze builds (aging, regression, carry-forward), which runs against a store directory you may have created and never added to — a CI job that pre-creates its evidence store has exactly that shape on its first run, and refusing there would fail an analysis with nothing wrong with it. So store list still prints an empty list for a store whose index.json is missing. Use store verify, which refuses, to tell an empty store from a store that lost its index. An index that is present and unusable (unreadable or malformed) is refused by store list.

  • sbomflow migrate-in <dir> — import another tool's estate (Trivy / Grype / SARIF exports) as origin-labelled claims, composing the importers that already ship. Every row carries claim_origin; nothing becomes a reviewer decision, VEX status, approval or gate input, because an incumbent tool's status is a claim about what that tool said, not authority over your product. Files it cannot read or recognise are listed with the reason rather than skipped, so a migration never looks more complete than it was. Every entry the walk reaches resolves to exactly one outcome — imported, unrecognised (its name is not one this command opens, or it was read and its structure matched no shipped importer), unreadable (it was tried and could not be read or listed) or refused (a symlink to a directory, or one whose target leaves the source directory — nothing behind it was examined) — and the counts sum to entries_reached, so an entry cannot be present without being counted. A notes.txt beside the exports is therefore named in the summary as unrecognised rather than passing unmentioned, and a subdirectory the process cannot list becomes an unreadable row instead of an absence. When more entries were discovered than the walk may examine, discovery_bounded says so and the manifest describes a prefix of the directory. That holds at row level too: a file can be imported and incomplete, so rows an importer could not read are counted per file, totalled as unreadable_rows_total, and the summary names those files under Imported INCOMPLETELY. A row carrying no vulnerability id, package name or purl is reported as unreadable rather than imported as a finding that names nothing — most often that means another tool's export was detected as a format it is not.

Who this is for: release managers, operators, portfolio owners, programme managers, migrating teams, auditor liaisons and data analysts.

sbomflow compare-releases#

Compare two SBOMFlow output directories and write release-drift.json.

text
Compare two existing runs of the same product and report what changed:
components, findings, KEV/EPSS, reachability, gaps, reviews to recheck,
support period, artifact hashes, and gate policy.

Preserved artifacts (traces, dumps, test results declared under
preserved_artifacts:) are compared by digest and written to
preserved-artifact-changes.json, beside release-drift.json, never in it.

Drift is comparison context only - it never replaces human review or
closes tracker issues.
FactValue
Grouphistory — Compare releases & keep history
Stabilitystable
Regimeany
Readstwo analyze or audit output directories of the same product.
Writesrelease-drift.json into --output; with --html also release-drift.html; preserved-artifact-changes.json into --output, beside release-drift.json; the release notes with --release-notes
--output namesa directory
Networkoffline — This command has no network-capable path.
Exit codes0 ok, including a comparison where one side was unavailable · 2 usage or input error, or with --require-both-releases a missing side
Machine outputartifact-suffices — writes release-drift.json

Options

OptionValueDefaultAllowedRequiredHelp
-h, --help(flag)none—noshow this help message and exit
--report-theme{light,dark}lightlight, darknoPalette for generated HTML surfaces (default: light). Print output is always light.
--previousPREVIOUSnoneanyyesPrevious output directory.
--currentCURRENTnoneanyyesCurrent output directory.
--outputOUTPUTnoneanyyesWhere release-drift.json is written (directory).
--require-both-releases(flag)false—noAutomation contract: exit 2 when either release directory is unavailable, instead of writing a drift in which nothing was compared. release-drift.json is still written first, so the reason survives the refusal. Without this flag exit codes are unchanged.
--html(flag)false—noAlso write release-drift.html, a human-readable release-diff report.
--release-notesFILEnoneanynoAlso write template-locked engineering security release notes (markdown): counts plus the list of fixed advisory ids. No free prose, no severities, and no still-open advisory ids.
--release-notes-formatFORMATmarkdownmarkdown, cyclonedx, bothnoWhich rendering(s) of the release note to write. 'markdown' (the default) is --release-notes FILE alone. 'cyclonedx' writes only engineering-release-notes.cdx.json - a standalone CycloneDX 1.7 document - into --output. 'both' writes both, and then --release-notes names the markdown destination. Both renderings project one normalized view, so they carry the same facts; the CycloneDX document adds no claim of its own.
--as-ofAS_OFnoneanynoPinned comparison timestamp.

Examples

Report what changed between two releases, with shareable notes.

sh
sbomflow compare-releases --previous out-prev --current out --output drift --release-notes drift/RELEASE-NOTES.md

Also write the human-readable drift report as HTML.

sh
sbomflow compare-releases --previous out-prev --current out --output drift --html

Refuse, after writing the drift, when a release directory is missing. Exits 2.

sh
sbomflow compare-releases --previous missing-dir --current out --output drift --require-both-releases

See also: sbomflow analyze, sbomflow store timeline, sbomflow release-chain verify, sbomflow help goal history.

sbomflow store#

Local, file-based evidence store: accumulate releases and query across them.

text
A local, file-based evidence store: accumulate finished release outputs
and query, verify, export and retain them across products and releases.
Deterministic and offline, with no database. --store is set here, before
the verb. Name a verb, or the group prints its usage and exits 2.
FactValue
Grouphistory — Compare releases & keep history
Stabilitystable
Regimeany
Readsnothing itself; its verbs read and write one local evidence store.
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
--storeSTORE.sbomflow-storeanynoStore directory (default: .sbomflow-store).

Examples

Record a finished release in the local evidence store.

sh
sbomflow store --store .sbomflow-store add out

Tell the cross-release story of one advisory.

sh
sbomflow store --store .sbomflow-store timeline --finding CVE-SAMPLE-OPENSSL-001

List the stored releases.

sh
sbomflow store --store .sbomflow-store list

See also: sbomflow store add, sbomflow store list, sbomflow store verify, sbomflow help goal history.

sbomflow store add#

Add a release output directory to the store.

text
Add one finished analyze or audit output directory to the store: its release
record is indexed in index.json, and the pack is copied in unless --no-copy
indexes it by reference. The source directory is never modified.
FactValue
Grouphistory — Compare releases & keep history
Stabilitystable
Regimeany
Readsone analyze or audit output directory.
Writesindex.json (the store index, with a copy of the release unless --no-copy)
Networkoffline — This command has no network-capable path.
Exit codes0 ok · 2 missing directory or refused store
Machine outputartifact-suffices — records the release into the store (JSON records + index); store list / store show with --json are the read side

Options

OptionValueDefaultAllowedRequiredHelp
-h, --help(flag)none—noshow this help message and exit
EVIDENCE_DIREVIDENCE_DIRnoneanyyesDirectory produced by 'analyze'/'audit'.
--no-copy(flag)false—noIndex the release by reference instead of copying it into the store.
--as-ofAS_OFnoneanynoPinned timestamp for the store index.
--break-stale-lock(flag)false—noRemove a provably-stale .sbomflow.lock (crashed run) from the store root before mutating. Never automatic; confirm no other run is active first.

Examples

Record a finished release in the local evidence store.

sh
sbomflow store --store .sbomflow-store add out

Start a new store with the earlier release.

sh
sbomflow store --store fresh-store add out-prev

Index a release by reference instead of copying it in.

sh
sbomflow store --store fresh-store add out --no-copy

See also: sbomflow store list, sbomflow store verify, sbomflow store timeline.

sbomflow store empty-trash#

Report (default) or permanently delete the retention .trash/ staging area.

text
Report what the retention staging area .trash/ holds, which `store retain
--apply` fills, or with --apply delete it permanently. The report is the
default and writes nothing; the index records of trashed releases are kept
either way.
FactValue
Grouphistory — Compare releases & keep history
Stabilitystable
Regimeany
Readsthe retention staging area, .trash/, of one store.
Writeswith --apply deletes .trash/ permanently; nothing otherwise; stdout (what is staged; --json prints JSON)
Networkoffline — This command has no network-capable path.
Exit codes0 ok · 2 an unreadable store index
Machine outputhas-json — read/status surface; one versioned JSON document on stdout

Options

OptionValueDefaultAllowedRequiredHelp
-h, --help(flag)none—noshow this help message and exit
--apply(flag)false—noPermanently delete .trash/ (default reports only).
--json(flag)false—noEmit JSON.
--break-stale-lock(flag)false—noRemove a provably-stale .sbomflow.lock (crashed run) from the store root before mutating. Never automatic; confirm no other run is active first.

Examples

Report what the retention staging area holds.

sh
sbomflow store --store .sbomflow-store empty-trash

The same report as JSON.

sh
sbomflow store --store .sbomflow-store empty-trash --json

Permanently delete the staging area.

sh
sbomflow store --store .sbomflow-store empty-trash --apply

See also: sbomflow store retain, sbomflow store verify.

sbomflow store exploitation-delta#

List newly KEV-listed identifiers and the stored releases whose frozen evidence records them.

text
Compare two KEV snapshots the local vulndb store named by --vulndb-dir holds
(no store is guessed from the working directory) and join every
identifier the --to catalogue adds to every stored release whose frozen
evidence records it as a finding id, an alias or a CPE candidate. Each row
carries the release's support state at the --to catalogue's dateReleased;
past-support releases are listed, never filtered. A release that could not
be read is named with its reason, never counted as zero exposure.
Offline and read-only apart from --output. Not a reporting determination:
it records no awareness time and opens no case.
FactValue
Grouphistory — Compare releases & keep history
Stabilityexperimental
Regimeany
Readstwo KEV snapshots in the local vulndb store --vulndb-dir names (none is guessed) and every stored release's frozen evidence pack and recorded CPE candidates.
Writesexploitation-delta.json (the file --output names, never inside the store); stdout (the snapshot pair, added and removed counts, examined and not-examined releases and exposures by support state)
--output namesone file
Networkoffline — This command has no network-capable path.
Exit codes0 the delta was written · 2 no --vulndb-dir (no store is guessed), a missing or unreadable vulndb store or store index, a snapshot id the vulndb store does not hold or that is not a KEV snapshot, bytes that no longer hash to the recorded digest, --from newer than --to or an order no catalogue states, the same id twice, or an --output inside the store; nothing is written
Machine outputartifact-suffices — writes the delta document to --output; that file is the machine surface

Options

OptionValueDefaultAllowedRequiredHelp
-h, --help(flag)none—noshow this help message and exit
--vulndb-dirDIRnoneanynoLocal vulndb store holding the two KEV snapshots (the directory vulndb update --dir wrote). Offline: its index and the two archives named by id are read, their bytes re-hashed against the recorded digests, and nothing is fetched. Required in effect: no default is assumed, because a store guessed from the working directory would make the delta depend on where the command was run; without it the command refuses.
--fromKEV_SNAPSHOT_IDnoneanyyesThe older KEV snapshot id (as vulndb inspect lists it).
--toKEV_SNAPSHOT_IDnoneanyyesThe newer KEV snapshot id; its dateReleased must not precede --from's.
--outputFILEnoneanyyesWhere the delta document is written (file). Never inside the store.

Examples

Join the identifiers a newer KEV snapshot adds to every stored release, with each release's support state.

sh
sbomflow store --store .sbomflow-store exploitation-delta --vulndb-dir kev-vulndb --from d9f358890df059b2 --to d2db4c619089a909 --output kev-delta.json

Refuse two snapshots named the wrong way round; the order comes from each catalogue's dateReleased, and nothing is written. Exits 2.

sh
sbomflow store --store .sbomflow-store exploitation-delta --vulndb-dir kev-vulndb --from d2db4c619089a909 --to d9f358890df059b2 --output kev-delta.json

See also: sbomflow case correlate, sbomflow recheck, sbomflow store query, sbomflow vulndb inspect.

sbomflow store export#

Export the whole estate + a completeness manifest (offline, portable).

text
Export every stored release as a portable directory with a completeness
manifest that reconciles against the store index. --dry-run reports what would
travel and writes no copies. A release that cannot travel is listed with the
reason; `store reissue-plan` diagnoses it.
FactValue
Grouphistory — Compare releases & keep history
Stabilitystable
Regimeany
Readsone store and every release its index records.
Writesthe portable export and its completeness manifest, into --output; stdout (the manifest; --json prints JSON)
--output namesa directory
Networkoffline — This command has no network-capable path.
Exit codes0 ok · 1 an incomplete estate: an indexed release has no stored copy to export (each one is named in the manifest) · 2 a missing store or a refused export · 4 a copied release does not verify, exactly as store verify would report it (each failing member is named)
Machine outputhas-json — one versioned completeness manifest, also written to the export directory; reconciles against the store index. store_index_state records how much of that index was read, and complete can only be true when it is read

Options

OptionValueDefaultAllowedRequiredHelp
-h, --help(flag)none—noshow this help message and exit
--outputOUTPUTnoneanyyesWhere the portable export is written (directory).
--dry-run(flag)false—noReconcile and report what would travel without writing copies.
--json(flag)false—noEmit the manifest as JSON.
--as-ofAS_OFnoneanynoPinned timestamp recorded in the manifest.

Examples

Export the whole estate with a completeness manifest.

sh
sbomflow store --store .sbomflow-store export --output export

Report what would travel, as JSON, without writing copies.

sh
sbomflow store --store .sbomflow-store export --output export --dry-run --json

Pin the manifest's timestamp.

sh
sbomflow store --store .sbomflow-store export --output export --as-of 2026-07-01T00:00:00Z

See also: sbomflow store reissue-plan, sbomflow store verify, sbomflow passport issue.

sbomflow store hold#

Place a preservation hold: retention and empty-trash skip the release.

text
Place a preservation hold on one stored release. While it is held,
`store retain` keeps the release whatever its policy says, and
`store empty-trash --apply` refuses while the release's copy is staged
in .trash/. The hold records who placed it, when, and the reason exactly
as stated, in index.json and in the append-only store-hold-log.jsonl.
It has no expiry: it stays until `store release-hold` lifts it. It does
not change the release, its content hash or the store seal, and
SBOMFlow does not assess the stated reason.
FactValue
Grouphistory — Compare releases & keep history
Stabilitystable
Regimeany
Readsone store's index and the named release.
Writesindex.json (the hold on the release's index record, and one line in store-hold-log.jsonl); stdout (what was placed; --json prints the release row)
Networkoffline — This command has no network-capable path.
Exit codes0 ok · 2 no release by that id, a release already held, or a missing actor or reason
Machine outputhas-json — read/status surface; one versioned JSON document on stdout

Options

OptionValueDefaultAllowedRequiredHelp
-h, --help(flag)none—noshow this help message and exit
RELEASERELEASEnoneanyyesRelease id or content-hash prefix naming one stored release (as store show takes).
--actorACTORnoneanyyesNamed person placing the hold; recorded with it.
--reasonREASONnoneanyyesWhy the hold is placed, recorded exactly as stated.
--as-ofAS_OFnoneanynoPinned timestamp recorded as when the hold was placed (default: now, UTC).
--json(flag)false—noEmit the release row, with its hold, as JSON.

Examples

Place a preservation hold on one stored release, naming who placed it and why.

sh
sbomflow store --store .sbomflow-store hold example-gateway-2.4.1 --actor records-team --reason 'Kept for the 2.4.1 field-return review.'

The same, at a pinned time, printing the release row with its hold as JSON.

sh
sbomflow store --store .sbomflow-store hold example-gateway-2.4.1 --actor records-team --reason 'Kept for the 2.4.1 field-return review.' --as-of 2026-07-01T00:00:00Z --json

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

sh
sbomflow store --store .sbomflow-store hold example-gateway-2.4.1 --actor records-team --reason 'A second hold.'

See also: sbomflow store release-hold, sbomflow store retain, sbomflow store empty-trash.

sbomflow store list#

List stored releases.

text
List the releases the store's index records, one row each: product, release,
content hash and gate outcome. An empty or absent store lists nothing. Read-
only.
FactValue
Grouphistory — Compare releases & keep history
Stabilitystable
Regimeany
Readsone store's index.
Writesstdout (one row per stored release; --json prints JSON)
Networkoffline — This command has no network-capable path.
Exit codes0 ok, including an empty or absent store · 2 an unreadable store index
Machine outputhas-json — read/status surface; one versioned JSON document on stdout

Options

OptionValueDefaultAllowedRequiredHelp
-h, --help(flag)none—noshow this help message and exit
--productPRODUCTnoneanynoFilter by product id or name substring.
--json(flag)false—noEmit JSON.

Examples

List the stored releases.

sh
sbomflow store --store .sbomflow-store list

The same list as JSON.

sh
sbomflow store --store .sbomflow-store list --json

Only one product's releases.

sh
sbomflow store --store .sbomflow-store list --product Gateway

See also: sbomflow store show, sbomflow store query, sbomflow store add.

sbomflow store query#

Query stored releases by attributes.

text
Query stored releases by attributes (product, channel, gate outcome, KEV
findings, gap count), or switch to cross-release finding rows with --finding-
key, --component or --cve: every release where it appeared, with the observed
version, the decision and the gate outcome. Read-only.
FactValue
Grouphistory — Compare releases & keep history
Stabilitystable
Regimeany
Readsone store's index and stored packs.
Writesstdout (the matching releases or rows; --json prints JSON)
Networkoffline — This command has no network-capable path.
Exit codes0 ok, including no match · 2 usage error
Machine outputhas-json — read/status surface; one versioned JSON document on stdout

Options

OptionValueDefaultAllowedRequiredHelp
-h, --help(flag)none—noshow this help message and exit
--productPRODUCTnoneanynoFilter by product id or name substring.
--channelCHANNELnoneanynoFilter by release channel.
--gate{pass,fail,enforced,informational}nonepass, fail, enforced, informationalnoFilter by gate outcome.
--has-kev(flag)false—noOnly releases with KEV findings.
--min-gapsMIN_GAPSnoneanynoOnly releases with >= N evidence gaps.
--finding-keyFINDING_KEYnoneanynoCross-release: every release where this finding key appeared.
--componentNAME[@VERSION]noneanynoCross-release: every release where this component's findings appeared.
--cveCVEnoneanynoCross-release: every release where this CVE (by id or alias) appeared.
--limitLIMITnoneanynoCross-release: cap the number of rows returned.
--offsetOFFSET0anynoCross-release: skip the first N rows (deterministic pagination).
--json(flag)false—noEmit JSON.

Examples

Every release in which an advisory appeared.

sh
sbomflow store --store .sbomflow-store query --cve CVE-SAMPLE-OPENSSL-001

Releases whose gate was informational, as JSON.

sh
sbomflow store --store .sbomflow-store query --gate informational --json

Only releases with known-exploited findings.

sh
sbomflow store --store .sbomflow-store query --has-kev

See also: sbomflow store timeline, sbomflow store list, sbomflow index query.

sbomflow store register-rename#

Record an operator-declared product rename so its release history stays connected.

text
Declare that a product was renamed, so its releases before and after the
rename resolve to one identity (drift/history/queries span the rename).
Nothing is ever merged automatically: renames are operator-declared,
never fuzzy-matched, and each is an audited event in registry.json.

Both names must already have releases in the store; their real stored
identities are connected (never recomputed).
FactValue
Grouphistory — Compare releases & keep history
Stabilitystable
Regimeany
Readsone store's index; both names must already have releases in it.
Writesregistry.json in the store root, with an audited rename event; stdout (the rename record; --json prints JSON)
Networkoffline — This command has no network-capable path.
Exit codes0 ok · 2 a conflicting registration or a name with no releases
Machine outputhas-json — read/status surface; one versioned JSON document on stdout

Options

OptionValueDefaultAllowedRequiredHelp
-h, --help(flag)none—noshow this help message and exit
--fromFROM_NAMEnoneanyyesPrevious product name.
--toTO_NAMEnoneanyyesCurrent (canonical) product name.
--noteNOTEnoneanynoOptional note recorded with the rename.
--as-ofAS_OFnoneanynoPinned timestamp for the rename event.
--json(flag)false—noEmit the rename record as JSON.

Examples

Declare that a product was renamed, so its history stays connected.

sh
sbomflow store --store .sbomflow-store register-rename --from 'Example Embedded Linux Gateway' --to 'Example Gateway' --note 'Renamed at 3.0.0.'

The same rename, as JSON, at a pinned time.

sh
sbomflow store --store .sbomflow-store register-rename --from 'Example Embedded Linux Gateway' --to 'Example Gateway' --json --as-of 2026-07-01T00:00:00Z

Refuse a name that has no release in the store. Exits 2.

sh
sbomflow store --store .sbomflow-store register-rename --from 'No Such Product' --to 'Example Gateway'

See also: sbomflow store list, sbomflow store query, sbomflow store timeline.

sbomflow store reindex#

Rebuild index.json from the canonical release packs the store owns.

text
Rebuild the store's index.json from the canonical release packs it
owns. Readable retention tombstones are preserved, every unreadable pack
is reported, and a prior index that will not parse is refused rather
than rebuilt over.
FactValue
Grouphistory — Compare releases & keep history
Stabilitystable
Regimeany
Readsthe canonical release packs a store owns, and its current index.
Writesindex.json (rebuilt, keeping readable retention tombstones); stdout (what was rebuilt and skipped; --json prints JSON)
Networkoffline — This command has no network-capable path.
Exit codes0 ok · 2 an unreadable prior index
Machine outputhas-json — rebuilds the deterministic index from canonical store-owned packs and reports every skipped input; prior_index_state says how much of the replaced index was read, and a prior index that will not parse is refused rather than rebuilt over

Options

OptionValueDefaultAllowedRequiredHelp
-h, --help(flag)none—noshow this help message and exit
--as-ofAS_OFnoneanynoPinned timestamp for deterministic output.
--json(flag)false—noEmit JSON.
--break-stale-lock(flag)false—noRemove a provably-stale .sbomflow.lock from the store root before mutating. Never automatic.

Examples

Rebuild the index from the packs the store owns.

sh
sbomflow store --store .sbomflow-store reindex

The same rebuild as JSON, at a pinned time.

sh
sbomflow store --store .sbomflow-store reindex --json --as-of 2026-07-01T00:00:00Z

Remove a provably stale lock left by a crashed run first.

sh
sbomflow store --store .sbomflow-store reindex --break-stale-lock

See also: sbomflow store verify, sbomflow store add.

sbomflow store reissue#

Create a NEW portable release from a fresh analysis (append-only).

text
Create a NEW portable release from a fresh analysis of the original
source, following a still-current reissue plan. Append-only: the
original is never edited, deleted or superseded, and no decision is
carried across, because approvals, waivers and VEX belong to the
evidence they were made against.
FactValue
Grouphistory — Compare releases & keep history
Stabilitystable
Regimeany
Readsa current reissue plan, and the original source checkout for a fresh analysis.
Writesevidence-pack.json (a NEW release pack, into an empty --output); portable-reissue-receipt.json beside it; the original is unchanged
--output namesa directory
Networkoffline — This command has no network-capable path.
Exit codes0 ok · 2 a stale or missing plan, or an unusable source or output
Machine outputartifact-suffices — writes a new release plus portable-reissue-receipt.json; human summary on stdout

Options

OptionValueDefaultAllowedRequiredHelp
-h, --help(flag)none—noshow this help message and exit
release_idrelease_idnoneanyyesThe release that could not be exported.
--planPLANnoneanyyesA still-current portable-reissue-plan.json. A stale plan is refused.
--sourceSOURCEnoneanyyesThe original source checkout. SBOMFlow does not retain one.
--targetTARGET.anynoTarget RELATIVE to --source (default: '.'). An absolute target is refused.
--outputOUTPUTnoneanyyesWhere the new evidence pack is written (directory). Must be empty and outside --source.
--configCONFIGnoneanynoConfig to use for the fresh analysis.
--as-ofAS_OFnoneanynoPinned timestamp recorded in the receipt.

Examples

Reissue a release as a new portable pack from a fresh analysis.

sh
sbomflow store --store .sbomflow-store reissue example-gateway-2.4.1 --plan plan.json --source . --output reissued

Pin the receipt's timestamp.

sh
sbomflow store --store .sbomflow-store reissue example-gateway-2.4.1 --plan plan.json --source . --output reissued --as-of 2026-07-01T00:00:00Z

Refuse a plan that is not there. Exits 2.

sh
sbomflow store --store .sbomflow-store reissue example-gateway-2.4.1 --plan missing-plan.json --source . --output reissued

See also: sbomflow store reissue-plan, sbomflow store export.

sbomflow store reissue-plan#

Diagnose why an export was refused and plan a safe reissue (read-only).

text
Diagnose why a stored release could not be exported and write a
portable-reissue-plan.json saying how a fresh analysis could reissue it.
Read-only: it writes no export and never modifies the store.
FactValue
Grouphistory — Compare releases & keep history
Stabilitystable
Regimeany
Readsone store's index and stored packs.
Writesportable-reissue-plan.json (at --output, or beside the store); stdout (the diagnosis; --json prints JSON)
--output namesone file
Networkoffline — This command has no network-capable path.
Exit codes0 ok · 2 an unknown release or a missing store
Machine outputhas-json — read-only diagnosis of a refused export; one versioned JSON document on stdout

Options

OptionValueDefaultAllowedRequiredHelp
-h, --help(flag)none—noshow this help message and exit
--outputOUTPUTnoneanynoWrite portable-reissue-plan.json here (file). Default: alongside the store.
--releaseRELEASEnoneanynoLimit the diagnosis to this release id (repeatable).
--json(flag)false—noEmit the plan as JSON; an --output named beside it is written too, with the same document.
--as-ofAS_OFnoneanynoPinned timestamp recorded in the plan.

Examples

Diagnose every stored release and plan any reissue.

sh
sbomflow store --store .sbomflow-store reissue-plan

Write the plan to a named file, as JSON.

sh
sbomflow store --store .sbomflow-store reissue-plan --output plan.json --json

Only one release.

sh
sbomflow store --store .sbomflow-store reissue-plan --release example-gateway-2.4.1

See also: sbomflow store reissue, sbomflow store export.

sbomflow store release-hold#

Lift a preservation hold; retention applies to the release again.

text
Lift the preservation hold on one stored release. The hold is not
deleted: its record stays in index.json flagged as lifted, with who
lifted it, when and the reason exactly as stated, and the event is
appended to store-hold-log.jsonl. From then on `store retain` and
`store empty-trash` treat the release like any other. A release that
is not held is refused.
FactValue
Grouphistory — Compare releases & keep history
Stabilitystable
Regimeany
Readsone store's index and the named release.
Writesindex.json (the lift on the release's index record (the hold is kept, flagged lifted), and one line in store-hold-log.jsonl); stdout (what was lifted; --json prints the release row)
Networkoffline — This command has no network-capable path.
Exit codes0 ok · 2 no release by that id, a release not held, or a missing actor or reason
Machine outputhas-json — read/status surface; one versioned JSON document on stdout

Options

OptionValueDefaultAllowedRequiredHelp
-h, --help(flag)none—noshow this help message and exit
RELEASERELEASEnoneanyyesRelease id or content-hash prefix naming one stored release (as store show takes).
--actorACTORnoneanyyesNamed person lifting the hold; recorded with it.
--reasonREASONnoneanyyesWhy the hold is lifted, recorded exactly as stated.
--as-ofAS_OFnoneanynoPinned timestamp recorded as when the hold was lifted (default: now, UTC).
--json(flag)false—noEmit the release row, with its hold, as JSON.

Examples

Lift the hold, naming who lifted it and why; the hold's record is kept.

sh
sbomflow store --store .sbomflow-store release-hold example-gateway-2.4.1 --actor records-team --reason 'Field-return review closed.'

The same, at a pinned time, printing the release row with its lifted hold as JSON.

sh
sbomflow store --store .sbomflow-store release-hold example-gateway-2.4.1 --actor records-team --reason 'Field-return review closed.' --as-of 2026-07-01T00:00:00Z --json

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

sh
sbomflow store --store .sbomflow-store release-hold example-gateway-2.4.1 --actor records-team --reason 'Field-return review closed.'

See also: sbomflow store hold, sbomflow store retain, sbomflow store empty-trash.

sbomflow store retain#

Plan (default) or apply a retention policy that trashes old stored copies.

text
Apply a retention policy to the local store with a two-step safety pattern.

  Default  writes a deterministic store-retention-plan.json (kept vs.
           trash, with reasons) and changes NOTHING.
  --apply  moves each trash victim's stored copy to .trash/<stamp>/ (never
           a hard delete), keeps its index record, then re-verifies
           integrity and rolls back on any failure.

Policy keys (retain.yaml or .json): keep_last (N newest per product),
keep_channels (list), min_age_days (protect newer releases). Every rule
only protects; only keep_last makes a release a trash candidate. An
UNRECOGNISED key is refused, never ignored: since every other rule only
protects, a misspelled one would drop the protection while keep_last
still trashed. Permanently delete the staging area with
`store empty-trash --apply`.
FactValue
Grouphistory — Compare releases & keep history
Stabilitystable
Regimeany
Readsa retention policy file and one store's index.
Writesstore-retention-plan.json; with --apply trash moves into .trash/; stdout (the plan; --json prints JSON)
Networkoffline — This command has no network-capable path.
Exit codes0 ok · 2 a missing or unrecognised policy
Machine outputhas-json — read/status surface; one versioned JSON document on stdout

Options

OptionValueDefaultAllowedRequiredHelp
-h, --help(flag)none—noshow this help message and exit
--policyPOLICYnoneanyyesRetention policy file (YAML/JSON).
--apply(flag)false—noAct on the plan (default is a dry-run plan only).
--as-ofAS_OFnoneanynoPinned timestamp for the plan / trash stamp.
--json(flag)false—noEmit the plan as JSON.
--break-stale-lock(flag)false—noRemove a provably-stale .sbomflow.lock (crashed run) from the store root before mutating. Never automatic; confirm no other run is active first.

Examples

Plan which stored copies the policy would trash, changing nothing.

sh
sbomflow store --store .sbomflow-store retain --policy retain.yaml

The plan as JSON, at a pinned time.

sh
sbomflow store --store .sbomflow-store retain --policy retain.yaml --json --as-of 2026-07-01T00:00:00Z

Apply it: older copies move to .trash/ and their index records stay.

sh
sbomflow store --store .sbomflow-store retain --policy retain.yaml --apply

See also: sbomflow store empty-trash, sbomflow store verify, sbomflow vulndb retention.

sbomflow store show#

Show one stored release by id or content hash.

text
Show one stored release's record by release id or content-hash prefix: product
identity, gate outcome, counts and where the pack is kept. Read-only.
FactValue
Grouphistory — Compare releases & keep history
Stabilitystable
Regimeany
Readsone store's index and the named release.
Writesstdout (the release's stored record; --json prints JSON)
Networkoffline — This command has no network-capable path.
Exit codes0 ok · 2 no release by that id or hash
Machine outputhas-json — read/status surface; one versioned JSON document on stdout

Options

OptionValueDefaultAllowedRequiredHelp
-h, --help(flag)none—noshow this help message and exit
keykeynoneanyyesRelease id or content-hash prefix.
--json(flag)false—noEmit JSON.

Examples

Show one stored release by id.

sh
sbomflow store --store .sbomflow-store show example-gateway-2.4.1

The same record as JSON.

sh
sbomflow store --store .sbomflow-store show example-gateway-2.4.1 --json

Refuse an id the store does not hold. Exits 2.

sh
sbomflow store --store .sbomflow-store show no-such-release

See also: sbomflow store list, sbomflow store verify, sbomflow store timeline.

sbomflow store timeline#

Tell the cross-release story of one finding (read-only, offline).

text
Compose the ordered story of a single finding across every stored release:
when it was first observed, how its severity/version/decision changed, which
releases it blocked, whether a waiver covered it, and when it stopped being
observed.

Absence is NOT a fix: a finding that stops appearing may have had its
component removed, renamed, or simply not scanned. Releases stored by
reference hold no canonical pack and are reported as explicit gaps, never
interpolated.
FactValue
Grouphistory — Compare releases & keep history
Stabilitystable
Regimeany
Readsevery stored release's pack.
Writesstdout (the finding's story, release by release; --json prints JSON)
Networkoffline — This command has no network-capable path.
Exit codes0 ok, including a finding never observed · 2 a missing store
Machine outputhas-json — read/status surface; one versioned JSON document on stdout

Options

OptionValueDefaultAllowedRequiredHelp
-h, --help(flag)none—noshow this help message and exit
--findingCVE|FINDING-KEYnoneanyyesAdvisory id (CVE-…, GHSA-…, OSV-…) or an opaque finding key.
--productPRODUCTnoneanynoScope to one product id or name substring.
--as-ofAS_OFnoneanynoPin the report timestamp (ISO-8601 UTC) for reproducible output.
--json(flag)false—noEmit JSON.

Examples

Tell the cross-release story of one advisory.

sh
sbomflow store --store .sbomflow-store timeline --finding CVE-SAMPLE-OPENSSL-001

The same story as JSON, at a pinned time.

sh
sbomflow store --store .sbomflow-store timeline --finding CVE-SAMPLE-OPENSSL-001 --json --as-of 2026-07-01T00:00:00Z

Scope it to one product.

sh
sbomflow store --store .sbomflow-store timeline --finding CVE-SAMPLE-BUSYBOX-001 --product Gateway

See also: sbomflow store query, sbomflow compare-releases, sbomflow recheck.

sbomflow store verify#

Verify store integrity: recompute every stored release's content hash.

text
Recompute each stored release's content hash, and list orphans and
missing releases, with a whole-store seal to compare after a transfer.
A tracked release that is missing or tampered exits 4. Read-only.
FactValue
Grouphistory — Compare releases & keep history
Stabilitystable
Regimeany
Readsone store's index and every stored release.
Writesstdout (one line per release and the store seal; --json prints JSON)
Networkoffline — This command has no network-capable path.
Exit codes0 ok · 2 a missing store or a malformed --expected-seal · 4 a tracked release is missing or tampered
Machine outputhas-json — read/status surface; one versioned JSON document on stdout

Options

OptionValueDefaultAllowedRequiredHelp
-h, --help(flag)none—noshow this help message and exit
--json(flag)false—noEmit JSON.
--strict(flag)false—noMachine-transfer/DR check: a by-reference release whose external directory cannot be read here FAILS instead of reporting 'by_reference' (exit 4). Use after moving or restoring a store.
--reference-rootREFERENCE_ROOTnoneanynoWhere by-reference release directories live on THIS machine, for a store that moved. Changes only where --strict looks; no recorded path is rewritten.
--expected-sealEXPECTED_SEALnoneanynoA whole-store seal recorded earlier (the 'seal' block of a previous --json run). A release lost in transfer fails here even though every surviving release still hashes correctly.

Examples

Recompute every stored release's content hash.

sh
sbomflow store --store .sbomflow-store verify

The same check as JSON, with the whole-store seal.

sh
sbomflow store --store .sbomflow-store verify --json

Fail a by-reference release this machine cannot read. Exits 4.

sh
sbomflow store --store by-reference verify --strict --reference-root nowhere

See also: sbomflow store verify-gates, sbomflow store verify-replay, sbomflow store reindex.

sbomflow store verify-gates#

Replay each stored release's gate and check its recorded outcome reproduces.

text
Answer the auditor's question: did release N's recorded gate outcome come
from the policy we think it did, over the pack we have on file?

For each release this checks two things:
  provenance  the policy's hash matches the recorded one; and
  integrity   re-running that policy over the stored pack reproduces the
              recorded outcome.

--policy is optional: a release records the full policy body it enforced, so
it replays from its own record. Supply --policy for a release written before
that body was recorded, or to check a release against a policy by hand.

Read-only. Exits 4 if any release with a matching policy fails to
reproduce its recorded outcome (a tamper/inconsistency signal).
FactValue
Grouphistory — Compare releases & keep history
Stabilitystable
Regimeany
Readsevery stored release's pack and the policy each recorded.
Writesstdout (one provenance and integrity verdict per release; --json prints JSON)
Networkoffline — This command has no network-capable path.
Exit codes0 ok · 2 an unknown policy or unreadable input · 4 a release's recorded gate outcome did not reproduce
Machine outputhas-json — read/status surface; one versioned JSON document on stdout

Options

OptionValueDefaultAllowedRequiredHelp
-h, --help(flag)none—noshow this help message and exit
--policyPOLICYnoneanynoThe gate policy whose recorded outcomes you are verifying: a shipped preset name or a policy file path (.json/.yaml), resolved exactly as sbomflow gate simulate and sbomflow policy show resolve it. Optional: when omitted, each release resolves its own policy from what it recorded — a packaged --policy-profile binding, else the full policy body it stored. A release that recorded neither, or whose stored body does not reproduce its recorded hash, is reported unverifiable, never guessed.
--vulndb-dirDIRnoneanynoLocal vulndb store to resolve each release's recorded source set against, for the replayability column only. Reads that store's index; no network on any path. Without it, a release that recorded a set reports unknown — source_store_not_examined, which states what this run looked at rather than anything about the release: replayable and the byte-level findings behind it (source_bytes_unavailable, source_bytes_unverifiable_by_content, source_store_unreadable) are only ever produced when this is supplied. No default is assumed — a store guessed from the working directory would make the column depend on where the command was run. Changes no gate verdict, no summary counter and no exit code.
--productPRODUCTnoneanynoScope to one product id or name substring.
--as-ofAS_OFnoneanynoPin the report timestamp (ISO-8601 UTC) for reproducible output.
--json(flag)false—noEmit JSON.

Examples

Replay every stored gate from the policy each release recorded.

sh
sbomflow store --store .sbomflow-store verify-gates

Check them against a named policy instead.

sh
sbomflow store --store .sbomflow-store verify-gates --policy cra-default

The verdicts as JSON.

sh
sbomflow store --store .sbomflow-store verify-gates --json

See also: sbomflow store verify, sbomflow gate simulate, sbomflow policy show.

sbomflow store verify-replay#

Re-derive a stored release's vulnerability result from the exact bytes it recorded.

text
Answer a different question from 'recheck-estate': not 'what would we
find today', but 'does release N's recorded result still follow from the
inputs release N actually read'.

Every input is resolved by the digest the RELEASE recorded. Nothing is
substituted from what is active now, nothing is fetched, and an input
that cannot be found is a terminal result of its own — never a
reproduction. Read-only: the stored release is left byte-identical and
the report is written to an isolated output directory.

A clean reproduction proves only that the deterministic computation can
be repeated from those bytes. It does not prove the source was complete,
the product is safe now, the release was approved, or any legal
conclusion.
FactValue
Grouphistory — Compare releases & keep history
Stabilitystable
Regimeany
Readsevery stored release's recorded inputs and, with --vulndb-dir, the retained advisory snapshots.
Writesvulnerability-replay-verification.json (with --output, into an isolated directory); stdout (one reproduction verdict per release; --json prints JSON)
--output namesa directory
Networkoffline — This command has no network-capable path.
Exit codes0 ok · 2 an unknown release or unusable input · 4 a stored release's result did not reproduce from its recorded bytes
Machine outputhas-json — replays a stored release's recorded vulnerability inputs and reports whether each resolved to its exact recorded bytes; a recorded input it cannot find is reported as unavailable, never reproduced from whatever is current

Options

OptionValueDefaultAllowedRequiredHelp
-h, --help(flag)none—noshow this help message and exit
[release][release]noneanynoOne release id or content hash. Omitted, every release in the store (optionally narrowed by --product) is replayed.
--vulndb-dirDIRnoneanynoLocal vulndb store holding the retained snapshots. Offline: the index and archives are read, nothing is fetched. Without it every release reports source_bytes_unavailable — which is the honest answer, since no store was examined; a store guessed from the working directory would make the verdict depend on where the command was run.
--productPRODUCTnoneanynoScope to one product id or name substring.
--outputDIRnoneanynoWhere vulnerability-replay-verification.json is written (directory); it must be ISOLATED. Never inside the store: a report deposited in the stored release would change the bytes a later replay hashes.
--as-ofAS_OFnoneanynoPin the report timestamp (ISO-8601 UTC) for reproducible output.
--json(flag)false—noEmit JSON.

Examples

Replay every stored release from the exact bytes it recorded.

sh
sbomflow store --store .sbomflow-store verify-replay

Write the verification report to an isolated directory.

sh
sbomflow store --store .sbomflow-store verify-replay --output replay

One release, as JSON.

sh
sbomflow store --store .sbomflow-store verify-replay example-gateway-2.4.1 --json

See also: sbomflow store verify-gates, sbomflow vulndb verify, sbomflow recheck.

sbomflow index#

Build/validate/query the derived SQLite evidence index (JSON stays canonical).

text
A derived SQLite index over one output directory or a whole evidence store,
for fast read-only queries. The JSON artifacts stay canonical: the index is
rebuilt from them and never written back. Name a verb, or the group prints its
usage and exits 2.
FactValue
Grouphistory — Compare releases & keep history
Stabilitystable
Regimeany
Readsnothing itself; its verbs build and read a SQLite index.
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

Build a SQLite index from a finished run.

sh
sbomflow index rebuild out --db index.sqlite

List the releases the index holds.

sh
sbomflow index query releases --db index.sqlite

Name no verb: print the group's usage and exit 2. Exits 2.

sh
sbomflow index

See also: sbomflow index rebuild, sbomflow index query, sbomflow store.

sbomflow index query#

Run a read-only named query over the index.

text
Run one read-only named query over the index: releases, components, findings,
unreviewed evidence, the release that recorded a source hash, or a free-text
search. Nothing is written.
FactValue
Grouphistory — Compare releases & keep history
Stabilitystable
Regimeany
Readsone SQLite index database.
Writesstdout (the named query's rows; --json prints JSON)
Networkoffline — This command has no network-capable path.
Exit codes0 ok · 2 a missing database or a usage error
Machine outputhas-json — read/status surface; one versioned JSON document on stdout

Options

OptionValueDefaultAllowedRequiredHelp
-h, --help(flag)none—noshow this help message and exit
namenamenonereleases, components, findings, unreviewed-evidence, source-hash, searchyesThe named query to run. One of: releases, components, findings, unreviewed-evidence, source-hash, search.
--dbDBnoneanyyesIndex database path.
--releaseRELEASEnoneanynoFilter to a release id.
--pathSOURCE_PATHnoneanynoSource path (for source-hash).
--termTERMnoneanynoSearch term (for search).
--kev(flag)false—noFindings: only known-exploited (KEV).
--json(flag)false—noEmit deterministic JSON.

Examples

List the releases the index holds.

sh
sbomflow index query releases --db index.sqlite

Every finding, as JSON.

sh
sbomflow index query findings --db index.sqlite --json

Search the index for a term.

sh
sbomflow index query search --term openssl --db index.sqlite

See also: sbomflow index rebuild, sbomflow index validate, sbomflow store query.

sbomflow index rebuild#

(Re)build the SQLite index from an output dir or an evidence store.

text
(Re)build the SQLite index from an analysis output directory or an evidence
store. The database is derived from the JSON artifacts, which stay canonical;
rebuilding it changes no evidence.
FactValue
Grouphistory — Compare releases & keep history
Stabilitystable
Regimeany
Readsone output directory or one evidence store.
Writessbomflow-index.sqlite, a SQLite database at --db
Networkoffline — This command has no network-capable path.
Exit codes0 ok · 2 missing or unusable input
Machine outputartifact-suffices — writes the derived SQLite index — itself a machine-queryable artifact (index query speaks --json)

Options

OptionValueDefaultAllowedRequiredHelp
-h, --help(flag)none—noshow this help message and exit
pathpathnoneanyyesAn analysis output dir or a store dir.
--dbDBnoneanynoIndex database path (default: <path>/sbomflow-index.sqlite).
--as-ofAS_OFnoneanynoPinned index build timestamp.

Examples

Build the index beside a finished run.

sh
sbomflow index rebuild out

Build one from a whole store, at a named path.

sh
sbomflow index rebuild .sbomflow-store --db index.sqlite

Pin the build timestamp.

sh
sbomflow index rebuild out --db index.sqlite --as-of 2026-07-01T00:00:00Z

See also: sbomflow index query, sbomflow index validate, sbomflow index-releases.

sbomflow index validate#

Validate the SQLite index (schema/integrity/FK).

text
Check a SQLite index's schema version, integrity and foreign keys. A missing
database is refused (exit 2) and one that fails a check exits 4, so a pipeline
can rebuild before it queries.
FactValue
Grouphistory — Compare releases & keep history
Stabilitystable
Regimeany
Readsone SQLite index database.
Writesstdout (the errors and notes; --json prints JSON)
Networkoffline — This command has no network-capable path.
Exit codes0 the index is valid · 2 the index database is not there (E015, a missing input) · 4 the index failed a schema, integrity or foreign-key check (a structural validation failure)
Machine outputhas-json — structured errors/notes on stdout; the exit code is unchanged, so switching on --json cannot change the verdict

Options

OptionValueDefaultAllowedRequiredHelp
-h, --help(flag)none—noshow this help message and exit
--json(flag)false—noEmit the errors/notes as one JSON document.
--dbDBnoneanyyesIndex database path.

Examples

Check the index's schema, integrity and foreign keys.

sh
sbomflow index validate --db index.sqlite

The same check as JSON.

sh
sbomflow index validate --db index.sqlite --json

A database that is not there is refused as a missing input and exits 2. Exits 2.

sh
sbomflow index validate --db missing.sqlite

See also: sbomflow index rebuild, sbomflow index query.

sbomflow index-releases#

Build release-index.json, the file dashboard reads, from SBOMFlow output directories.

text
Read one or more output directories and write one cross-release release-
index.json, the machine-readable input the dashboard and portfolio views
render. A directory that cannot be read is listed with a warning rather than
failing the run.
FactValue
Grouphistory — Compare releases & keep history
Stabilitystable
Regimeany
Readsone or more output directories; an unusable one is listed with a warning.
Writesrelease-index.json at --output
--output namesone file
Networkoffline — This command has no network-capable path.
Exit codes0 ok, with a warning for each unusable directory · 2 usage error
Machine outputartifact-suffices — writes release-index.json, the machine-readable cross-release index. A product entry also carries product_line when its releases declared one distinct from the product name; absent means no line was declared, never that grouping failed

Options

OptionValueDefaultAllowedRequiredHelp
-h, --help(flag)none—noshow this help message and exit
output_dirs [output_dirs ...]output_dirs [output_dirs ...]noneanyyesOutput directories to index.
--outputOUTPUTnoneanyyesWhere release-index.json is written (file).
--as-ofAS_OFnoneanynoPinned index timestamp.

Examples

Index two releases into one cross-release file.

sh
sbomflow index-releases out-prev out --output release-index.json

Pin the index timestamp.

sh
sbomflow index-releases out-prev out --output release-index.json --as-of 2026-07-01T00:00:00Z

A missing directory is listed with a warning, not fatal.

sh
sbomflow index-releases missing-dir out --output release-index.json

See also: sbomflow dashboard, sbomflow portfolio, sbomflow store.

sbomflow dashboard#

Render a static release dashboard from release-index.json or output directories.

text
Render one static HTML dashboard of releases: from a release-index.json, or
from output directories indexed on the fly. A directory that cannot be read is
left out with a warning. No server and no network.
FactValue
Grouphistory — Compare releases & keep history
Stabilitystable
Regimeany
Readsa release-index.json, or one or more output directories.
Writesdashboard.html at --output
--output namesone file
Networkoffline — This command has no network-capable path.
Exit codes0 ok · 2 usage error
Machine outputartifact-suffices — renders a static human dashboard from release-index.json, which is already the machine surface

Options

OptionValueDefaultAllowedRequiredHelp
-h, --help(flag)none—noshow this help message and exit
--report-theme{light,dark}lightlight, darknoPalette for generated HTML surfaces (default: light). Print output is always light.
inputs [inputs ...]inputs [inputs ...]noneanyyesA release-index.json file, or one or more SBOMFlow output directories.
--outputOUTPUTnoneanyyesWhere dashboard.html is written (file).
--as-ofAS_OFnoneanynoPinned dashboard timestamp when indexing dirs.

Examples

Render a static dashboard for two releases.

sh
sbomflow dashboard out-prev out --output dashboard.html

Render it from a release index.

sh
sbomflow dashboard release-index.json --output dashboard.html

In the dark palette.

sh
sbomflow dashboard out --output dashboard.html --report-theme dark

See also: sbomflow index-releases, sbomflow portfolio, sbomflow review-view.

sbomflow portfolio#

Aggregate the releases a portfolio config lists into one index and dashboard.

text
Read a portfolio config (JSON or YAML) listing output directories, flat or
grouped by product, and aggregate them into one cross-product dashboard and,
with --index, a portfolio-index.json. Relative paths resolve against the
config file's own directory. Grouping is presentation only; it never changes a
product's identity.
FactValue
Grouphistory — Compare releases & keep history
Stabilitystable
Regimeany
Readsa portfolio config and the output directories it lists.
Writesthe portfolio dashboard HTML at --output; portfolio-index.json with --index
--output namesone file
Networkoffline — This command has no network-capable path.
Exit codes0 ok · 2 a missing or malformed config
Machine outputartifact-suffices — writes the cross-product portfolio index plus its dashboard. When the config declares products:[], the index also carries config_grouping: the operator's own group names joined to the product_ids their directories resolved to. Presentation only — it never changes identity, and a group whose outputs span two product identities is reported as spanning two rather than merged

Options

OptionValueDefaultAllowedRequiredHelp
-h, --help(flag)none—noshow this help message and exit
configconfignoneanyyesPortfolio config file (.json/.yaml).
--outputOUTPUTnoneanyyesWhere the portfolio dashboard HTML is written (file).
--indexINDEXnoneanynoAlso write the aggregated portfolio-index.json here.
--as-ofAS_OFnoneanynoPinned aggregation timestamp.

Examples

Aggregate the releases a portfolio config lists into one dashboard.

sh
sbomflow portfolio portfolio.yaml --output portfolio.html

Also write the aggregated index, at a pinned time.

sh
sbomflow portfolio portfolio.yaml --output portfolio.html --index portfolio-index.json --as-of 2026-07-01T00:00:00Z

Refuse a config that is not there. Exits 2.

sh
sbomflow portfolio missing-portfolio.yaml --output portfolio.html

See also: sbomflow dashboard, sbomflow index-releases, sbomflow batch-run.

sbomflow release-chain#

Verify a hash-linked chain of release records across output directories.

text
Tamper-evident release history: each release record written with
`analyze --previous-output` embeds the hash of the record before it, and
the verb here walks that chain. Name the verb, or the group prints its
usage and exits 2.
FactValue
Grouphistory — Compare releases & keep history
Stabilitystable
Regimeany
Readsnothing itself; its verb reads release records.
Writesnothing; its verb writes nothing either
Networkoffline — This command has no network-capable path.
Exit codes0 the 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

Verify that a release links to the one before it.

sh
sbomflow release-chain verify out-prev out-next

A single record is its own genesis.

sh
sbomflow release-chain verify out

Name no verb: print the group's usage and exit 2. Exits 2.

sh
sbomflow release-chain

See also: sbomflow release-chain verify, sbomflow compare-releases.

sbomflow release-chain verify#

Walk release records in order and verify each links to the previous by hash.

text
Verify tamper-evident release history. Each record after the first must
embed the SHA-256 of the record before it; the first is the genesis. A
wrong or edited link is tampered and an unreadable record is a break
(exit 4); a record that carries no link at all is detached, which is a
gap rather than corruption (exit 8).

Links are written by `analyze --previous-output`; this only reads them.
FactValue
Grouphistory — Compare releases & keep history
Stabilitystable
Regimeany
Readsrelease-record.json from each directory, in the order given.
Writesstdout (the chain report; --json prints JSON)
Networkoffline — This command has no network-capable path.
Exit codes0 the chain verified, or a single genesis record · 2 usage error · 4 a record is unreadable, or a link is broken or tampered · 8 a record carries no link to its predecessor
Machine outputhas-json — read/status surface; one versioned JSON document on stdout

Options

OptionValueDefaultAllowedRequiredHelp
-h, --help(flag)none—noshow this help message and exit
record_dirs [record_dirs ...]record_dirs [record_dirs ...]noneanyyesOutput directories in chronological order.
--json(flag)false—noEmit the chain report as JSON.

Examples

Verify that a release links to the one before it.

sh
sbomflow release-chain verify out-prev out-next

The chain report as JSON.

sh
sbomflow release-chain verify out-prev out-next --json

Two runs analyzed without --previous-output are detached and exit 8. Exits 8.

sh
sbomflow release-chain verify out-prev out

See also: sbomflow analyze, sbomflow compare-releases, sbomflow store verify.

sbomflow backfill#

Register past releases in the store as DECLARED records, never as observed runs.

text
Backfill historical releases, shipped before SBOMFlow, from an
operator-authored manifest (product, and per release: version, date,
channel, optional sbom file, support period, note). Each row becomes one
DECLARED release record (record_provenance=declared), chained by hash.
Offline, local files only, no source scanning.

Declared records give the timeline continuity history needs WITHOUT
fabricating a run: composition comes only from a supplied SBOM (imported,
referenced + hashed) else 'unknown', and declared records are excluded from
observed-only computations (e.g. the evidence-regression drift gate).
FactValue
Grouphistory — Compare releases & keep history
Stabilitystable
Regimeany
Readsan operator-authored manifest of past releases, and any SBOM file it names.
Writesindex.json (one DECLARED release record per row, chained by hash); store-backfill-log.jsonl; stdout (the summary; --json prints JSON)
Networkoffline — This command has no network-capable path.
Exit codes0 ok · 2 a missing or malformed manifest, a duplicate version or a bad date
Machine outputhas-json — read/status surface; one versioned JSON document on stdout

Options

OptionValueDefaultAllowedRequiredHelp
-h, --help(flag)none—noshow this help message and exit
manifestmanifestnoneanyyesBackfill manifest (YAML/JSON).
--storeSTORE.sbomflow-storeanynoStore directory to backfill into (default: .sbomflow-store).
--as-ofAS_OFnoneanynoPinned timestamp for the store index / backfill log.
--json(flag)false—noEmit the backfill summary as JSON.

Examples

Register two past releases as declared records in a new store.

sh
sbomflow backfill backfill.yaml --store fresh-store

Backfill into a store that already holds an observed release, at a pinned time.

sh
sbomflow backfill backfill.yaml --store .sbomflow-store --as-of 2026-07-01T00:00:00Z

Print the summary as JSON.

sh
sbomflow backfill backfill.yaml --store fresh-store --json

See also: sbomflow store list, sbomflow store timeline, sbomflow migrate-in.

sbomflow migrate-in#

Import another tool's estate as origin-labelled CLAIMS (offline; never decisions).

text
Point this at a directory of another tool's exports. Every row is
imported as a CLAIM carrying its origin — never a reviewer decision,
VEX status, approval or gate input. An incumbent tool's status is a
claim about what that tool said, not authority over your product.
FactValue
Grouphistory — Compare releases & keep history
Stabilitystable
Regimeany
Readsa directory of another tool's exports (Trivy, Grype, SARIF JSON).
Writesthe migration manifest, into --output; stdout (the manifest summary; --json prints JSON)
--output namesa directory
Networkoffline — This command has no network-capable path.
Exit codes0 ok · 2 an unreadable source directory
Machine outputhas-json — one versioned migration manifest; every row carries claim_origin and unimported files are listed with reasons

Options

OptionValueDefaultAllowedRequiredHelp
-h, --help(flag)none—noshow this help message and exit
sourcesourcenoneanyyesDirectory of competitor exports.
--outputOUTPUTnoneanynoWhere the migration manifest is written (directory).
--json(flag)false—noEmit the manifest as JSON.
--as-ofAS_OFnoneanynoPinned timestamp recorded in the manifest.

Examples

List what another tool's exports would bring in, as origin-labelled claims.

sh
sbomflow migrate-in exports

Write the migration manifest, at a pinned time.

sh
sbomflow migrate-in exports --output migration --as-of 2026-07-01T00:00:00Z

Print the manifest as JSON.

sh
sbomflow migrate-in exports --json

See also: sbomflow ingest-trivy, sbomflow ingest-grype, sbomflow ingest-sarif, sbomflow backfill.

sbomflow sbom-set#

Work with SBOM documents you were handed (offline).

text
Work with SBOM documents supplied to you, without changing them. Offline;
nothing is fetched or sent.
FactValue
Grouphistory — Compare releases & keep history
Stabilityexperimental
Regimeany
Readsnothing itself; its verb reads the SBOM files it is given.
Writesnothing itself; its 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

Compare a supplier's CycloneDX SBOM with an integrator's SPDX SBOM of the same build.

sh
sbomflow sbom-set compare sboms/gateway-2.4.1.supplier.cdx.json sboms/gateway-2.4.1.integrator.spdx.json

See also: sbomflow sbom-set compare, sbomflow help history.

sbomflow sbom-set compare#

Explain where two or more SBOMs for the same build agree and differ.

text
Compare two or more CycloneDX or SPDX 2.x SBOMs that should describe the same
build, and say where they agree and where they differ: components, versions,
identifiers, digests, licences, dependency edges, scope, generator and field
coverage, N sources wide, each fact with the document coordinate it came from.

Comparability comes first. The documents must be shown to describe the same
bytes: each states the same release id and subject digest, or the operator
declares they belong together (recorded as declared, never verified). Otherwise
the result is incomparable_subject and no figure is computed at all. A shared
name or version string is not a binding, and different digests are never
'low agreement'.

Nothing is merged, scored or ranked: a fact in two sources is corroborated
presence, not truth, and the originals are never modified. A document this
build cannot read is counted as unsupported, never as an empty SBOM.
FactValue
Grouphistory — Compare releases & keep history
Stabilityexperimental
Regimeany
Readstwo or more CycloneDX JSON or SPDX 2.x (JSON or tag-value) SBOM files, and optionally the released bytes (--subject).
Writesstdout (the summary, or the JSON document with --json); sbom-concordance.json (the same JSON document, only with --output)
--output namesone file
Networkoffline — This command has no network-capable path.
Exit codes0 a comparison was produced, whatever it found (comparable or incomparable_subject) · 2 a refusal: fewer than two SBOMs, a missing or unreadable SBOM or --subject file, or an --output that names an input; nothing is written
Machine outputhas-json — one sbomflow-sbom-concordance-v1 document with --json (stdout) or --output (file): the subject binding, every input's custody record and, when the binding holds, the nine dimensions

Options

OptionValueDefaultAllowedRequiredHelp
-h, --help(flag)none—noshow this help message and exit
SBOM [SBOM ...]SBOM [SBOM ...]noneanyyesTwo or more CycloneDX JSON or SPDX 2.x (JSON or tag-value) files.
--subjectFILEnoneanynoThe released bytes. SBOMFlow hashes them, and a document stating another subject digest is incomparable. It never makes a binding stronger.
--declared-same-subject(flag)false—noRecord the operator's statement that these documents describe the same subject. Recorded as declared, never verified; a digest mismatch still refuses.
--outputFILEnoneanynoAlso write the sbom-concordance JSON document here, replacing it (file).
--json(flag)false—noPrint the JSON document on stdout instead of the summary.

Examples

Compare two SBOMs of one build in two formats, checking each stated subject digest against the released bytes.

sh
sbomflow sbom-set compare sboms/gateway-2.4.1.supplier.cdx.json sboms/gateway-2.4.1.integrator.spdx.json --subject sboms/released-image.bin

Two SBOMs with the same names and version but different subject digests are incomparable, and no figure is computed.

sh
sbomflow sbom-set compare sboms/gateway-2.4.1.supplier.cdx.json sboms/other-build.cdx.json

Three sources, N-way, as the JSON document on stdout and in a file.

sh
sbomflow sbom-set compare sboms/gateway-2.4.1.supplier.cdx.json sboms/gateway-2.4.1.pipeline.cdx.json sboms/gateway-2.4.1.integrator.spdx.json --json --output sbom-concordance.json

Refused (exit 2): a comparison needs at least two SBOMs; nothing is written. Exits 2.

sh
sbomflow sbom-set compare sboms/gateway-2.4.1.supplier.cdx.json

See also: sbomflow compare-releases, sbomflow reproducible-compare, sbomflow help history.