Skip to main content
Private preview. fremforge is in private preview — invited customers only. Content is still subject to change. Request access →

SBOMs (Software Bill of Materials)

An SBOM (Software Bill of Materials) is the per-build ingredient list — every direct and transitive dependency baked into an artifact, with versions and hashes. fremforge stores SBOMs your CI produces so the inventory survives across builds, image rotations, and dependency drift.

When SBOMs are generated automatically

If your repo was scaffolded by fremforge, it ships with a sbom-syft.yaml workflow that fires on release tags only — any tag matching v* (e.g. v1.2.3, v2026.06). This is the standard semver / calver release-tag shape and matches the SLSA provenance spec.

Trigger an SBOM by tagging a release

From the command line:

git tag v1.2.3
git push origin v1.2.3

From the Forgejo UI: open the repo → ReleasesNew release — set a tag name starting with v (e.g. v1.2.3) and click Publish.

A one-off SBOM for an arbitrary commit is also available via the Actions tab → SBOM (Syft)Run workflow.

Why tag-only? Generating SBOMs on every commit produces dozens of nearly-identical manifests that nobody reads — auditors want the SBOM “at the moment we shipped release X”. Tag-only keeps the SBOM count proportional to your actual release cadence. If your team genuinely needs per-commit SBOMs (high-frequency release trains), edit the workflow’s on: block in your repo and add push: { branches: [main] }.

What fremforge accepts

  • CycloneDX (JSON or XML) — preferred. The wider tooling ecosystem (Trivy, Syft, anchore) emits CycloneDX natively.
  • SPDX 2.x / 3.x (JSON) — also accepted. The ingest validates spdxVersion before storing.

Each SBOM is stored with:

  • The SHA-256 digest of the artifact it describes (so the SBOM provably belongs to that build).
  • An optional in-toto attestation bundle (JSONL) — Cosign-signed when your workflow signs the SBOM. The ingest validates the JSONL shape + per-line in-toto fields before storing.
  • The artifact name + version, exactly as your workflow names it. Used for the de-duplication key (same digest + name + version = one row, not many).

5 MiB hard cap per SBOM. The ingest endpoint returns 413 payload-too-large above that — chunk large monorepo SBOMs by module if you hit it.

How to push an SBOM from CI

The fremforge runner image ships Syft and Trivy, so most workflows are a 3-line snippet:

- name: Generate + push SBOM
  run: |
    syft packages dir:. --output cyclonedx-json=sbom.json
    DIGEST=$(sha256sum dist/myapp | cut -d' ' -f1)
    curl -sS --fail \
      -H "Authorization: Bearer ${{ secrets.FREMFORGE_SBOM_TOKEN }}" \
      -F "sbom=@sbom.json" \
      -F "artifact_name=myapp" \
      -F "artifact_version=${GITHUB_SHA}" \
      -F "sha256_digest=${DIGEST}" \
      -F "format=cyclonedx-json" \
      "https://frem.sh/_app/api/v1/orgs/${ORG}/sboms"

The PAT needs the sboms:write scope; mint one from Admin → API tokens with the SBOM-push scope checked.

Where SBOMs surface in the admin UI

Admin → Code security → SBOMs lists every ingested SBOM, grouped by artifact. Each row shows:

  • Artifact name + version + SHA-256 digest
  • Format (CycloneDX / SPDX) + size
  • Ingested timestamp + the user/PAT that pushed it
  • “Verify signature” link when an attestation is attached

How SBOMs interact with other security signals

  • Scorecard — drives the Dependencies + Vulnerabilities scores on the OpenSSF Scorecard computation.
  • OSV recommendations — when an OSV advisory matches a component listed in an SBOM, the recommendation surfaces with the SBOM-anchored impact (“this affects build myapp@v1.2.3 from 2026-05-12”).
  • Cosign — the SBOM-signing flow uses the same Cosign trust root as image signing. See Cosign for the verification path.

Retention

SBOMs follow the per-tenant audit retention policy (90 / 180 / 365 / 730 days configurable). The SHA-256 digest of every SBOM stays in the hash-chained event index forever for tamper-evidence; the JSON payload itself ages out per your policy.

See also