SBOM and Supply Chain¶
Bijux Canon generates separate CycloneDX inventories for production and
development dependencies of each package. bijux-canon-dev prepares the
requirements inputs; shared Make logic invokes pip-audit, names the artifacts,
and can validate them with the CycloneDX CLI; release workflows stage available
SBOMs beside package assets.
flowchart LR
metadata[Package pyproject.toml] --> writer[requirements_writer]
workspace[Local package map] --> writer
writer --> prod[requirements.prod.txt]
writer --> dev[requirements.dev.txt]
prod --> audit[pip-audit CycloneDX output]
dev --> audit
audit --> sboms[Production and development CDX JSON]
sboms --> validate[CycloneDX validation]
sboms --> release[Optional release attachment]
Requirement Inputs¶
sbom.requirements_writer reads [project].dependencies for the production
set. For the development set it appends the selected optional dependency group,
which defaults to dev. Requirement strings are deduplicated in declaration
order.
Workspace dependencies are rewritten as absolute file: requirements pointing
at the package checkout. Extras and environment markers are preserved. This
lets the downstream resolver inspect the local source instead of requiring an
already published sibling version.
The generated requirements files are inputs, not complete SBOMs. They begin from direct package metadata; pip-audit performs downstream dependency resolution and writes the CycloneDX document.
Output Identity¶
Default package output lives under artifacts/<package>/sbom/:
requirements.prod.txt
requirements.dev.txt
<package>-<resolved-version>-<git-sha>.prod.cdx.json
<package>-<resolved-version>-<git-sha>.dev.cdx.json
summary.txt
The filename records package, resolved version, short Git SHA, and dependency
scope. If version resolution is unavailable, the Make layer can fall back to
0.0.0; such a filename is diagnostic evidence, not release-quality version
identity.
Production and development inventories answer different questions. The production document approximates dependencies needed by consumers; the development document also covers tools used to test, document, audit, and build the package. Never publish one scope under the other’s name.
Generation and Validation Are Separate¶
The sbom target cleans prior SBOM output, generates both scopes, and writes a
component-count summary. Current generation commands tolerate pip-audit
failure with || true, and the summary is best-effort. Therefore:
make sbomrecords an attempted generation; it does not by itself prove that both documents exist or satisfy CycloneDX validation.
Use the separate validator for an acceptance claim:
make sbom PACKAGE=bijux-canon-runtime
make -f "$PWD/makes/packages/bijux-canon-runtime.mk" \
-C packages/bijux-canon-runtime sbom-validate
The root dispatcher exposes sbom; the validator is a package-profile target.
Package directories do not contain standalone Makefiles, so direct validation
must include the repository profile path.
sbom-validate refuses a missing CLI, an empty SBOM directory, or any document
rejected by cyclonedx validate. Inspect the generated files and command output
before reporting success.
flowchart TD
generate[Run sbom generation] --> exists{Both scope files exist?}
exists -- no --> fail[Generation incomplete]
exists -- yes --> validate[Run sbom-validate]
validate --> valid{All documents valid?}
valid -- no --> fail
valid -- yes --> identity[Confirm package, version, SHA, and scope]
identity --> retain[Retain with exact release artifact]
Supply-Chain Claim Ladder¶
An SBOM moves through independent decisions. Preserve the evidence for every rung actually claimed:
| Claim | Required evidence | Does not establish |
|---|---|---|
| dependency input was prepared | package metadata plus generated production or development requirements | successful dependency resolution |
| inventory was generated | nonempty CycloneDX JSON plus pip-audit completion record | structural validity or vulnerability acceptance |
| inventory is structurally valid | successful cyclonedx validate result for the exact bytes |
that every dependency is safe or complete |
| vulnerability policy accepted the resolution | audit report, ignore policy, and gate verdict | artifact provenance or build reproducibility |
| SBOM was staged with a release candidate | stable staged name, workflow run, source SHA, and package version | publication at a registry or release page |
| published SBOM describes a released artifact | destination identity, SBOM digest, wheel/sdist or image digest, and common tagged source | signature, attestation, or runtime safety |
The repository currently provides generation, validation, audit policy, and optional release staging as separate surfaces. It does not provide signing or a build-provenance attestation. Consumers needing those guarantees must add a separate trusted control rather than infer them from CycloneDX presence.
Vulnerability Ignores¶
SBOM generation passes the configured vulnerability ignore IDs to pip-audit. An ignored advisory is excluded from the audit decision; it is not evidence that the dependency is patched or that the issue is non-exploitable. Review package-specific ignore sets alongside the security gate and remove entries when their applicability ends.
Supply-chain inventory and vulnerability policy remain separate claims. An SBOM can be structurally valid while describing a vulnerable component, and a clean audit can still be incomplete if dependency resolution failed.
Release Staging¶
The release-artifact workflow stages available files under stable names:
<package>-sbom-prod.cdx.json;<package>-sbom-dev.cdx.json;<package>-sbom-summary.txt.
The workflow skips the SBOM attachment block when the directory is absent and ignores unrecognized filenames. Staged Actions artifacts are retained for 14 days. The build and release decision must therefore check that required SBOM assets were actually staged rather than infer their presence from workflow success elsewhere.
Consumer Verification¶
Retain an SBOM with the exact wheel, source distribution, or OCI artifact it describes. Confirm:
- package and version identity match the release;
- the Git SHA belongs to the tagged source;
- production and development scopes are not conflated;
- the JSON passes CycloneDX validation;
- local
file:references have been interpreted in the build context; - vulnerability exceptions and audit date are available;
- the release asset bytes and registry identity are preserved.
The current SBOM path provides dependency inventory and component counts. It does not sign artifacts, attest the build environment, prove source reproducibility, or establish runtime safety. Those claims require separate controls and evidence.
Failure Routing¶
| Symptom | Inspect first |
|---|---|
| requirements file is empty | package dependency metadata and selected optional group |
| workspace dependency cannot resolve | local package name map and absolute package path |
filename contains 0.0.0 |
version resolver, Hatch environment, and Git tags |
no CDX file after make sbom |
pip-audit output and cache/network resolution |
| validation fails | document syntax, CycloneDX version, and incomplete generation |
| release lacks an SBOM | source artifact directory, naming pattern, and staging log |
| component counts differ between runs | resolver inputs, markers, Python version, and dependency index state |
See Security Gates for audit semantics and Release Support for version and publication authority.