---
id: 10-sbom-ci-workflow
epic: security-headers-rate-limit-sbom
title: SBOM CI workflow and ADR-023 amendment
type: technical-story
status: done
feature: ci
depends-on: []
blocks: []
created: 2026-05-20T00:00:00Z
updated: 2026-05-20T11:33:07.860Z
---

## Goal

Amend `.github/workflows/release-please.yml` to generate a CycloneDX SBOM and upload it as a GitHub release asset whenever release-please cuts a release, and capture the concrete workflow step as an amendment to ADR-023 §10.

## Why

Consumers pursuing SOC 2 / ISO 27001 / FedRAMP / EU CRA must answer "what's in version X" without inventory inspection. A CycloneDX SBOM attached to every GitHub release gives auditors a machine-readable, per-release artifact. `pnpm dlx` avoids adding `@cyclonedx/cyclonedx-npm` to the lockfile (CI-only tool per ADR-022). The Renovate-pinned SHA on `softprops/action-gh-release` follows the established ADR-023 pattern.

## Done when

- `.github/workflows/release-please.yml` has a conditional step that runs `pnpm dlx @cyclonedx/cyclonedx-npm --output-file sbom-<tag>.cdx.json --output-format json` when `steps.release.outputs.releases_created == 'true'`.
- A `softprops/action-gh-release@<SHA>` step (Renovate-managed SHA per ADR-023) uploads the SBOM JSON file as a release asset with `tag_name: ${{ steps.release.outputs.tag_name }}`.
- `docs/decisions/adr-023-ci-security-and-supply-chain.md` contains a new amendment subsection capturing the SBOM step's concrete shape and rationale.
- Local validation: `pnpm dlx @cyclonedx/cyclonedx-npm --output-file sbom-test.cdx.json` succeeds and produces valid CycloneDX JSON.
- `pnpm typecheck && pnpm lint && pnpm test && pnpm conformance && pnpm fallow:audit && pnpm coverage:diff` all pass.

## In scope

- `.github/workflows/release-please.yml` — conditional SBOM generation + upload steps.
- `softprops/action-gh-release@<SHA>` with Renovate-managed SHA (choose a recent stable release; Renovate will keep it current).
- `docs/decisions/adr-023-ci-security-and-supply-chain.md` — amendment subsection §10 SBOM.

## Out of scope

- Per-PR SBOM generation — release-only per PRD.
- SBOM signing / SLSA provenance attestation — bare CycloneDX only; attestation is a future PRD.
- Per-package SBOMs — root SBOM covers all workspace packages; industry practice for monorepos.
- `@cyclonedx/cyclonedx-npm` added to `package.json` — invoked via `pnpm dlx` only.

## Tasks

- [x] Add conditional SBOM generation step (`pnpm dlx @cyclonedx/cyclonedx-npm --output-file sbom-${{ steps.release.outputs.tag_name }}.cdx.json --output-format json`) and upload step (`softprops/action-gh-release@<SHA>` with `files:` pointing to the SBOM and `tag_name:` from release-please output) to `.github/workflows/release-please.yml`; add amendment subsection to `docs/decisions/adr-023-ci-security-and-supply-chain.md` documenting the concrete step shape and rationale; all gates pass.
