Skip to content

Docs,Hand the report to other tools

CycloneDX ML-BOM

docs/cyclonedx.md in the Socair repository at v0.1.0-rc.6 (commit e3d1bf2a6cb2), the release Get started installs. The repository is the source of truth: view it on GitHub.

Socair renders every report as a CycloneDX 1.6 machine-learning BOM (internal/render/cyclonedx), and signs it on request. The BOM is derived from the report and says nothing the report does not: a field the report does not know is left out, never filled with a guess.

socair scan model.gguf --format cyclonedx > bom.json        # scan, print the BOM
socair render model.gguf --cyclonedx bom.json               # scan, write the BOM
socair sign --key op.key --bom --attestation r.dsse.json    # sign the BOM of an attestation
socair verify --bom r.cdx.dsse.json --trusted op.pub --artifact model.gguf --cyclonedx bom.json

POST /api/render with "format": "cyclonedx" returns the same BOM for a report document (api.md). A header-only report names no exact bytes and has no BOM.

What the BOM says

The subject (metadata.component) is the model, a machine-learning-model. Its components are the files the report attested: the one file of a single-file report, or every file of a model directory, each with its SHA-256, size, and role. The model's hash is the file's SHA-256, or for a directory the manifest digest over its files (internal/modeldir).

Field Holds Left out when
metadata.authors The report's issuer: whoever signed it. An unsigned report's BOM says Unsigned: no issuer has signed this report. Never.
metadata.tools socair and its version, from the report. The version, when the report records none.
metadata.lifecycles post-build: Socair reads a built artifact's bytes. Never.
version, purl The pinned commit, and pkg:huggingface/<account>/<repo>@<commit> with the commit in lower case, per the Package URL huggingface type. The report records no https://huggingface.co/<account>/<repo> origin, or its commit is not a 40 or 64 hex commit id (a branch or tag). A mirror's URL does not show it serves the hub's repositories, so it gets neither.
supplier The hub account the repository belongs to. It supplied the bytes; it may not have trained the model. No huggingface.co origin.
manufacturer Never filled: the report does not know who trained the model. Always.
publisher The publisher as the report records it (a provenance manifest, or a verified publisher signature's signer). The report records none.
licenses The license the artifact states, declared, by SPDX id when the SPDX list bundled with the CycloneDX 1.6 schema has it, else by name; socair:license_id keeps the report's catalogue id. The report identifies no license, or its statements disagree. A disagreement is stated in socair:license_disagreement, never resolved.
socair:license_source Each statement the scan read (model card, LICENSE file, GGUF general.license, a declared base model's publisher license), and what it named. The report read none.
pedigree.ancestors The base models the artifact declares, with where each is declared. The pedigree notes say they are claims, not verified lineage. The artifact declares none.
modelCard modelParameters.modelArchitecture, and as properties the declared and observed quantization and the hashes of what prepares the model's input: socair:input:tokenizer_sha256 (for a GGUF, of the vocabulary in id order; for a directory, of tokenizer.json) and socair:input:chat_template_sha256. Each value the report lacks. modelParameters.inputs and outputs take only a free-text format, which the report does not know, so they are left out.
externalReferences The repository (vcs), the report (static-analysis-report, by document id and, once signed, document hash), and once signed the attestation (attestation). No repository; an unsigned report has no attestation reference.
dependencies The model depends on each attested file. Never.
compositions The completeness of the model's files: complete for a model directory, whose report lists every file; incomplete for one part of a split file, or for a directory scan that excluded an entry other than tool metadata (a symlinked directory, an unreadable or non-regular file); unknown for a single file, which may need files the report never saw. What the model needs beyond its files is always unknown. Never.
Properties Every check row with its status (socair:check:<row>), the promotion state, assurance level, check-set version, any acceptance, and the fixed "does not certify" sentence. A NOT_TESTED row reads NOT_TESTED, never as an absence of findings. Never.

The serial number is derived from the document, and the timestamp is the report's issue time, so the BOM is byte-stable for a fixed report. A signed or re-issued report has a new document hash, and so a new serial number. internal/render/cyclonedx/testdata/bom.cdx.json is the BOM of testdata/report.json; go test ./internal/render/cyclonedx -run TestGoldenBOM -update rewrites it.

Framework mappings

Each check row's framework entries (maps_to, for example MITRE ATLAS AML.T0051 or OWASP LLM01) travel as CycloneDX Attestations (CDXA), the part of CycloneDX 1.6 made for relating claims to the requirements of a standard. It is the format's own place for them, where a property would be a name only Socair defines; CycloneDX publishes third-party standards such as NIST SSDF and OWASP ASVS in the same form (official-3rd-party-standards).

  • definitions.standards: one standard per framework edition, named and versioned as the rows record them (maps_to[].framework and .version), whose requirements are the entries the checks address, by identifier and name. Everything is read from the report's rows, never from Socair's current mapping table, so an older report keeps the editions it was mapped against; a row that records no version gives a standard with none.
  • declarations.claims: one claim per check row that maps to anything, about the model, stating the row's status and what the check looks for.
  • declarations.attestations: one attestation mapping each requirement to the claims of the rows that address it.

No conformance or confidence is given. A mapping says what a check inspects, not that the model meets a requirement, and each claim carries its row's status, so a NOT_TESTED row is mapped as not tested. A consumer that does not read CDXA ignores both sections. control-mapping.md says what each row is evidence for.

G7 SBOM for AI minimum elements

Software Bill of Materials for AI – Minimum Elements, by the G7 Cybersecurity Working Group, published on 12 May 2026 jointly by BSI, ACN, ANSSI, CSE, CISA, NCSC-UK, and Japan's NCO with the European Commission, lists elements in seven clusters. Its elements "are not mandatory; do not create requirements, standards, or legislation" (p. 1). Socair describes one model artifact, not the AI system that serves it, so it fills the Metadata and Models clusters as far as the report knows them; a system's BOM can include this one. control-mapping.md says which report fields are evidence for the Models elements; this table says where the BOM carries them.

Cluster Element In the BOM
Metadata SBOM author metadata.authors: the report's issuer.
SBOM version version (1) and serialNumber, an RFC 4122 UUID.
SBOM data format name, version bomFormat CycloneDX, specVersion 1.6.
SBOM author signature socair sign --bom: an Ed25519 signature (EdDSA, approved in NIST FIPS 186-5) in a DSSE envelope; see below.
SBOM tool name, version metadata.tools: socair and its version.
SBOM generation context metadata.lifecycles: post-build.
SBOM timestamp metadata.timestamp: the report's issue time, RFC 3339.
SBOM dependency relationship dependencies (the model includes its files), compositions, and pedigree.ancestors for declared base models (derived from).
System level properties All nine Not filled: they describe an AI system, not a model artifact.
Models Model name metadata.component.name.
Model identifier The SHA-256 hash, and purl at a pinned hub commit. CPE, OmniBOR, and SWHID identifiers are not computed.
Model version version: the pinned commit. Absent when the report has none; CycloneDX has no "unknown" value, so the absence is the statement.
Model timestamp Not filled: the report does not record when the model was last changed.
Model producer Partly: publisher as the report records it, and supplier, the hub account. manufacturer is not filled: neither is known to have trained the model.
Model description Lineage only: declared base models in pedigree, marked as claims. No capabilities or limitations; the report has none.
Model hash value, algorithm hashes: SHA-256.
Model properties modelParameters.modelArchitecture, the quantization labels, and the size. The parameter count is not in the report.
Model input-output properties The tokenizer and chat-template hashes in modelCard, as input preprocessing. Modality, data types, and context length are not in the report.
Model training properties Not filled.
Model license licenses, declared, with each statement read in socair:license_source. Whether the model is open weight is not stated.
Model external references externalReferences: the repository, the report, the attestation.
Datasets properties All ten Not filled: the report holds nothing about training data.
Infrastructure Software, hardware Not filled: out of scope for a model artifact.
Security properties Security controls, compliance, policy, vulnerability referencing Not filled. The framework entries the checks address are carried as CDXA claims mapped to requirements, which state no compliance.
Key performance indicators Security metrics The check results, promotion state, and assurance level, as properties: what the checks found within their scope, not a robustness metric. Tier 2 measurements are not in the BOM.
Operational performance KPIs Not filled.

Signing a BOM

socair sign --key op.key --bom --attestation r.dsse.json [--out r.cdx.dsse.json] [--cyclonedx bom.json]
socair verify --bom r.cdx.dsse.json (--trusted op.pub | --store <path>) [--artifact <path>] [--cyclonedx bom.json]

sign --bom verifies the attestation against the signing key itself, renders the BOM of the signed report, and signs it: an in-toto Statement v1 with predicate type https://cyclonedx.org/bom (the type the in-toto attestation framework and actions/attest-sbom use), the BOM as the predicate, and the attested artifact's digest as the subject, in a DSSE envelope signed with the same Ed25519 key. Only the key that signed the attestation can sign its BOM, so the BOM's author is always its signer. --cyclonedx also writes the BOM itself.

verify --bom checks the signature against your trusted keys, that the statement is a CycloneDX statement with one subject, and that the BOM's model carries the subject's SHA-256, then confirms the BOM's author against the name your trust list gives the key, as socair verify does for an issuer. Any change to the BOM breaks the signature. --artifact checks that a file or model directory is the artifact the BOM describes; --cyclonedx writes the verified BOM.

A signed BOM is an inventory, not an attestation. It is a different statement with a different predicate type: socair verify without --bom refuses it, and airlock promote never accepts it as a ticket (a test holds this). The attestation is the only thing that authorizes promotion.

The envelope is the same DSSE format as Socair's attestations, signed with an operator key, not a Sigstore bundle; gh attestation verify does not read it.

Why CycloneDX 1.6

CycloneDX 1.7 (October 2025) adds citations, which could attribute the license to the statement it was read from by JSON pointer, plus TLP distribution constraints, patents, and isExternal. It leaves the model card's structure as it is in 1.6. The tests could validate 1.7 offline as they do 1.6, from committed schemas. What decides it is what reads the BOM: Dependency-Track added 1.7 in 5.1.0 (2026-08-27) and backported it to 4.14.4 (2026-09-14), so earlier releases still in service do not list it as supported; cyclonedx-cli added it in v0.30.0 (2026-02-10), sbom-utility in v0.19.0 (2026-05-18), and the OWASP AIBOM Generator writes a 1.6 and a 1.7 BOM side by side. Socair stays on 1.6 until 1.7 ingestion is common, and then moves the license sources to citations.

OWASP AIBOM completeness scoring

The OWASP GenAI Security Project's AIBOM Generator scores completeness from a field registry. It treats as critical bomFormat, specVersion, serialNumber, version, the model's name, type, and version, the supplier's name, and modelCard.modelParameters.task. Socair always fills the first six; it fills the model's version and supplier only from a huggingface.co origin (the version only at a pinned commit), and never the task, which the report does not know. The scorer reads the model from components[0]; Socair describes the model in metadata.component, the BOM's subject as CycloneDX defines it, and lists its files as the components, so that scorer reads the first file.

Sources

All the docs