CycloneDX is AIROM’s interchange format for AI inventory. The writer emits an ML-BOM: machine-learning-model components carrying modelCard, with every claim backed by evidence.occurrences[] and evidence.identity[].
Emit a CycloneDX ML-BOM

Spec version

Target CycloneDX 1.7
The modelCard shape is identical in both versions, so only the declared specVersion and $schema differ. The 1.6→1.7 delta is four externalReference types AIROM never emits. Pick 1.6 unless a consumer demands otherwise.

Document shape

bom.json (abridged)
The scan root is emitted once, as metadata.component, and never duplicated in components[]. Scan provenance travels in metadata.properties under the airom: prefix (airom:source.type, airom:source.target, airom:source.digest, airom:source.git.remote, airom:source.git.commit, airom:source.git.dirty, airom:source.k8s.context, airom:tool.commit).
airom:unknowns is always present, even at 0. Honesty over silence: the count of files AIROM could not fully process is part of the BOM, not a log line you might miss.

bom-ref identity

Every component’s bom-ref is its AIROM component ID (airom: followed by a stable hex digest). It is the anchor for every intra-document reference: dependencies[].ref, modelCard.modelParameters.datasets[].ref, and the airom:rel.* property values all point at a bom-ref.

Kind → component type

The 13 AIROM component kinds map onto CycloneDX’s coarser component.type. The exact kind always survives in the airom:kind property, so nothing is lost. Components typed data also carry a data[] facet: datasetdata[].type: "dataset", ai-config"configuration", prompt"other".

modelCard

A modelCard is emitted for machine-learning-model components — but only when there is something to put in it. If AIROM knows no task, no architecture, no model card, and no trained-on edge, the key is absent rather than empty.
A hosted model with a modelCard
trained-on edges become modelCard.modelParameters.datasets[].ref, pointing at the dataset component’s bom-ref.
airom:param.temperature = "0.2 @ src/rag.py:42". A bound generation parameter carries its call site inline, so the provenance of a decoding setting survives into the BOM. An unbound param carries just its value.
Model facts with no native CycloneDX slot become namespaced properties: airom:model.paramCount, airom:model.quantization, airom:model.contextLength, airom:model.format, airom:model.baseModel, and airom:pickle.risk / airom:pickle.imports for static pickle-walk findings.

vulnerabilities[] — the risk overlay

Artifact risks — a poisoned pickle, a Keras Lambda layer, an unsafe-load call site — project into the top-level vulnerabilities[] array. Each is a non-CVE id (e.g. AIROM-RISK-PICKLE-IMPORT) with source.name: airom, ratings[].method: other (AIROM claims no CVSS), and affects[].ref pointing at the affected component’s bom-ref.
a risk in vulnerabilities[]

definitions / declarations — the compliance overlay

When you pass --compliance, the framework mapping projects into CycloneDX’s native attestation model: definitions.standards[] declares the framework and its requirements[] (controls), and declarations records AIROM as a first-party assessor with one claim + attestation.map[] entry per control.
a control's verdict in declarations
A manual control emits its claim and attestation.map entry with no conformance.score — AIROM never asserts a figure it cannot back with evidence.

Evidence

Every component that has evidence carries it natively.
evidence on a component
occurrences[] is every sighting: location (required), plus line, symbol and additionalContext (the snippet) when known. A whole-file sighting omits line entirely rather than claiming line 0. identity[] is what AIROM concluded and why. Note that identity entries can disagree — a low-confidence competing concludedValue sits alongside the winning one rather than being silently discarded. AIROM’s 8 detection methods map to CycloneDX evidence techniques by identical string, with one exception: config-analysis has no enum value, so it degrades to technique: "other" with value: "config-analysis" as a recovery marker.

dependencies[] — what AIROM will and won’t claim

The writer maps depends-on edges to dependencies[] {ref, dependsOn}, and root edges naturally reference the metadata.component bom-ref.
In practice, no rule pack that ships with AIROM declares a depends-on edge, so a stock scan produces no dependencies[] key at all. This is deliberate, not an oversight: AIROM does not fabricate depends-on edges it cannot substantiate from evidence. An absent dependencies[] means “AIROM did not prove any dependency,” never “this component has no dependencies.”
If a custom pack supplied via --rules declares depends-on edges, the writer emits them. Every other relationship type is lossy in CycloneDX and travels as a property on the edge’s source component: The property value is "<to-bom-ref>@<confidence>", e.g. airom:rel.uses = "airom:1a2b3c4d5e6f7788@0.9". Type, endpoints and confidence round-trip; the edge’s own evidence occurrences do not survive. Use json or yaml if you need them.

purl and hashes

Local weight files get real purls and SHA-256 hashes — their identity is their bytes:
Hosted API models get no purl, deliberately. Minting pkg:generic/openai/gpt-4.1 would misuse the spec — pkg:generic is reserved for bare files — and would pollute purl-keyed consumers such as Dependency-Track. A hosted model’s identity travels as bom-ref + airom:model.provider + airom:model.id. This will be revisited if purl standardizes an AI type.
Only SHA-256 digests reach the BOM. AIROM’s internal XXH3 hashes are a cache detail and are not in the CycloneDX algorithm enum, so they are not emitted.

Determinism

Components arrive sorted by ID and stay that way. Properties are sorted by name then value. dependencies[] refs and their dependsOn lists are sorted. Two scans of an unchanged tree produce byte-identical BOMs, so a checked-in bom.json diffs cleanly.

Reference

Full field mapping

Every internal field and its route into each format is specified in docs/mapping.md in the repository.

SARIF output

For code-scanning UIs rather than BOM exchange.