The clean-machine installed journey

This is the whole human release-evidence workflow, walked end to end on a clean, offline machine, through the installed sbomflow command only — from installing the wheel to a four-layer bundle verification, including a real human review with an attributed reviewer, an enforced release gate, and a tamper that is caught and recovered.

Every step below is exercised by an offline harness (make onboard-check) that builds the wheel, installs it into a fresh virtual environment, unsets PYTHONPATH so only the installed package can be imported, blocks every network transport, and then runs each command and asserts that the guidance on this page actually appears. The harness writes one machine-readable journey record with every username, temporary path, and version normalised out, so two runs at a pinned time are byte-identical. A recipe here cannot rot into something that no longer works.

Three rules hold throughout:

  • Offline by default. Nothing on this page reaches the network. The bundled sample advisory feed is the clearly non-real CVE-SAMPLE-* feed.
  • Observed is not reviewed. The engine observes; a human decides. The review and approval steps record real, attributed decisions — never fabricated, never promoted from an observation. See observed vs reviewed.
  • Exit codes are the contract. 0 healthy, 1 an enforced gate said no, 2 usage/input, 3 a bundle verification failure, 4 integrity/structural, 5 needs attention. See exit codes.
StepWhat it proves
OJ-01Access and install verify
OJ-02Environment doctor
OJ-03Quickstart (init + first offline audit)
OJ-04Offline audit
OJ-05Inspect and explain the gate
OJ-06Human review — attributed
OJ-07Human approval — attributed
OJ-08Enforced release gate
OJ-09Portable evidence bundle
OJ-10Sign / attest (optional)
OJ-11Four-layer verify — directory
OJ-12Four-layer verify — archive

OJ-01 — Access and install verify

On a machine with no package-index egress, install the wheel offline and confirm the binary is on the path:

bash
pip install --no-index --no-deps sbomflow-<version>-py3-none-any.whl
sbomflow --version
sbomflow --help

sbomflow --version prints sbomflow <version>; sbomflow --help lists every command. The harness additionally proves the imported package resolves from the installed wheel, not from any source tree.

OJ-02 — Environment doctor

Ask the tool what it sees about this machine before doing any work:

bash
sbomflow doctor .

doctor reports environment and config facts — the python: version, which optional extra packages are present, and the discovered config: — and never touches the network. Absent optional extras are stated as absent, never hidden.

OJ-03 — Quickstart

One command auto-detects the project, runs a first offline audit, scaffolds a starter config, and prints copy-paste next steps:

bash
sbomflow quickstart . --output ./sbomflow-evidence

It prints detected: with the product it found, running offline audit, and a Next steps block, finishing with SBOMFlow quickstart complete. The gate here is informational; nothing is enforced yet.

OJ-04 — Offline audit

Produce the canonical, deterministic evidence pack and a portable bundle (run from inside your product directory, shown here as .):

bash
sbomflow audit . --output ./evidence \
  --product-name "Your Product" --product-version 1.0.0 --as-of 2026-06-16T12:00:00+00:00

The summary line starts sbomflow analyze, states what it observed, and prints the gate verdict. The run writes evidence-pack.json and the full artifact set; with inputs and --as-of pinned, the artifacts are byte-reproducible.

OJ-05 — Inspect and explain

Read the gate back in plain English:

bash
sbomflow explain ./evidence --gate

It prints Release gate: with the verdict and a Reviewer boundary: line stating the gate is deterministic policy — not a legal or conformity decision.

OJ-06 — Human review

Machine observation is not human review. Record a real, attributed evidence acceptance — the decision names its reviewer and appends a hash-chained audit event:

bash
sbomflow review ./evidence --accept vulnerability_disclosure_policy \
  --reviewer you@example.com --reviewed-at 2026-06-16T12:00:00+00:00

The command prints Recorded … with the audit event appended: path. An acceptance without a --reviewer is refused as unattributed — a human decision must name its author.

OJ-07 — Human approval

Record an attributed release sign-off for a required role:

bash
sbomflow approve ./evidence --role release-manager \
  --reviewer approver@example.com --as-of 2026-06-16T12:00:00+00:00

There is no actorless way to satisfy a quorum; the --role grant is an attributed, audited record.

OJ-08 — Enforced gate

Now re-run the gate as an enforced policy. The approval just recorded satisfies the required role, so the same policy that would block now passes:

bash
sbomflow audit . --output ./evidence \
  --product-name "Your Product" --product-version 1.0.0 \
  --require-role release-manager --fail-on-missing-approvals \
  --approvals ./evidence/approvals.json --as-of 2026-06-16T12:00:00+00:00

With --require-role and --fail-on-missing-approvals, an unmet role exits 1; here the gate prints PASS and exits 0 because the quorum is complete.

OJ-09 — Evidence bundle

Assemble the portable reviewer/auditor handoff and its deterministic zip:

bash
sbomflow bundle ./evidence --zip ./evidence/evidence-bundle.zip \
  --as-of 2026-06-16T12:00:00+00:00

This composes existing artifacts into evidence-bundle.json and a reproducible evidence-bundle.zip. The bundle adds no authority of its own; it is a handoff, not a conclusion.

OJ-10 — Sign or attest

Signing is an optional capability that needs the sbomflow[sign] extra — run sbomflow sign-bundle ./evidence --key reviewer.key with your local PEM key (add --format in-toto for a DSSE attestation). It writes a signature over the bundle manifest (integrity/authenticity only, never conformity). When the cryptography extra is not installed, this step is skipped honestly — the harness never produces a fake signature — and the four-layer verify below still checks content, offline, with the standard library alone. (Shown inline because it needs the optional extra and a key, so the offline docs harness cannot run it; make onboard-check exercises it end to end where the extra is present.)

OJ-11 — Four-layer verify (directory)

Verify the whole bundle, not just its manifest:

bash
sbomflow verify-bundle ./evidence --layered

It reports four independent dimensions, never collapsed into one verdict: content (every listed member is present and matches its recorded sha256), envelope (the signature verifies over the manifest bytes), semantics (a DSSE attestation has the expected payload type and binding), and authority (the verified signer is on your allow-list). Exit 0 means no dimension failed; exit 3 means one did. For an unsigned bundle, content verifies and the run exits 0, but the overall verdict is honestly NOT TRUSTED — full trust needs a signature and an authorised-signer policy, which is what OJ-10 adds. See read an evidence bundle.

OJ-12 — Four-layer verify (archive)

The same four-layer check runs against a .zip archive of the bundle — point it at the archive with sbomflow verify-bundle ./evidence/evidence-bundle.zip --layered (shown inline because the offline docs harness does not seed a zip; the OJ-09 zip is verified for real by make onboard-check).

Hostile archives (path traversal, symlinks, duplicate members, decompression bombs) are refused before any byte is trusted, and nothing is extracted outside a temporary working directory.


When something goes wrong — failure injections

Each scenario below creates a real failure and recovers it strictly by its published playbook — no hand-editing files, no deleting journals. The harness runs all six and records the diagnose → recover path for each.

FI-01 — Wrong directory

Point the tool at the wrong directory — a path that does not exist, or a single file — and the failure explains itself: [E001] (target not found) or [E002] (not a directory), each with a one-line fix and a docs link, exit 2. Recover by pointing at a real product directory. See your first audit.

FI-02 — Evidence gap or unmet approval role

Enforce a required sign-off role with no approval on file and the gate blocks: exit 1, reason code GATE_MISSING_APPROVALS, naming the unmet role. explain --gate restates it. Recover with an attributed sbomflow approve --role … and re-run — the gate passes once the role is covered. Follows PB-08. The sibling case, an evidence gap, is closed by supplying real evidence and a human acceptance, never inference.

FI-03 — Invalid bundle content

A bundle whose listed member is absent is not partially trusted: the layered content dimension reports member_missing and the run exits 3. Recover by re-exporting from the canonical output directory (re-run the audit), then verify again. See read an evidence bundle.

FI-04 — Unauthorized signer

A cryptographically valid signature from a signer you did not authorise is reported valid_but_unauthorized and is not trusted (exit 3). Recover by confirming you hold the right key and adding its fingerprint to the --authorized-key-sha256 allow-list. This dimension needs a real signature, so it runs only when the sbomflow[sign] extra is present — otherwise it is skipped honestly. See read an evidence bundle.

FI-05 — Tamper (alter one non-identity artifact)

Alter a single non-identity artifact inside the bundle — a derived coverage report, not the product identity — and the content dimension catches it: member_hash_mismatch, verdict NOT TRUSTED, exit 3, even though the manifest signature would still be intact. The documented recovery is a re-export from the canonical output directory (re-run the audit); because artifacts are deterministic, the true bytes are restored and the next verify exits 0. Never hand-edit a member to a new shape.

FI-06 — Interrupted decision transaction

If a decision command is killed between writing the decision file and appending its audit event, a recoverable journal is left behind — never a silently half-recorded decision. Diagnose with sbomflow decisions verify-transactions (exit 5, a recoverable row) and heal it with sbomflow decisions recover --operation-id <id>; a clean re-verify follows. Follows PB-01.


The journey record

make onboard-check writes one normalised JSON record: per step the command, the expected and observed exit code, and the deterministic artifact digest; per injection the diagnose and recover commands with their exit codes; and whether the sign/attest step ran or was skipped. Usernames, temporary paths, and the version are normalised to stable placeholders, so a rerun at the same pinned time is byte-identical. Artifact digests are preserved as deterministic facts.

See also