ML BOM CycloneDX: Model Cards, Datasheets and AI Supply-Chain Provenance for EU AI Act Documentation (2026)
Most teams can tell you which version of requests is in their production container and cannot tell you which dataset snapshot, base model and licence produced the weights serving their customers. Software supply chains got bills of materials, signatures and admission gates over the last five years. Model supply chains mostly got a Markdown README and a download link. That gap is now a regulatory problem as well as a security one: the EU AI Act asks providers of general-purpose AI models and high-risk systems to keep structured technical documentation about training data, design choices, evaluation and risk.
This post shows how to close the gap with an ML BOM in CycloneDX as the machine-readable spine, model cards and datasheets as the human-readable layer, and signing plus provenance as the integrity layer. You will leave with a reference architecture, a sample ML-BOM document, a CI recipe, a mapping from artifacts to AI Act documentation duties, and a list of failure modes I would test for before trusting any of it. This is engineering analysis, not legal advice.
What this covers: the documentation stack (model cards, datasheets, AI bill of materials), CycloneDX and SPDX 3.0 schemas, pickle and safetensors risk, signing with Sigstore and the OpenSSF model-signing project, SLSA-style provenance, dataset lineage, EU AI Act documentation duties and dates, a CI generator, trade-offs, and a checklist.
Context and Background
Three documentation traditions converged on the same problem from different directions.
Model cards came from Mitchell et al., “Model Cards for Model Reporting” (FAT* 2019, Google). The paper proposes a short structured document accompanying a trained model with sections for model details, intended use, relevant factors, metrics, evaluation data, training data, quantitative analyses (disaggregated by group), ethical considerations, and caveats and recommendations. The key idea is disaggregated evaluation: a single accuracy number hides subgroup differences. Hugging Face turned the idea into practice: a repository’s README.md carries YAML front matter (fields such as license, language, library_name, base_model, datasets, metrics, pipeline_tag and a model-index block for evaluation results) above free-form prose.
Datasheets for datasets came from Gebru et al. (first posted to arXiv in 2018, later published in Communications of the ACM in 2021), borrowing from electronics datasheets. They are a question list organised by the dataset lifecycle: motivation, composition, collection process, preprocessing/cleaning/labelling, uses, distribution and maintenance. Where a model card describes behaviour, a datasheet describes the thing that shaped the behaviour.
Bills of materials came from the software supply-chain world. A software bill of materials (SBOM) lists components, versions, suppliers and hashes so you can answer “am I affected?” within minutes of a disclosure. If you have not read our treatment of the formats, start with SBOM and VEX with CycloneDX vs SPDX; this post assumes that vocabulary and extends it to models and datasets.
Why prose documentation alone fails
A model card in Markdown is readable and unverifiable. Nothing ties the card to the exact weights file, nothing forces the datasets: field to match what was really used, and nothing lets a deploy gate refuse a model whose card is missing a licence. Prose cards also drift: the card is written at upload, the model is fine-tuned or quantised later, and the card keeps describing the parent. A machine-readable record bound to hashes and signatures turns “documentation” into “evidence”.
Why now
Two pressures landed in the same window. First, the EU AI Act’s obligations for providers of general-purpose AI (GPAI) models have applied since 2 August 2025, and the AI Office’s enforcement powers over them begin from 2 August 2026. Second, attackers discovered that model hubs are software distribution channels: a checkpoint serialised with Python’s pickle can execute code at load time. Both pressures reward the same investment: know exactly what your model is made of and prove it has not changed.
External anchors worth bookmarking: the CycloneDX specification site (now also standardised through Ecma International as ECMA-424), the EU AI Act text on EUR-Lex, and the OpenSSF model-signing project.
Reference Architecture: Documentation, Bill of Materials and Integrity as One Pipeline
Direct answer: An AI supply-chain provenance system has three layers. A descriptive layer (model cards and datasheets) explains intent and behaviour. A declarative layer (the ML-BOM, typically CycloneDX) lists components, datasets, hashes and licences in machine-readable form. An integrity layer (signatures and provenance attestations) binds both to exact bytes and a trusted build identity, so deploy gates can verify rather than trust.

Figure 1: Reference architecture. Datasheets and the model card feed the ML-BOM, which is signed with the weights and later verified at deploy time and reused as the evidence pack for regulatory documentation.
The flow in Figure 1 reads left to right and top to bottom. Datasets and base models are inputs to a training run. The run emits weights; the pipeline then writes the model card and datasheets, and generates the ML-BOM that references all of them by identifier and hash. The BOM and the weights are signed together, pushed to a registry, and checked by the deploy gate. A parallel branch collects the same facts into the documentation pack that compliance teams need.
Layer 1: descriptive documents (what and why)
What: model cards and datasheets are human-facing narratives with a stable structure. Why: reviewers, auditors, downstream integrators and affected people need context that no hash can provide: intended use, out-of-scope use, known failure modes, subgroup performance, collection method, consent and labelling process. Where: the model card lives next to the weights (Hugging Face README.md, an internal registry description field, or a MODEL_CARD.md in the artifact bundle); the datasheet lives next to the dataset snapshot. When: produced at release, regenerated on every material change (new fine-tune, new quantisation, new evaluation). How: templates plus automation. Evaluation tables are filled by the evaluation harness, not typed by hand, because hand-typed numbers are where cards lie.
Layer 2: the ML-BOM (what exactly)
What: a structured inventory of the model, its datasets, its code and library dependencies, and its configuration. Why: queries. “Which deployed models were trained on dataset X after we discovered contamination?” is a graph query, not a text search. Where: CycloneDX JSON or XML shipped as a sibling file of the weights and stored in an SBOM/BOM management platform. When: generated by CI at the same time as the weights, never reconstructed after the fact. How: component entries of type machine-learning-model and data, with a modelCard object for model-specific metadata, and dependency relationships between them.
Layer 3: integrity and provenance (is it really that)
What: signatures over the weights and BOM, plus a provenance attestation describing the build. Why: a BOM that anyone can edit is a claim, not a control. Where: signature bundles stored beside the artifact, transparency-log entries in a public or private log, policy in the deploy gate. When: at the end of the training or packaging job, verified at every promotion boundary. How: Sigstore keyless signing or a key-based alternative, an in-toto/SLSA-style provenance statement, and policy enforcement. The container-image analogue is covered in Trivy, Grype and Cosign image scanning and signing; the model version of the same idea is almost identical, except the artifact is a directory of tensors rather than an OCI image.
Why a BOM and a card, not one or the other
The two documents answer different questions and fail differently. A card without a BOM cannot be queried or verified. A BOM without a card contains hashes and licences but no judgement about suitability. The pragmatic design is to make the card derivable from the BOM where possible (name, version, base model, datasets, metrics) and to reference the card from the BOM as external documentation for what cannot be structured. CycloneDX’s modelCard object covers much of the card’s content directly, which is why many teams treat the BOM as the source of truth and render the human card from it.
The Schemas: CycloneDX ML-BOM, SPDX 3.0 AI Profile and Hugging Face Cards
CycloneDX: component types and the modelCard
CycloneDX added support for machine learning in version 1.5 (2023), introducing the component type machine-learning-model and a modelCard object, alongside the data component type for datasets. Later releases (1.6 added cryptographic and attestation capabilities; check the current release notes for newer versions) kept these structures. The official capability page describes the AI/ML-BOM as representing datasets, models and configurations, documenting provenance and ethical considerations for datasets, and helping identify risks related to bias, data integrity and model security.
The modelCard object has three principal blocks that map neatly onto Mitchell et al.:
| modelCard block | What it holds | Card section it mirrors |
|---|---|---|
modelParameters |
approach.type (such as supervised, unsupervised, reinforcement-learning, self-supervised), task, architectureFamily, modelArchitecture, datasets (inline data components or references), inputs, outputs |
Model details, training data |
quantitativeAnalysis |
performanceMetrics (type, value, slice, confidence interval) and graphics |
Metrics, quantitative analyses |
considerations |
users, useCases, technicalLimitations, performanceTradeoffs, ethicalConsiderations, fairnessAssessments |
Intended use, ethical considerations, caveats |
Field names above follow the CycloneDX 1.5 schema as I know it; confirm exact names and enumerations against the version you pin, because they have been refined between releases.
A dataset appears as a component of type data, with a data array holding entries that describe type (dataset, source-code, configuration, other), contents, classification, governance (custodians, stewards, owners) and sensitiveData. That structure is the BOM-side echo of a datasheet’s composition and governance questions.
SPDX 3.0: AI and Dataset profiles
SPDX 3.0 (released in 2024) reorganised the specification into a core model plus profiles, including an AI profile and a Dataset profile. The AI profile’s properties include items such as autonomyType, domain, energyConsumption, hyperparameter, informationAboutApplication, informationAboutTraining, limitation, metric, modelExplainability, safetyRiskAssessment, standardCompliance, typeOfModel and useSensitivePersonalInformation. The Dataset profile adds properties such as datasetType, dataCollectionProcess, dataPreprocessing, datasetSize, intendedUse, knownBias, sensitivePersonalInformation, confidentialityLevel and datasetAvailability. I am listing these from the published profile model; treat spellings as indicative and check the spec before generating tooling.
Two details matter in practice. First, SPDX’s AI profile stays closer to the narrative of a model card (domain, limitation, safety risk assessment), while CycloneDX embeds quantitative fields (metrics with slices and confidence intervals). Second, SPDX 3.0 is a JSON-LD graph, where CycloneDX is a document tree. That makes SPDX natural for linking many elements across organisations and CycloneDX natural for dropping a single file next to a model.
CycloneDX versus SPDX for models: which to pick
| Question | CycloneDX | SPDX 3.0 |
|---|---|---|
| Model card fields built in | Yes, modelCard object |
Yes, AI profile properties |
| Dataset representation | data component with governance fields |
Dataset profile elements |
| Security-tooling ecosystem | Strong (VEX, vulnerability exchange, CBOM) | Growing; strong licence tradition |
| Document shape | JSON or XML tree | JSON-LD graph |
| Standardisation | ECMA-424 | ISO/IEC 5962:2021 covers SPDX 2.x; 3.0 is a Linux Foundation spec |
| My default | Pipelines and security teams | Licence-compliance-heavy organisations |
The choice is rarely religious. If your SBOM program already speaks CycloneDX, extend it; converters exist between the two, but conversion tends to lose profile-specific fields, so pick one as the system of record.
Hugging Face model cards as an input, not the record
The Hub’s YAML front matter is machine-readable but unvalidated by default for most free-text fields, and datasets: or base_model: values are claims by the uploader. Use them as a source when ingesting third-party models, then record in your own BOM what you actually verified. A useful rule: any field you did not generate yourself or verify by hash is marked in your BOM as “declared by supplier”, for example through a property such as provenance:declared-by, a naming convention of your own (not a standard).
Walk-through: A Sample ML-BOM and a CI Generator
A minimal CycloneDX ML-BOM
The document below is illustrative: the names, hashes, URLs and metric values are placeholders I invented to show structure, not real artifacts or measurements. It has not been validated against the CycloneDX JSON schema; validate yours with the official schema or a CLI such as cyclonedx-cli validate.
{
"bomFormat": "CycloneDX",
"specVersion": "1.5",
"serialNumber": "urn:uuid:00000000-0000-4000-8000-000000000000",
"version": 1,
"metadata": {
"timestamp": "2026-11-05T02:00:00Z",
"component": {
"type": "machine-learning-model",
"bom-ref": "model:acme-defect-detector@2.3.0",
"name": "acme-defect-detector",
"version": "2.3.0",
"licenses": [{ "license": { "id": "Apache-2.0" } }],
"hashes": [{ "alg": "SHA-256", "content": "<sha256-of-weights-manifest>" }],
"modelCard": {
"modelParameters": {
"approach": { "type": "supervised" },
"task": "image-classification",
"architectureFamily": "convolutional",
"modelArchitecture": "resnet-style, 50 layers",
"datasets": [
{ "type": "dataset", "ref": "data:line3-images@2026-09" }
],
"inputs": [{ "format": "image/png 224x224 RGB" }],
"outputs": [{ "format": "class label plus score" }]
},
"quantitativeAnalysis": {
"performanceMetrics": [
{ "type": "recall", "value": "0.00 (placeholder)", "slice": "night shift" }
]
},
"considerations": {
"useCases": ["visual inspection of machined parts"],
"technicalLimitations": ["not validated for reflective surfaces"],
"ethicalConsiderations": [
{ "name": "worker monitoring", "mitigationStrategy": "no person-level outputs" }
]
}
}
}
},
"components": [
{
"type": "data",
"bom-ref": "data:line3-images@2026-09",
"name": "line3-images",
"version": "2026-09",
"data": [{
"type": "dataset",
"classification": "internal",
"governance": { "owners": [{ "organization": { "name": "Acme Quality Engineering" } }] }
}],
"hashes": [{ "alg": "SHA-256", "content": "<sha256-of-snapshot-manifest>" }]
},
{
"type": "machine-learning-model",
"bom-ref": "model:base-vision-backbone@1.0",
"name": "base-vision-backbone",
"version": "1.0",
"licenses": [{ "license": { "name": "supplier custom licence" } }],
"externalReferences": [
{ "type": "distribution", "url": "https://example.com/models/base-vision-backbone" }
]
},
{
"type": "library",
"bom-ref": "pkg:pypi/torch@2.5.0",
"name": "torch",
"version": "2.5.0",
"purl": "pkg:pypi/torch@2.5.0"
}
],
"dependencies": [
{ "ref": "model:acme-defect-detector@2.3.0",
"dependsOn": ["data:line3-images@2026-09",
"model:base-vision-backbone@1.0",
"pkg:pypi/torch@2.5.0"] }
]
}
Several design choices are worth defending. The model hash is a hash of a manifest, not of one file, because real checkpoints are sharded directories; see the signing section. The dataset is a first-class component with its own version and hash, so a dataset takedown becomes a dependency query. The base model is a separate component with its own licence, which is where derivative-model licence obligations surface. And placeholders such as 0.00 (placeholder) are deliberate: in a real pipeline those values are written by the evaluation job, never typed.
Dataset lineage: the hard part
The BOM above records one hop: model depends on dataset snapshot. Real lineage is a graph. A dataset snapshot may be assembled from licensed vendor data, scraped web data, synthetic data generated by another model, and human labels from a labelling vendor. Each edge carries different obligations:
- Licence and consent: each source needs its licence or lawful basis recorded; scraped corpora need the opt-out and crawl-policy decisions you made.
- Transformation: deduplication, filtering, PII scrubbing and sampling change the dataset; record the tool and configuration hash.
- Synthetic derivation: if a dataset was generated by a model, that model’s licence terms and its own BOM become upstream.
- Immutability: a snapshot must be content-addressed (for example a Merkle-style manifest of shard hashes in DVC, lakeFS or Delta Lake versioning) so “dataset 2026-09” cannot silently change.
Represent the chain in CycloneDX with nested components or dependencies and pedigree ancestors/commits for derivation, and keep the heavy detail (collection method, labeller instructions) in the datasheet, referenced as an externalReferences entry of type documentation.
Generating the ML-BOM in CI
The sketch below, untested and simplified, shows the shape of a generator step: hash the weights directory into a manifest, read evaluation output, read the lockfile, and emit CycloneDX JSON. It uses only the standard library so you can see the logic; production teams usually reach for the official cyclonedx-python-lib and schema validation.
# UNTESTED sketch: emit a minimal ML-BOM after training. Adapt paths and fields.
import hashlib, json, pathlib, uuid, datetime
def sha256_file(p, chunk=1 << 20):
h = hashlib.sha256()
with open(p, "rb") as f:
while block := f.read(chunk):
h.update(block)
return h.hexdigest()
def weights_manifest(weights_dir):
entries = []
for p in sorted(pathlib.Path(weights_dir).rglob("*")):
if p.is_file():
entries.append({"path": str(p.relative_to(weights_dir)),
"sha256": sha256_file(p)})
digest = hashlib.sha256(json.dumps(entries, sort_keys=True).encode()).hexdigest()
return digest, entries
def build_bom(name, version, weights_dir, eval_json, dataset_ref):
digest, files = weights_manifest(weights_dir)
metrics = json.loads(pathlib.Path(eval_json).read_text())
return {
"bomFormat": "CycloneDX", "specVersion": "1.5",
"serialNumber": f"urn:uuid:{uuid.uuid4()}", "version": 1,
"metadata": {
"timestamp": datetime.datetime.utcnow().isoformat() + "Z",
"component": {
"type": "machine-learning-model", "name": name, "version": version,
"bom-ref": f"model:{name}@{version}",
"hashes": [{"alg": "SHA-256", "content": digest}],
"properties": [{"name": "weights:file-count", "value": str(len(files))}],
"modelCard": {
"modelParameters": {"datasets": [{"type": "dataset", "ref": dataset_ref}]},
"quantitativeAnalysis": {"performanceMetrics": [
{"type": k, "value": str(v)} for k, v in metrics.items()]},
},
},
},
}
if __name__ == "__main__":
bom = build_bom("acme-defect-detector", "2.3.0", "out/weights",
"out/eval.json", "data:line3-images@2026-09")
pathlib.Path("out/model.cdx.json").write_text(json.dumps(bom, indent=2))
Three details separate a toy from a pipeline step. Run the generator in the same job that produced the weights so the hash is of the artifact that will ship. Fail the job if evaluation output is missing rather than emitting an empty quantitativeAnalysis. And validate the output against the schema before signing, since a signed but invalid BOM is worse than none.
File Formats and Load-Time Risk: Pickle, Safetensors and GGUF
A model is a file you load into a process that often holds credentials and GPUs. Whether loading that file can run code is the first supply-chain question to answer, and it is why the BOM should record file format.

Figure 2: Load-time risk. A pickle-based checkpoint executes during deserialisation; safetensors and GGUF parse data only. Verifying signature and hash before any load closes the substitution window for both.
Figure 2 (file fig4) shows the decision. Python’s pickle format is a small stack machine; objects can specify a callable via __reduce__, and the unpickler calls it. A malicious checkpoint can therefore run arbitrary code the moment torch.load or pickle.load deserialises it. PyTorch’s .pt/.bin checkpoints historically used pickle. PyTorch documents a weights_only mode that restricts unpickling to tensors and primitive types, and recent releases changed the default of torch.load to weights_only=True (check the release notes for your version; do not rely on it being on in older environments).
Safetensors, created by Hugging Face, stores an 8-byte header length, a JSON header mapping tensor names to dtype, shape and byte offsets, and then the raw tensor bytes. Loading parses JSON and maps memory; there is no code path from file content to execution, and the format supports zero-copy loading. It is not a security panacea: it protects against code execution at load, not against poisoned weights, backdoors or malicious tokenizer and config files that sit beside it. GGUF, the llama.cpp format, is similarly a data container with metadata and tensors, though parsers in any language can have memory-safety bugs, so keep runtimes patched.
Scanning is a mitigation, not a control. Pickle scanners look for dangerous opcodes and imports, and researchers have repeatedly shown bypasses (for example by using less common callables or malformed archives that scanners skip but loaders accept). A scanner verdict belongs in the BOM as evidence, never as the gate. The gate is a policy: only load formats on an allow list, only from signed artifacts, and only inside an unprivileged sandbox when the format is risky.
Record in the BOM, via component properties: serialisation format, loader library and version, whether trust_remote_code or custom modelling code is required (a huge hidden dependency: executable Python shipped with the model), and scanner results with scanner version.
Integrity: Signing Models with Sigstore and the OpenSSF Model-Signing Project

Figure 3: Keyless signing and verification. CI proves its identity through OIDC, receives a short-lived certificate, logs the signature in a transparency log, and the deploy gate verifies identity and hashes before accepting the model.
The OpenSSF model-signing project (developed in the sigstore/model-transparency repository and announced as v1.0 in 2025 by Google, NVIDIA, HiddenLayer and others under the OpenSSF AI/ML working group) provides a CLI and Python library. Per the Sigstore blog post on v1.0, you install it with pip install model-signing, sign with model_signing sign ${MODEL_PATH}, and verify with model_signing verify ${MODEL_PATH} --signature ${SIGNATURE} --identity ${IDENTITY} --identity_provider ${OIDC_PROVIDER}. Sigstore is the recommended default, with PKI and self-signed certificate options also supported.
How keyless signing works
In the keyless flow (Figure 3), the signer obtains an OIDC token proving a workload or human identity (for GitHub Actions, the workflow identity), generates an ephemeral key pair, and sends the public key and token to Fulcio, Sigstore’s certificate authority. Fulcio issues a short-lived certificate binding the identity to the key. The signer signs, discards the private key, and records the signature and certificate in the Rekor transparency log, which returns an inclusion proof. Because the log entry proves the signature existed while the certificate was valid, verification still works after the certificate expires. The identity check is the heart of it: verification passes only if the signature came from the expected identity (for example your release workflow in your repository), not just from anyone with a valid certificate.
What gets signed
A model is not one file. The project hashes files of a model directory into a manifest and signs the manifest, so one signature covers a sharded checkpoint, tokenizer and config. The design goal, as the project describes it, is to detect any added, removed or modified file. Check the project documentation for the exact manifest and bundle format of the version you use, since the serialisation (it builds on the in-toto attestation ecosystem) has evolved.
Sign the ML-BOM and weights in the same operation or sign the BOM as an attestation about the weights’ digest. The first is simpler; the second matches the in-toto/SLSA pattern where an attestation states “subject digest X was produced by builder Y from sources Z”.
Provenance: SLSA for models
SLSA (Supply-chain Levels for Software Artifacts) defines build integrity levels and a provenance format carried in in-toto statements. For model pipelines, the useful translation is: the builder is the training or packaging job on managed infrastructure (not a notebook on a laptop), the provenance names the source repository commit, the training configuration, the dataset snapshot digest and the builder identity, and the attestation is signed by the builder, not by the developer. Be honest about the limit: SLSA provenance attests how the artifact was built, not that training data was clean or that the model behaves well. Higher assurance for training is an open research area, and I am not aware of a ratified “SLSA for ML” level definition; if your auditors ask for one, you are defining a profile, not following a standard.
EU AI Act Documentation: Mapping Artifacts to Duties
The AI Act (Regulation (EU) 2024/1689) imposes documentation duties in two places that matter to this stack. This is an engineering mapping, not legal advice; have counsel confirm how your role (provider, deployer, importer) and your system’s risk class apply.

Figure 4: Mapping. The same three artifacts feed GPAI duties under Article 53 and the technical file for high-risk systems under Article 11 and Annex IV.
General-purpose AI models: Article 53, Annexes XI and XII
Providers of GPAI models must, under Article 53(1), draw up and keep up to date technical documentation of the model, including its training and testing process and evaluation results, containing at least the elements in Annex XI, and provide it to the AI Office and national competent authorities on request. They must also make information available to downstream providers who integrate the model (the content is in Annex XII), put in place a copyright-compliance policy, and publish a sufficiently detailed summary of the content used for training, following a template from the AI Office. Models with systemic risk (presumed above a cumulative training compute of 10^25 floating-point operations, per Article 51) carry additional duties under Article 55, such as model evaluation, adversarial testing, serious-incident reporting and cybersecurity protection.
These obligations have applied since 2 August 2025. Models placed on the market before that date have a transition period to 2 August 2027. The Commission and AI Office also published a GPAI Code of Practice in 2025 and guidance on obligations; the Code is a voluntary route to demonstrate compliance. The Digital Omnibus on AI (Regulation (EU) 2026/1744, published in the Official Journal on 24 July 2026 and in force from 27 July 2026, per the reporting I checked) left the GPAI provisions as planned. Partial exemptions exist for open-source releases of the documentation duties in Article 53(1)(a) and (b), but not where the model has systemic risk, and not for the copyright policy or training summary.
Annex XI asks, in substance, for a general description of the model (tasks, architecture, parameters, modalities, licence, distribution), a description of the elements and the development process (including design specifications, training methodologies, data used with provenance and curation methods, and computational resources and energy), and for systemic-risk models additional evaluation and adversarial-testing detail. Every one of those has a natural home in a CycloneDX ML-BOM plus datasheet: licence and distribution in component fields, data provenance in data components, compute and energy in properties, evaluation in quantitativeAnalysis.
High-risk systems: Article 11 and Annex IV
For high-risk AI systems, Article 11 requires technical documentation drawn up before the system is placed on the market and kept up to date, demonstrating compliance with the Chapter III requirements. Annex IV lists the content: a general description of the system and its intended purpose; a detailed description of elements and development process (including pre-trained systems or third-party tools used, design choices, data requirements and datasheets describing training methodologies and datasets, data provenance, labelling and cleaning); information on monitoring, functioning and control; the risk management system; changes through the lifecycle; applied standards; and the post-market monitoring plan. Annex IV explicitly mentions datasheets, which is a rare case of a regulation naming an academic practice.
On dates: the original Annex III high-risk application date of 2 August 2026 was moved by the Omnibus to 2 December 2027, and the Annex I (products-embedded) date from 2 August 2027 to 2 August 2028, according to the law-firm and news summaries I checked. Article 50 transparency duties from 2 August 2026 stay, with a grace period for machine-readable marking of systems already on the market. Dates in this area have moved once and could move again via delegated acts, harmonised standards timelines and national implementation, so treat any date in a blog post, including this one, as a pointer to verify on EUR-Lex.
What the BOM does and does not give you
An ML-BOM does not make you compliant. It gives you three things regulators and auditors care about: completeness (a documented inventory of components and datasets), traceability (hash-pinned identifiers for exactly what shipped), and change history (versioned BOMs per release, matching the lifecycle-changes expectation). It does not substitute for the risk management system, human-oversight design, data-governance reasoning, bias analysis or conformity assessment. Think of the BOM as the index and evidence store for the technical file, and write the narrative parts of Annex IV as documents that cite BOM identifiers.
A practical mapping table:
| Duty or Annex item | Artifact that carries it | Automated or authored |
|---|---|---|
| General description, intended purpose | Model card, considerations.useCases |
Authored, versioned |
| Architecture, parameters, licence | BOM modelCard.modelParameters, licenses |
Generated |
| Training data provenance and curation | Datasheet plus BOM data components |
Generated hashes, authored narrative |
| Third-party and pre-trained components | BOM components and dependencies |
Generated |
| Compute and energy | BOM properties or SPDX energyConsumption |
Generated from job metering |
| Evaluation and metrics | quantitativeAnalysis from harness |
Generated |
| Changes over lifecycle | Series of signed BOMs in registry | Generated |
| Risk management, oversight, post-market monitoring | Separate controlled documents | Authored |
| Public training-content summary (GPAI) | Derived from datasheets, AI Office template | Authored from generated inputs |
Deployment Gates and Operating the Pipeline
The deploy-time policy
A BOM nobody queries is a compliance ornament. The deploy gate should evaluate policy over the signed BOM before a model reaches a serving environment. Representative rules:
- The signature verifies against the expected CI identity and the manifest hash matches the files on disk.
- File format is on the allow list (safetensors, GGUF), or the model is routed to a sandbox.
- Every dataset component has a licence and an owner; none is on a revoked list.
- Every base model has an approved licence and a recorded source.
- Required model card sections are non-empty; evaluation metrics exist for each declared slice.
trust_remote_codeis false, or the code is itself pinned by commit hash and reviewed.
Policy engines such as OPA/Rego or Kyverno-style admission controllers can evaluate these over the BOM JSON; the registry or an admission webhook enforces the result. The same pattern works for image gates in Kubernetes.
Incident response: the query you will run at 2 a.m.
When a dataset is found to contain disallowed content, a base model is recalled, or a library CVE hits a serving stack, the BOM store should answer “which production models depend on X, transitively, and since when?” in seconds. This is the same reasoning as SBOM plus VEX for software; the difference is that for models the remediation is usually retraining or rollback rather than a patch, so knowing the blast radius earlier has more value, not less. Keep rollback candidates, with their signed BOMs, in the registry so a safe previous version can be redeployed.
Capacity and cost
BOM generation is cheap: hashing a multi-gigabyte checkpoint is limited by disk throughput, and a 100 GB weights directory read at, say, 1 GB per second takes on the order of minutes (illustrative figures, not a benchmark). Parallelise per-shard hashing and cache hashes of unchanged shards. Signing is milliseconds once the manifest exists. Storage for BOMs and signatures is negligible next to the weights. The real cost is organisational: assigning owners to datasheets and keeping evaluation harnesses wired to the BOM generator.
Trade-offs, Gotchas, and What Goes Wrong
Declared versus verified. The most common failure is a beautifully structured BOM whose contents are unverified claims. If the datasets field is typed by a researcher, an auditor will treat it as an assertion. Generate it from the data loader’s actual manifests, log dataset digests at training start, and compare to the BOM at the end; mismatch fails the build.
Hash of the wrong thing. Hashing a single file when the model is a sharded directory, or hashing before quantisation or conversion when production serves the converted file, creates a signature that verifies nothing relevant. Every format conversion, quantisation and adapter merge is a new artifact that needs its own BOM entry, with the parent referenced through pedigree.
Fine-tunes and adapters. A LoRA adapter on a base model yields a composite whose behaviour and licence depend on both. Record both components and the merge method. Licences with use restrictions travel with derivatives; the BOM makes that visible but does not decide whether you comply.
Privacy and secrecy. Dataset descriptions can leak competitive details or personal data. Separate internal BOMs from external ones: publish a redacted BOM or a summary to customers and regulators, keep the full graph internal, and sign both. For GPAI training-content summaries, regulators expect a sufficiently detailed but not trade-secret-exposing description; the exact threshold is guided by the AI Office template, not by this post.
Schema churn. CycloneDX and SPDX AI fields are evolving. Pin the spec version, validate in CI, and write a migration note when upgrading. Tools differ in which optional fields they emit, so cross-tool comparisons of “completeness” mislead.
Signing is not safety. A signed model can be malicious if the signer was. Keyless identity binding shifts trust to “this workflow in this repo”, so protect the workflow: branch protection, reviewed changes to training code, locked-down OIDC claims. Signing also does nothing about data poisoning or backdoors introduced in training; those need evaluation and red-team evidence recorded in the card.
Compliance theatre. Teams sometimes generate a thick pack of documents to meet a deadline and never use them. The test of a good system is operational: has the BOM answered a real question in the last quarter? If not, fix the queries before adding fields.
Model cards that overpromise. Disaggregated metrics on small slices have wide confidence intervals; the CycloneDX metric object supports confidence intervals for exactly this reason. Report them, or the slice table creates false precision.
Practical Recommendations
Start small and wire it into the build rather than writing a policy first. Pick one system of record (CycloneDX if your security team already runs SBOM tooling, SPDX 3.0 if licence compliance dominates), pin its version, and generate the BOM in the training or packaging job. Make datasheets a required input for any dataset snapshot, with a template covering the Gebru et al. categories and an owner field. Make the model card partly generated: metrics from the harness, lineage from the BOM, narrative from the model owner.
Next add integrity: sign weights and BOM with a keyless identity from CI, verify at promotion boundaries, and block non-allow-listed file formats. Then add policy checks in the registry. Only after those work should you invest in public-facing outputs such as training-content summaries.
For regulatory readiness, map your models to roles and risk classes with counsel, then use the mapping table above to see which fields you still lack. Keep a change log for dates and guidance, because the EU timeline has already moved.
A short checklist:
- [ ] BOM generated in the same job that produces the weights, validated against the schema
- [ ] Weights manifest hashes cover every file in the model directory
- [ ] Dataset snapshots are content-addressed and referenced by digest
- [ ] Model card metrics are generated, with slices and confidence intervals
- [ ] File format allow list enforced;
trust_remote_codeblocked by default - [ ] Keyless signature with an expected-identity check at deploy
- [ ] Base model and dataset licences recorded and reviewed
- [ ] Reverse dependency query tested against a drill scenario
- [ ] Redacted external BOM variant defined
- [ ] Legal review of AI Act role, risk class and dates
Frequently Asked Questions
What is an ML-BOM in CycloneDX?
An ML-BOM is a CycloneDX bill of materials that describes machine-learning assets: models (component type machine-learning-model, with a modelCard object for parameters, metrics and considerations), datasets (component type data), and their dependencies. It was introduced with CycloneDX 1.5 and lets you query, sign and exchange model composition the same way you handle software SBOMs, including licences, hashes and provenance.
How is a model card different from a datasheet?
A model card documents a trained model: intended use, evaluation across groups, limitations and ethical considerations (Mitchell et al., 2019). A datasheet documents a dataset: why it was created, what it contains, how it was collected and cleaned, recommended uses and maintenance (Gebru et al.). Cards describe behaviour; datasheets describe the inputs that shaped it. Mature programmes maintain both and link them from the ML-BOM.
Does an ML-BOM satisfy EU AI Act documentation requirements?
Not by itself. It supplies much of the structured evidence for Annex XI (GPAI models) and Annex IV (high-risk systems): components, datasets, licences, evaluation and change history. Duties such as risk management, human oversight, data-governance reasoning, the training-content summary and conformity assessment need authored documents and processes. Treat the BOM as the index and evidence store, and confirm role and risk class with legal counsel.
Are safetensors files safe to load?
Safetensors removes the load-time code execution that pickle allows, because the format is a JSON header plus raw tensor bytes. It does not protect against poisoned or backdoored weights, malicious tokenizer or configuration code, or parser bugs. Combine format allow-listing with signature verification, sandboxed loading where possible and evaluation for anomalous behaviour. Safer format reduces one class of risk, not all.
How do I sign a model so deploy gates can verify it?
Use the OpenSSF model-signing tooling: hash all files in the model directory into a manifest, sign it with a Sigstore keyless identity from your CI workflow, and publish the signature bundle beside the weights. At deploy time run the verify command with the expected identity and OIDC issuer, then compare hashes to the files on disk. Reject on any mismatch or unexpected identity.
Which should I use, CycloneDX or SPDX 3.0, for AI bills of materials?
Both carry model and dataset metadata. CycloneDX embeds a modelCard with quantitative metrics and fits security-tooling pipelines and single-file shipping. SPDX 3.0 has AI and Dataset profiles in a JSON-LD graph and suits organisations with strong licence-compliance practices. Choose the one your existing SBOM tooling already supports and pin its version; converting between them can lose profile-specific fields.
Further Reading
- SBOM and VEX: CycloneDX vs SPDX for software supply-chain vulnerability triage for the base vocabulary this post extends.
- Trivy, Grype and Cosign: container image scanning and signing pipelines for the image-side equivalent of model signing.
- CycloneDX ML-BOM capability page and the OWASP CycloneDX Authoritative Guide to AI/ML-BOM.
- Sigstore blog: practical model signing with model-transparency v1.0.
- Regulation (EU) 2024/1689 on EUR-Lex for Articles 11, 53, 55 and Annexes IV, XI, XII.
Non-legal-advice note: this article is technical analysis of documentation architecture and is not legal advice.
By Riju — about
