> ## Documentation Index
> Fetch the complete documentation index at: https://docs.roboticks.io/llms.txt
> Use this file to discover all available pages before exploring further.

# SBOM (SPDX & CycloneDX)

> Ingest SPDX and CycloneDX SBOMs. They land in the evidence pack and satisfy EU MR Annex IV.6 and CRA supply-chain requirements.

# SBOM (SPDX & CycloneDX)

A **Software Bill of Materials** lists every component (and its version) that goes into a released binary. Auditors increasingly require one — EU MR Annex IV.6, the EU Cyber Resilience Act, US Executive Order 14028. Roboticks ingests SBOMs and embeds them in the per-release evidence pack so the auditor finds the answer without asking.

Roboticks accepts both major SBOM formats:

| Format                                | Spec              | Producer examples           |
| ------------------------------------- | ----------------- | --------------------------- |
| **SPDX** 2.3 (and 3.0 when finalised) | ISO/IEC 5962:2021 | syft, scancode, Mend, FOSSA |
| **CycloneDX** 1.4–1.6                 | OWASP             | syft, trivy, anchore, snyk  |

For most teams **syft** (multi-format, multi-language) or **trivy** (image + filesystem) are the right tools. Both ship in the [bundled OSS scanners](/integrations/oss-scanners) — no extra fee.

## Why an SBOM lands in the evidence pack

Regulators ask "what's in the binary you shipped to my factory?" and they want a machine-readable answer pinned to the release tag. The Roboticks evidence pack already contains:

* Source state (git SHA, commit signature)
* Test results (JUnit, MCAP)
* Requirement coverage snapshot (ReqIF)
* Findings (SARIF + vendor reports)

Adding the SBOM closes the supply-chain loop. The pack now answers:

1. *What was tested?* — JUnit + traceability matrix
2. *What were the requirements?* — ReqIF snapshot
3. *What runtime behaviours did we capture?* — MCAP attachments
4. *What components went into the binary?* — **SBOM**

## Upload an SBOM

```bash theme={null}
rbtk sbom upload --tool syft sbom.cdx.json
rbtk sbom upload --tool trivy trivy.sbom.spdx.json
```

The CLI auto-detects SPDX vs CycloneDX by document structure. The `--tool` label is free-form for grouping in the dashboard.

## Wire it into CI

<Tabs>
  <Tab title="syft (CycloneDX)">
    ```bash theme={null}
    syft . -o cyclonedx-json=sbom.cdx.json
    rbtk sbom upload --tool syft sbom.cdx.json
    ```
  </Tab>

  <Tab title="syft (SPDX)">
    ```bash theme={null}
    syft . -o spdx-json=sbom.spdx.json
    rbtk sbom upload --tool syft sbom.spdx.json
    ```
  </Tab>

  <Tab title="trivy">
    ```bash theme={null}
    trivy fs --format cyclonedx --output sbom.cdx.json .
    rbtk sbom upload --tool trivy sbom.cdx.json
    ```
  </Tab>

  <Tab title="Container image">
    ```bash theme={null}
    # syft against a built image
    syft my-registry.acme.com/firmware:v2.4.0 -o cyclonedx-json=sbom.cdx.json
    rbtk sbom upload --tool syft sbom.cdx.json --label image=firmware:v2.4.0
    ```
  </Tab>
</Tabs>

## CI recipe — SBOM on every release

```yaml theme={null}
# .github/workflows/release-sbom.yml
name: Release SBOM
on:
  release:
    types: [published]

jobs:
  sbom:
    runs-on: ubuntu-latest
    permissions: { id-token: write, contents: read }
    steps:
      - uses: actions/checkout@v4

      - name: Install syft + trivy
        run: |
          curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b /usr/local/bin
          curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin

      - name: Build the artefact
        run: ./scripts/build.sh

      - name: Generate SBOMs
        run: |
          syft  ./build -o cyclonedx-json=sbom.cdx.json
          syft  ./build -o spdx-json=sbom.spdx.json
          trivy fs --format cyclonedx --output trivy.cdx.json ./build

      - run: pipx install roboticks-cli
      - run: rbtk auth oidc-from-github

      - name: Upload SBOMs (release-tagged)
        run: |
          rbtk sbom upload --tool syft  sbom.cdx.json --release ${{ github.event.release.tag_name }}
          rbtk sbom upload --tool syft  sbom.spdx.json --release ${{ github.event.release.tag_name }}
          rbtk sbom upload --tool trivy trivy.cdx.json --release ${{ github.event.release.tag_name }}
```

The `--release` flag pins the SBOM to the release tag — it ends up in **that** release's evidence pack, even if you re-run the workflow later.

## Where SBOMs appear

* **Release detail view** — a **Supply chain** panel shows component counts, license breakdown, and a CVE summary (via the vulnerability scan if `trivy` was used).
* **Release evidence pack** — the SBOM ships in:
  * The PDF, as a **Supply-Chain Appendix** with component-list, license-table, and the CVE summary
  * The ZIP, as raw `sbom.cdx.json` and `sbom.spdx.json` files for downstream tooling
* **Comparison** — diff one release's SBOM against the previous to spot newly introduced components.

## Diffing SBOMs

```bash theme={null}
rbtk sbom diff v2.3.0 v2.4.0

ADDED       3 components
  • openssl@3.0.13 (was: 3.0.11)
  • libcurl@8.7.1 (NEW)
  • cyclonedx-py@5.0.0 (NEW)

REMOVED     1 component
  • python-jose@3.3.0

LICENSE CHANGES
  None
```

`--output json` returns the structured diff including PURLs and license deltas.

## License surfacing

Roboticks extracts SPDX license identifiers from both SBOM formats and surfaces:

* **Per-release license summary** — total components per license
* **Copyleft check** — flags GPL/AGPL/LGPL components for review
* **Unknown licenses** — components syft/trivy couldn't classify

A `release.license_policy.yaml` lets you fail an evidence-pack generation if a forbidden license appears:

```yaml theme={null}
# .roboticks/release.license_policy.yaml
forbidden:
  - AGPL-3.0-only
  - AGPL-3.0-or-later
require_known_license: true
```

## Provenance and signatures

If your SBOM is signed (in-toto, Cosign, or DSSE-wrapped), upload the signature alongside:

```bash theme={null}
rbtk sbom upload --tool syft sbom.cdx.json --signature sbom.cdx.json.sig
```

Roboticks records the signature in the SBOM's metadata block and embeds it in the evidence pack so the auditor can re-verify.

## Quotas

| Aspect              | Limit                                                        |
| ------------------- | ------------------------------------------------------------ |
| Components per SBOM | 100,000                                                      |
| Upload size         | 500 MB per file; gzip helpful at scale                       |
| SBOMs per release   | unlimited; multiple uploads merge (latest-per-`--tool` wins) |

## Standards references

* **SPDX**: [spdx.dev](https://spdx.dev), spec ISO/IEC 5962:2021
* **CycloneDX**: [cyclonedx.org](https://cyclonedx.org), OWASP project
* **NTIA minimum elements**: [ntia.gov/SBOM](https://www.ntia.doc.gov/SBOM) — what regulators expect
* **CISA SBOM page**: [cisa.gov/sbom](https://www.cisa.gov/sbom) — US Executive Order context
* **OpenSSF**: [openssf.org](https://openssf.org) — supply-chain best practices

## Troubleshooting

<AccordionGroup>
  <Accordion title="`SBOM schema validation failed`">
    Confirm the format with `cyclonedx-cli validate` or `spdx-tools verify`. Most validation failures stem from custom fields outside the spec.
  </Accordion>

  <Accordion title="SBOM uploaded but not in the release evidence pack">
    Use `--release <tag>` on upload. Without it, the SBOM attaches to the *current* state, not the *release-tagged* state. You can re-attach:

    ```bash theme={null}
    rbtk sbom attach --release v2.4.0 --sbom-id sbom_a1b2
    ```
  </Accordion>

  <Accordion title="syft / trivy emit duplicate components">
    Both tools' default modes can produce dupes when scanning a tree with multiple package managers (e.g., npm + pip). Pass `--exclude` to narrow scope or merge SBOMs with `cyclonedx-cli merge` before upload.
  </Accordion>
</AccordionGroup>

## Next

<CardGroup cols={2}>
  <Card title="OSS scanners" icon="shield-check" href="/integrations/oss-scanners">
    syft and trivy ship bundled.
  </Card>

  <Card title="Evidence packs" icon="file-shield" href="/evidence/overview">
    Where SBOMs end up.
  </Card>
</CardGroup>
