Machine output coverage
Which commands speak --json, which produce machine-readable output another way, and where a real gap remains — audited per command against the shipped command grammar, and machine-checked by the offline test suite so this page cannot silently rot when a command is added or gains a flag.
If you are scripting against SBOMFlow, start with the CLI overview's integration contract; this page is the per-command census behind it.
The machine interface, in order of authority#
- Artifacts on disk are the API. SBOMFlow's product is deterministic, versioned JSON files. A command that writes artifacts already has a machine interface — usually a richer one than any terminal flag could carry.
sbomflow schema listnames the families;sbomflow schema show <family>documents the fields offline. --jsonwhere operators read terminals. Read/status commands emit one pure, versioned JSON document on stdout (schema_versionon every payload), with the same exit codes as the human output. Stdout stays pure: progress, notes and warnings go to stderr.- Exit codes are a machine contract of their own. Every command's exit code follows the published exit-code reference, and errors carry stable
[E0xx]codes in both text and JSON. - A failed
--jsonrun still answers on stdout. When a command that took--jsonfails, the coded error is also emitted as a versionedsbomflow-cli-error-v1document on stdout, carrying the[E0xx]code, the title, the detail, the repair, the docs link, the exit code and the retry/state-change semantics. A consumer therefore parses exactly one stream in success and in failure, and an empty stdout never has to be read as either. The stderr text is unchanged, and a command that already emits its own document on the failure path (validate --jsonprints its validate-report and then fails) keeps exactly that document — stdout never carries two.
A universal "result envelope" wrapping every command's stdout is deliberately not provided: artifacts on disk are the primary machine interface, and re-wrapping every command's output for symmetry would break existing consumers without serving a named need.
The census#
In this build the CLI grammar has 147 command paths (85 top-level commands + 62 nested verbs) — re-derived from the parser by the offline suite on every run, never carried forward from a previous release. Of these, 56 take --json, 2 are machine-native without a flag, 67 produce artifacts on disk as their machine interface, and 22 are human-only by design. The three audited gaps the first pass found are now closed, so no row is left verdicted add.
| Verdict | Count | Meaning |
|---|---|---|
| has-json | 56 | Takes --json; one versioned JSON document on stdout. |
| machine-native | 2 | Machine output is already the default: JSON with no flag, or a CI-native format. |
| artifact-suffices | 67 | The command's product is deterministic files on disk (or an identical sibling surface already speaks --json); a terminal flag would duplicate the real interface. |
| human-only-by-design | 22 | Prose, interactive or dispatch surfaces for a person at a terminal; a machine consumer has no job here (14 of the 22 are bare group names that print usage). |
| add | 0 | A real machine-consumer job unserved by any surface. The audit found three; all three now ship the flag. |
Every verdict below was audited against the command's implementation, not its help text: what it writes, what its exit code means, and which sibling command already serves the same data.
Where schema_version sits. Every --json payload carries it. Most payloads are JSON objects and carry it as a top-level key. Two — store list and store query — are JSON arrays, and their contract name rides on each element rather than in a wrapper: wrapping the array would break every consumer already parsing it, for a key those consumers do not read. upgrade plan and gate simulate shipped their version under a schema key before this promise was written; both now carry schema_version as well, with the same value, and the original key is unchanged.
The audited matrix#
<!-- JSON-COVERAGE:BEGIN — machine-checked against the shipped CLI grammar by the offline test suite. Every command path appears exactly once as the row's first backtick token; the verdict column uses only the five values defined above; a row's has-json verdict must match the real parser. Do not add, remove, or reword rows without re-running the suite. -->
| Command | Machine output today | Verdict | Why |
|---|---|---|---|
analyze | artifacts on disk | artifact-suffices | writes the full evidence artifact set (SBOM, findings, gaps, CRA report, gate…); the files are the API |
annotate | CI-native via --format | machine-native | its output IS a machine format: native CI annotations selected with --format (github, gitlab) |
approve | --json | has-json | read/status surface; one versioned JSON document on stdout |
audit | --json | has-json | read/status surface; one versioned JSON document on stdout |
auditor-pack | artifacts on disk | artifact-suffices | writes auditor-pack.zip, a self-contained read-only package |
backfill | --json | has-json | read/status surface; one versioned JSON document on stdout |
batch-run | --json | has-json | one versioned batch summary on stdout; each job's own output is captured so the document stays parseable, and every row carries that job's warning count (with the line count it was taken out of) plus its declared network posture |
bundle | artifacts on disk | artifact-suffices | writes evidence-bundle.json / .zip, the portable reviewer handoff |
case | usage only | human-only-by-design | group dispatcher: prints usage/guidance only; the machine surfaces are its nested verbs |
case accept-risk | artifacts on disk | artifact-suffices | appends the named human's bounded risk decision to the hash-chained case journal |
case assign | artifacts on disk | artifact-suffices | appends JSON case records to a hash-chained journal; case show with --json is the read side |
case communicate | artifacts on disk | artifact-suffices | drafts, approves, or records delivery in the local communication journal; it never sends |
case correlate | artifacts on disk | artifact-suffices | appends JSON case records to a hash-chained journal; case show with --json is the read side |
case disclose | artifacts on disk | artifact-suffices | records a human disclosure-timeline event without publishing anything |
case export-advisory | artifacts on disk | artifact-suffices | writes an unsigned, unsubmitted advisory draft |
case intake | artifacts on disk | artifact-suffices | appends JSON case records to a hash-chained journal; case show with --json is the read side |
case open | artifacts on disk | artifact-suffices | appends JSON case records to a hash-chained journal; case show with --json is the read side |
case propose-propagation | --json | has-json | emits a versioned proposal document with applied: false |
case remediate | artifacts on disk | artifact-suffices | appends a remediation observation and its frozen-pack verification result |
case show | --json | has-json | read/status surface; one versioned JSON document on stdout |
case transition | artifacts on disk | artifact-suffices | appends JSON case records to a hash-chained journal; case show with --json is the read side |
case triage | artifacts on disk | artifact-suffices | appends JSON case records to a hash-chained journal; case show with --json is the read side |
case reopen | artifacts on disk | artifact-suffices | appends the attributed advisory-reopen decision and transition to the hash-chained journal; no case is reopened automatically |
cmake-evidence | artifacts on disk | artifact-suffices | writes its deterministic evidence JSON (file or stdout) |
compare-releases | artifacts on disk | artifact-suffices | writes release-drift.json |
config | usage only | human-only-by-design | group dispatcher: prints usage/guidance only; the machine surfaces are its nested verbs |
config show | --json | has-json | read/status surface; one versioned JSON document on stdout |
cosign-verify | artifacts on disk | artifact-suffices | writes its verification observations JSON via --output |
dashboard | artifacts on disk | artifact-suffices | renders a static human dashboard from release-index.json, which is already the machine surface |
decisions | usage only | human-only-by-design | group dispatcher: prints usage/guidance only; the machine surfaces are its nested verbs |
decisions quarantine | --json | has-json | moves an unreadable decision journal under quarantine as an AUDITED event — the copy, its digest and the parse failure are appended before the original is unlinked, so an un-audited quarantine is unreachable; one versioned JSON document on stdout |
decisions recover | --json | has-json | read/status surface; one versioned JSON document on stdout |
decisions transactions | --json | has-json | read/status surface; one versioned JSON document on stdout |
decisions verify-transactions | --json | has-json | read/status surface; one versioned JSON document on stdout |
diffoscope-compare | artifacts on disk | artifact-suffices | writes the diffoscope comparison observations JSON |
doctor | --json | has-json | read/status surface; one versioned JSON document on stdout |
elf-evidence | artifacts on disk | artifact-suffices | writes its deterministic evidence JSON (file or stdout) |
emit-user-info | artifacts on disk | artifact-suffices | writes the UNSIGNED CRA Annex II user-information draft files |
esp-idf-evidence | artifacts on disk | artifact-suffices | writes its deterministic evidence JSON (file or stdout) |
examples | --json | has-json | read/status surface; one versioned JSON document on stdout |
explain | text for humans | human-only-by-design | renders a prose explanation composed from artifacts that are already machine-readable on disk |
firmware-extract | artifacts on disk | artifact-suffices | writes the bounded extraction inventory JSON |
gate | usage only | human-only-by-design | group dispatcher: prints usage/guidance only; the machine surfaces are its nested verbs |
gate simulate | --json | has-json | read/status surface; one versioned JSON document on stdout |
help | --json | has-json | read/status surface; one versioned JSON document on stdout |
index | usage only | human-only-by-design | group dispatcher: prints usage/guidance only; the machine surfaces are its nested verbs |
index query | --json | has-json | read/status surface; one versioned JSON document on stdout |
index rebuild | artifacts on disk | artifact-suffices | writes the derived SQLite index — itself a machine-queryable artifact (index query speaks --json) |
index validate | --json | has-json | structured errors/notes on stdout; the exit code is unchanged, so switching on --json cannot change the verdict |
index-releases | artifacts on disk | artifact-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 |
ingest-grype | artifacts on disk | artifact-suffices | writes sourced-observation JSON artifacts |
ingest-preserved-artifact | --json | 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 |
ingest-sarif | artifacts on disk | artifact-suffices | writes sourced-observation JSON artifacts |
ingest-codex-security | --json | has-json | imports a sealed local Codex Security scan as external AI-assisted observations; one versioned JSON document on stdout, every record needs_review |
ingest-trivy | artifacts on disk | artifact-suffices | writes sourced-observation JSON artifacts |
init | artifacts on disk | artifact-suffices | scaffolds sbomflow.yaml and inert review templates; the scaffolded files are the product |
license | usage only | human-only-by-design | group dispatcher: prints usage/guidance only; the machine surfaces are its nested verbs |
license inspect | --json | has-json | read/status surface; one versioned JSON document on stdout |
license install | --json | has-json | verifies a delivered entitlement offline, then files it at the per-user discovery path; one versioned, non-secret JSON result on stdout (never the customer id, signature or entitlement id) |
license status | --json | has-json | read/status surface; one versioned JSON document on stdout |
license verify | --json | has-json | read/status surface; one versioned JSON document on stdout |
linker-map-evidence | artifacts on disk | artifact-suffices | writes its deterministic evidence JSON (file or stdout) |
mcuboot-evidence | artifacts on disk | artifact-suffices | writes its deterministic evidence JSON (file or stdout) |
mcuboot-verify | artifacts on disk | artifact-suffices | writes its verification observations JSON via --output |
mender-evidence | artifacts on disk | artifact-suffices | writes its deterministic evidence JSON (file or stdout) |
migrate-in | --json | has-json | one versioned migration manifest; every row carries claim_origin and unimported files are listed with reasons |
next-actions | --json | has-json | one versioned next-actions document on stdout; aggregation only, never a decision |
notify | artifacts on disk | artifact-suffices | writes the notification plan artifact; delivery is a separate explicit step |
oci-evidence | artifacts on disk | artifact-suffices | writes its deterministic evidence JSON (file or stdout) |
passport | usage only | human-only-by-design | group dispatcher: prints usage/guidance only; the machine surfaces are its nested verbs |
passport issue | artifacts on disk | artifact-suffices | writes the signed passport files; passport verify (with --json) is the read side |
passport verify | --json | has-json | read/status surface; one versioned JSON document on stdout |
policy | usage only | human-only-by-design | group dispatcher: prints usage/guidance only; the machine surfaces are its nested verbs |
policy diff | --json | has-json | read/status surface; one versioned JSON document on stdout |
policy show | --json | has-json | read/status surface; one versioned JSON document on stdout |
portfolio | artifacts on disk | artifact-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 |
prov-map | artifacts on disk | artifact-suffices | writes a deterministic PROV mapping JSON (file or stdout) |
provenance-check | artifacts on disk | artifact-suffices | writes provenance-check observations JSON; expectations verdicts are in the artifact |
quickstart | --json | has-json | one versioned JSON document on stdout (sbomflow-quickstart-preflight-v1, or sbomflow-quickstart-ci-preflight-v1 under --ci — two shapes, so two names). The RUN's machine interface is still the artifact set it writes; --json serves the pre-write disclosure instead — the same document the terminal form renders, so a caller reading JSON and a caller reading the terminal cannot reach different conclusions about the scan root, the bounded and labelled file count, or which of the five states the output directory is in. It emits that document and exits without writing, auditing or consenting |
rauc-evidence | artifacts on disk | artifact-suffices | writes its deterministic evidence JSON (file or stdout) |
recheck | artifacts on disk | artifact-suffices | writes the recheck report artifact comparing shipped evidence to newer local advisory data |
release-chain | usage only | human-only-by-design | group dispatcher: prints usage/guidance only; the machine surfaces are its nested verbs |
release-chain verify | --json | has-json | read/status surface; one versioned JSON document on stdout |
report | usage only | human-only-by-design | group dispatcher: prints usage/guidance only; the machine surfaces are its nested verbs |
report freshness | --json | has-json | read/status surface; one versioned JSON document on stdout |
reproducible-compare | artifacts on disk | artifact-suffices | writes the digest-comparison JSON for two build trees |
review | --json | has-json | read/status surface; one versioned JSON document on stdout |
review-merge | artifacts on disk | artifact-suffices | writes the merged reviewer decision file plus a conflict report |
review-view | artifacts on disk | artifact-suffices | writes reviewer.html, a human console composed from artifacts that are already machine-readable |
schema | usage only | human-only-by-design | group dispatcher: prints usage/guidance only; the machine surfaces are its nested verbs |
schema list | --json | has-json | one versioned JSON document; exposing the flag revived a branch the parser could never reach |
schema show | --json | has-json | read/status surface; one versioned JSON document on stdout |
setup | text for humans | human-only-by-design | interactive, TTY-gated onboarding; refuses CI and redirected IO by design |
sign-bundle | artifacts on disk | artifact-suffices | writes detached signature envelope files (formats: sbomflow, in-toto) |
standards-view | --json | has-json | read/status surface; one versioned JSON document on stdout |
regime-view | --json | has-json | read-only view of an existing verified pack through a versioned profile; one versioned JSON document on stdout |
regime-profile | --json | has-json | local profile authoring kit; validate emits one versioned JSON document on stdout, and every action writes only profile files — never evidence |
store reissue-plan | --json | has-json | read-only diagnosis of a refused export; one versioned JSON document on stdout |
store reissue | — | artifact-suffices | writes a new release plus portable-reissue-receipt.json; human summary on stdout |
store | usage only | human-only-by-design | group dispatcher: prints usage/guidance only; the machine surfaces are its nested verbs |
store add | artifacts on disk | artifact-suffices | records the release into the store (JSON records + index); store list / store show with --json are the read side |
store empty-trash | --json | has-json | read/status surface; one versioned JSON document on stdout |
store export | --json | has-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 |
store list | --json | has-json | read/status surface; one versioned JSON document on stdout |
store query | --json | has-json | read/status surface; one versioned JSON document on stdout |
store register-rename | --json | has-json | read/status surface; one versioned JSON document on stdout |
store reindex | --json | has-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 |
store retain | --json | has-json | read/status surface; one versioned JSON document on stdout |
store show | --json | has-json | read/status surface; one versioned JSON document on stdout |
store timeline | --json | has-json | read/status surface; one versioned JSON document on stdout |
store verify | --json | has-json | read/status surface; one versioned JSON document on stdout |
store verify-replay | --json | has-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 |
store verify-gates | --json | has-json | read/status surface; one versioned JSON document on stdout |
support-bundle | artifacts on disk | artifact-suffices | writes the redaction-audited diagnostics zip; its contents are structured JSON |
swupdate-evidence | artifacts on disk | artifact-suffices | writes its deterministic evidence JSON (file or stdout) |
syft-scan | artifacts on disk | artifact-suffices | writes syft-result.json (file or stdout) as ingested observations |
sync-dependencytrack | artifacts on disk | artifact-suffices | dry-run by default: writes the machine-readable sync plan artifact; delivery is a separate explicit step |
sync-issues | artifacts on disk | artifact-suffices | dry-run by default: writes the machine-readable sync plan artifact; delivery is a separate explicit step |
sync-jira | artifacts on disk | artifact-suffices | dry-run by default: writes the machine-readable sync plan artifact; delivery is a separate explicit step |
sync-servicenow | artifacts on disk | artifact-suffices | dry-run by default: writes the machine-readable sync plan artifact; delivery is a separate explicit step |
twister-evidence | artifacts on disk | artifact-suffices | writes its deterministic evidence JSON (file or stdout) |
update-manifest-evidence | artifacts on disk | artifact-suffices | writes its deterministic evidence JSON (file or stdout) |
upgrade | usage only | human-only-by-design | group dispatcher: prints usage/guidance only; the machine surfaces are its nested verbs |
upgrade inspect | text for humans | human-only-by-design | pre-migration eyeball of artifact families/versions; the machine decision surface is upgrade plan with --json |
upgrade migrate-copy | artifacts on disk | artifact-suffices | writes migrated copies plus upgrade-manifest.json to a new directory, never in place |
upgrade plan | --json | has-json | read/status surface; one versioned JSON document on stdout |
upgrade validate | artifacts on disk | artifact-suffices | the identical validation ships as top-level validate, which speaks --json with the same exit contract |
uptane-evidence | artifacts on disk | artifact-suffices | writes its deterministic evidence JSON (file or stdout) |
validate | --json | has-json | read/status surface; one versioned JSON document on stdout |
validate-annexii-model | exit code + text | human-only-by-design | packaged-model self-check; the machine contract is the exit code (0 pass, non-zero fail) |
validate-article14-model | exit code + text | human-only-by-design | packaged-model self-check; the machine contract is the exit code (0 pass, non-zero fail) |
validate-config | artifacts on disk | artifact-suffices | the same config validation surfaces machine-readably in doctor --json (config block: errors and warnings) |
validate-graph | exit code + text | human-only-by-design | packaged-model self-check; the machine contract is the exit code (0 pass, non-zero fail) |
validate-ssvc-model | exit code + text | human-only-by-design | packaged-model self-check; the machine contract is the exit code (0 pass, non-zero fail) |
validate-techdoc-model | exit code + text | human-only-by-design | packaged-model self-check; the machine contract is the exit code (0 pass, non-zero fail) |
verify-bundle | --json | has-json | verification detail, per-layer under --layered; parity with passport verify --json. Version skew stays context, changing no status and no exit code |
verify-package-artifacts | artifacts on disk | artifact-suffices | writes digest-verification observations JSON |
vulndb | usage only | human-only-by-design | group dispatcher: prints usage/guidance only; the machine surfaces are its nested verbs |
vulndb change-impact | --json | has-json | explains which stored releases a source-set change touches, reusing the release comparison already on disk; --store is required so the command can only emit the shape the schema registry declares |
vulndb inspect | JSON on stdout (no flag) | machine-native | prints its report as JSON on stdout with no flag needed |
vulndb pin | artifacts on disk | artifact-suffices | records the active-snapshot pin; vulndb inspect is the JSON read side |
vulndb reindex | artifacts on disk | artifact-suffices | rebuilds the derived index from complete snapshot manifests after index loss or corruption |
vulndb retention | artifacts on disk | artifact-suffices | writes a deterministic needed-snapshots binding and conservative retention plan; deletion requires explicit --apply |
vulndb sources | --json | has-json | read/status surface; lists the registered sources and what each one has ACTIVE locally |
vulndb update | artifacts on disk | artifact-suffices | records a pinned, hashed snapshot plus its manifest (the one explicit network step) |
vulndb verify | --json | has-json | read/status surface; one versioned JSON document on stdout |
zephyr-evidence | artifacts on disk | artifact-suffices | writes its deterministic evidence JSON (file or stdout) |
<!-- JSON-COVERAGE:END -->
The three audited gaps — CLOSED#
Only three of the 123 non-dispatch paths audited in that pass were verdicted a real gap, all of them read-only verifiers or listings whose sibling commands already speak --json. All three now ship the flag, additively:
schema list— the machine-readable listing already existed internally, behind a flag the parser never declared, so the branch could not be reached. Declaring--jsonexposes the document and revives that dead branch.index validate— integrity notes and errors are now a structured document. The exit code is unchanged, so a caller that switches on--jsoncannot get a different verdict from the one the text reported.verify-bundle— verification detail, per-layer under--layered, at parity withpassport verify --json. Version skew stays context: it appears in the document but changes no layer status and no exit code.
Human text, exit codes, and artifacts are byte-identical to before on every one of the three.
verify-bundle --layered: what a zero exit proves#
Every layered document carries exit_code_scope, and the human output prints the same sentence, because a consumer reading only one of the two must not have to find that boundary in prose somewhere else. Exit 0 proves that no layer failed and that every layer named by --require-layers reached a trusted status. It does not prove the bundle is trusted (read verdict / trusted_use), that an unrequired layer verified, that the evidence inside the bundle is correct, or anything at all about conformity.
--require-layers content,envelope,semantics,authority adds an expectation block — required, met, unmet, and a per-layer row carrying the observed status alongside the accepted_statuses that would have satisfied the requirement — and exits 7 when a well-formed report does not meet it. The full layer report is still emitted: an expectation narrows what the exit code means, never what the run reports. Without the flag the block is absent and every exit code is exactly what it was; a genuine verification failure stays 3 and outranks 7; an unknown layer name is refused as an input error (2) before verification runs, because a stricter check that silently did not happen is worse than no check. Nothing else on this page is a commitment to add flags; the remaining non---json rows are deliberate.
Doctor facts added by the same audit#
The same audit reviewed two outstanding doctor requests, and both shipped alongside it as facts-only additions — doctor reports local facts, fabricates nothing, and never decides:
- offline docs availability — whether
sbomflow help errorandsbomflow help exitrender from this install, with their code counts, and which web docs URLs error messages will print (recorded, never fetched). - path length — the absolute target path's length in characters and bytes next to the filesystem's own reported
PATH_MAX/NAME_MAXlimits, or an honest "not reported" where the platform has no answer.
See Troubleshooting for what doctor reports and why each row is context, never a verdict.