Skip to content

ADR-017: SBOM Generation

Status

Accepted Date: 2026-02-27

Context

  • Supply chain security is increasingly important (US EO 14028, EU CRA)
  • cosalette is distributed as a PyPI wheel — consumers need to assess dependency risk
  • No SBOM, attestation, or supply-chain tooling existed in the project
  • Two dominant SBOM formats: CycloneDX (OWASP, security-focused) and SPDX (Linux Foundation, compliance-focused, ISO 5962:2021)
  • Multiple generator tools exist: syft (Anchore), cdxgen, cyclonedx-python-lib, trivy
  • The release workflow previously built the wheel independently in each publish job

Decision

  1. Format: CycloneDX JSON — security-oriented, native VEX support, simpler schema, better fit for an IoT library. SPDX can be added later as a one-flag change.

  2. Generator: syft (Anchore) — supports Python wheels natively, multi-format output, single binary, also handles OCI images if needed later.

  3. Integration points:

  4. DevContainer: syft binary installed in Dockerfile (available for local task sbom)
  5. Taskfile: task sbom builds the wheel and generates CycloneDX JSON
  6. Release workflow: After building the wheel, syft generates a CycloneDX SBOM that is attached to the GitHub Release as a downloadable asset

  7. Build-once publish-twice: The release workflow is refactored to build the wheel once and upload the same artifact to TestPyPI and PyPI, ensuring the SBOM accurately describes the published artifact.

Decision Drivers

  • Providing supply chain transparency for consumers of the published wheel
  • Aligning with Python ecosystem and OWASP security tooling conventions
  • Minimising complexity — one format, one generator, automated in release pipeline
  • Preserving ability to add SPDX later without architectural changes

Considered Options

SPDX instead of CycloneDX

  • Advantages: ISO standard (5962:2021), required by some US government agencies
  • Disadvantages: More verbose schema, license/compliance focus vs. security focus
  • Rejected because: Current consumers are personal IoT bridges, not regulated entities. CycloneDX's security orientation is a better fit. Adding SPDX output is trivial (one syft flag) if demand arises.

SBOM inside the wheel

  • Rejected because: Not standard practice. SBOMs are separate artifacts alongside the distribution, not embedded in it. PyPI does not host SBOMs.

No SBOM (status quo)

  • Rejected because: Supply chain transparency is becoming non-optional for published libraries. Even for a small project, this is minimal effort with the right tooling.

Consequences

Positive

  • Consumers can assess cosalette's dependency tree for known vulnerabilities
  • CycloneDX JSON is machine-parseable by downstream tools (Dependency-Track, Grype, Trivy)
  • Single build ensures wheel checksums match across TestPyPI and PyPI
  • syft in devcontainer enables local SBOM generation during development
  • Foundation for future supply chain improvements (attestations, SLSA provenance, VEX)

Negative

  • DevContainer image grows by ~50MB (syft binary)
  • Release workflow gains one additional step (minimal complexity)
  • SBOM must be regenerated if dependencies change (automated via release workflow)

Neutral

  • SPDX output can be layered on later without architectural changes
  • PyPI attestations (PEP 740) and SLSA provenance are deferred to a future ADR
  • DevContainer image SBOM is deferred (not relevant to end users)

2026-02-27