# AI-BOMs in practice: documenting models and datasets with CycloneDX ML-BOM and SPDX 3.0

> What an AI bill of materials should record for models and datasets, how CycloneDX ML-BOM and SPDX 3.0 represent it, and how to verify it in CI.

- Published: 2026-10-24
- Author: Enforster AI (https://enforster.ai)
- Category: AI Governance
- Tags: AI Governance, Supply Chain, Compliance, Risk Management, Engineering
- Canonical URL: https://blog.enforster.ai/ai-bom-cyclonedx-ml-bom-spdx-ai-profile

## Key takeaways

- An AI bill of materials (AI-BOM) is a machine-readable inventory of the models, datasets, and software components in an AI system, with hashes and provenance for each.
- CycloneDX added ML-BOM support in version 1.5 in June 2023, with a machine-learning-model component type, a data component type, and a modelCard object.
- SPDX 3.0, released on April 16, 2024, added AI and Dataset profiles; its AIPackage class includes fields such as safetyRiskAssessment, autonomyType, and useSensitivePersonalInformation.
- OWASP LLM03:2025 recommends a signed, up-to-date SBOM and says teams should evaluate AI BOM options starting with OWASP CycloneDX.
- An AI-BOM is only useful if CI checks it: fail the build when a deployed model file's hash does not match the hash recorded in the BOM.

---

An AI bill of materials (AI-BOM) is a machine-readable inventory of everything an AI system is built from: the models, the datasets they were trained or tuned on, and the software that loads and serves them, with a hash and provenance for each. Two open standards can carry it today: CycloneDX, which added ML-BOM support in version 1.5, and SPDX 3.0, which added AI and Dataset profiles. The practical goal is simple: when a model, dataset, or library turns out to be compromised, you can answer "where do we use it?" in minutes, and your pipeline can refuse to deploy an artifact that does not match what was reviewed.

This article covers what an AI-BOM should record, how each standard represents it, and how to make the BOM enforceable instead of decorative.

## Why does an AI system need more than a normal SBOM?

A software bill of materials lists packages and versions. That covers the inference server and its Python dependencies, but it misses the parts that most often determine an AI system's behavior and risk:

- **Model weights.** A checkpoint is a large binary that a normal package scanner does not see. A pickle-based file can also execute code on load (see [loading third-party model weights safely](https://blog.enforster.ai/loading-third-party-model-weights-safely)).
- **Lineage.** A fine-tuned model inherits the properties of its base model and its training data. Neither shows up in a dependency tree.
- **Datasets.** Training and evaluation data carry licensing, personal data, and bias questions that auditors and data protection teams will ask about.

The OWASP Top 10 for LLM Applications addresses this under [LLM03:2025 Supply Chain](https://genai.owasp.org/llmrisk/llm032025-supply-chain/). It recommends keeping an up-to-date, accurate, and signed inventory of components using an SBOM, notes that model cards offer no guarantees about a model's origin, and states that AI BOMs and ML SBOMs are an emerging area where teams should evaluate options starting with OWASP CycloneDX.

An AI-BOM is not a replacement for an organizational model inventory. The inventory answers governance questions such as owner, purpose, and risk tier across the company (see [a practical AI model inventory](https://blog.enforster.ai/ai-governance-model-inventory)). The AI-BOM is the per-system technical record that the inventory can point to.

## What should an AI-BOM record?

For every AI system, record at least the following. Keep it to what you can verify; an empty field is better than a guessed one.

| Component | Record | Why it matters |
| --- | --- | --- |
| Deployed model | name, version, file hashes, format, supplier | Lets CI confirm the reviewed file is the deployed file |
| Base model | name, version, source URL, license | Lineage for fine-tunes; license obligations |
| Adapters (LoRA etc.) | name, version, hashes, source | Small files that change behavior and are easy to swap |
| Datasets | name, version, classification, contains personal data | Data protection and licensing review |
| Serving software | packages and versions, as in a normal SBOM | Vulnerability matching |
| External model APIs | provider, model identifier, data handling terms | Dependencies you cannot hash but still rely on |
| Known limitations | documented misuse cases and failure modes | Context for incident response and risk review |

Hosted model APIs deserve a line even though there is no file to hash. If a provider or gateway in your request path is compromised or changes behavior, the BOM should tell you which systems depend on it.

## How does CycloneDX represent models and datasets?

CycloneDX introduced ML-BOM in [version 1.5, announced on June 26, 2023](https://cyclonedx.org/news/cyclonedx-v1.5-released). The standard is now also published by Ecma International as [ECMA-424](https://ecma-international.org/publications-and-standards/standards/ecma-424/), whose 2nd edition (December 2025) defines CycloneDX 1.7.

The [CycloneDX 1.6 JSON reference](https://cyclonedx.org/docs/1.6/json/) defines the pieces you need:

- Component `type` values `machine-learning-model` and `data`, alongside the familiar `library` and `application`.
- A `modelCard` object on a model component, with `modelParameters` (including `approach`, `task`, `architectureFamily`, `modelArchitecture`, `datasets`, `inputs`, and `outputs`), `quantitativeAnalysis` with `performanceMetrics`, and `considerations` (including `useCases`, `technicalLimitations`, and `ethicalConsiderations`).
- Dataset entries with fields such as `type`, `name`, `classification`, `sensitiveData`, and `governance`.
- The standard `hashes` array and `pedigree` object, which records `ancestors`, `variants`, and other lineage.

A trimmed example for a fine-tuned support classifier:

```json
{
  "bomFormat": "CycloneDX",
  "specVersion": "1.6",
  "version": 1,
  "components": [
    {
      "type": "machine-learning-model",
      "bom-ref": "model-ticket-router",
      "name": "ticket-router",
      "version": "2026.10.2",
      "hashes": [
        { "alg": "SHA-256", "content": "9f2c...e41a" }
      ],
      "pedigree": {
        "ancestors": [
          { "type": "machine-learning-model", "name": "example-base-model", "version": "1.0" }
        ]
      },
      "modelCard": {
        "modelParameters": {
          "approach": { "type": "supervised" },
          "task": "text-classification",
          "datasets": [
            {
              "type": "dataset",
              "name": "support-tickets-2026q3",
              "classification": "confidential"
            }
          ],
          "inputs": [{ "format": "text/plain" }],
          "outputs": [{ "format": "application/json" }]
        },
        "considerations": {
          "useCases": ["Route inbound support tickets to a queue"],
          "technicalLimitations": ["Not evaluated on non-English tickets"]
        }
      }
    }
  ]
}
```

The hash in the example is shortened for readability. A real `content` value must be the full hex digest, 64 characters for SHA-256.

## How does SPDX 3.0 represent AI systems?

The Linux Foundation [released SPDX 3.0 on April 16, 2024](https://www.linuxfoundation.org/press/spdx-3-revolutionizes-software-management-in-systems-with-enhanced-functionality-and-streamlined-use-cases), adding profiles for use cases including AI model training and characterization and dataset provenance.

In the SPDX 3.0.1 model, the [AIPackage class](https://spdx.github.io/spdx-spec/v3.0.1/model/AI/Classes/AIPackage/) describes a model or AI system. Its fields lean toward governance: `autonomyType`, `domain`, `energyConsumption`, `hyperparameter`, `informationAboutTraining`, `limitation`, `metric`, `modelExplainability`, `safetyRiskAssessment`, `standardCompliance`, `typeOfModel`, and `useSensitivePersonalInformation`, among others. The DatasetPackage class adds dataset fields such as `datasetType`, `dataCollectionProcess`, `intendedUse`, `knownBias`, `hasSensitivePersonalInformation`, and `anonymizationMethodUsed`.

### Which standard should you pick?

Pick the one your existing SBOM tooling already produces and consumes. If your security team already ingests CycloneDX for vulnerability management, extend that pipeline with ML components. If your compliance or open source program office runs on SPDX, use its AI and Dataset profiles. As a rough guide, which is our opinion rather than a rule in either standard:

- CycloneDX's `modelCard` maps closely to model cards that engineering teams already write.
- SPDX's AIPackage has explicit fields for safety risk assessment, autonomy, and sensitive personal data, which map well to governance questionnaires.

Generating both from one internal source of truth is reasonable if different consumers need different formats. Maintaining two hand-written documents is not.

> **Generate it, do not hand-write it:**
>
> Produce the AI-BOM in the same pipeline step that packages the model. Hashes copied by hand drift from the real artifact within a release or two, and a stale BOM is worse than none because people trust it.

## How do you make an AI-BOM enforceable?

A BOM that nobody checks is documentation. Three controls turn it into a safeguard.

### 1. Verify hashes before deployment

Fail the deploy when the model file on disk does not match the BOM. This catches swapped checkpoints, partial downloads, and a model pulled from the wrong source.

```python
import hashlib, json, sys
from pathlib import Path

def sha256(path: Path) -> str:
    h = hashlib.sha256()
    with path.open("rb") as f:
        for chunk in iter(lambda: f.read(1 << 20), b""):
            h.update(chunk)
    return h.hexdigest()

bom = json.loads(Path("ai-bom.cdx.json").read_text())
artifacts = {"ticket-router": Path("models/ticket-router.safetensors")}

failed = False
for comp in bom["components"]:
    if comp["type"] != "machine-learning-model" or comp["name"] not in artifacts:
        continue
    expected = {h["content"] for h in comp.get("hashes", []) if h["alg"] == "SHA-256"}
    actual = sha256(artifacts[comp["name"]])
    if actual not in expected:
        print(f"hash mismatch for {comp['name']}: {actual}")
        failed = True

sys.exit(1 if failed else 0)
```

### 2. Sign the BOM

OWASP LLM03 calls for a signed inventory. Sign the BOM file in the same release step that builds the model artifact, and verify the signature before the hash check above. Otherwise an attacker who can replace the model can also replace the BOM.

### 3. Use it during incidents

The test of an AI-BOM is the next supply chain incident. When a popular package or gateway is compromised, as with [the LiteLLM 1.107.0 release](https://blog.enforster.ai/litellm-1-107-0-supply-chain-compromise), you should be able to search every BOM for the affected component and version and get a list of systems and owners. Run that query as a drill once a quarter before you need it.

> **A BOM records claims, not truth:**
>
> Fields such as training data, limitations, and license come from whoever wrote the BOM, often the model supplier. Treat them as supplier statements to verify, especially for third-party models, and record who asserted each claim.

## Where do AI-BOMs fit in compliance work?

Regulators and customers increasingly ask for documentation of the models and data behind AI systems. An AI-BOM does not by itself satisfy any specific legal requirement, and this article is not legal advice. What it does is give you a consistent, versioned technical record that technical documentation, vendor questionnaires, and risk assessments can draw on, instead of rebuilding the same answers by hand each time.

Start small: one production AI system, its deployed model with hashes, its base model, its datasets, and a CI check. Then extend the pattern to every system in your model inventory.
