Priority: P3 · Effort: M · Theme: Supply-chain & CI/governance hardening
Filed from the 2026-06-29 repo quality audit.
Problem
Releases ship a signed APK with a Sigstore attestation, but no machine-readable bill of materials of what is inside that APK is published for downstream auditors or CVE forensics. The dependency-graph SBOM that exists is API-only, default-branch-scoped, and (per the Netty saga) includes build-only UTP test tooling that never ships — per the repo's own docs it does not describe the shipped artifact.
Evidence
docs/release/SHIPPING.md:489 — gh release create … app-release.apk, then GitHub auto-generates the attestation. APK only.
grep -rni 'sbom\|cyclonedx\|spdx' app/build.gradle.kts gradle/libs.versions.toml → nothing.
docs/security-posture.md:311 — the existing dependency-graph SBOM lists 24 build-only UTP Netty entries that never ship in the APK.
Proposed approach
Add a CycloneDX (or SPDX) Gradle plugin scoped to releaseRuntimeClasspath, generate the SBOM at release-cut, and attach it one-shot to gh release create alongside the APK (immutable-compatible; inherits the same free GitHub auto-attestation). This is distinct from the retired cosign/SLSA build-provenance tooling (#210/#211) — an SBOM is a bill-of-materials artifact, not build provenance, and one-shot attachment avoids the immutable-release incompatibility that retired that machinery. M effort: plugin wiring + SHIPPING.md update + lockfile interaction.
Acceptance criteria
Problem
Releases ship a signed APK with a Sigstore attestation, but no machine-readable bill of materials of what is inside that APK is published for downstream auditors or CVE forensics. The dependency-graph SBOM that exists is API-only, default-branch-scoped, and (per the Netty saga) includes build-only UTP test tooling that never ships — per the repo's own docs it does not describe the shipped artifact.
Evidence
docs/release/SHIPPING.md:489—gh release create … app-release.apk, then GitHub auto-generates the attestation. APK only.grep -rni 'sbom\|cyclonedx\|spdx' app/build.gradle.kts gradle/libs.versions.toml→ nothing.docs/security-posture.md:311— the existing dependency-graph SBOM lists 24 build-only UTP Netty entries that never ship in the APK.Proposed approach
Add a CycloneDX (or SPDX) Gradle plugin scoped to
releaseRuntimeClasspath, generate the SBOM at release-cut, and attach it one-shot togh release createalongside the APK (immutable-compatible; inherits the same free GitHub auto-attestation). This is distinct from the retired cosign/SLSA build-provenance tooling (#210/#211) — an SBOM is a bill-of-materials artifact, not build provenance, and one-shot attachment avoids the immutable-release incompatibility that retired that machinery. M effort: plugin wiring + SHIPPING.md update + lockfile interaction.Acceptance criteria
releaseRuntimeClasspath(excludes build-only test configs).docs/release/SHIPPING.md§B documents attaching the SBOM in the one-shotgh release create.