Generating an AI-BOM¶
See also: the canonical design reference at docs/PROJECT_REFERENCE_MANIFEST.md.
Manifest — Project Reference & Design Document¶
AI-BOM generator for AI systems. Inventory everything.
Part of the Bulwark suite (see BULWARK.md). Manifest reuses bulwark-core (findings, severity,
rules, report, AI) and composes Airlock (parts) and Warden (assembly) as risk backends. This
document is the source of truth for Manifest's design.
Status (v0.1, shipped): all discoverers (including notebooks), provenance/license/OSV resolution,
B1–B9, the Airlock/Warden risk bridges (--scan-risk), CycloneDX and SPDX output, NIST AI RMF
and EU AI Act mapping (--govern), the risk register, and BOM diff (manifest diff) are built and
tested green. Roadmap markers below are retained as history; every listed capability is complete.
1. Thesis & positioning¶
Governance starts with visibility. A modern AI system is an unlabeled pile of models, datasets, MCP servers, prompt templates, tools, and libraries — usually with no single record of what's inside, where it came from, or whether it's safe. Manifest produces that record: a standardized AI bill-of- materials with provenance, licenses, versions, and risk, so the system can be governed, audited, and trusted.
It is the governance/GRC layer of Bulwark and the aggregator: Manifest = the inventory plus Airlock's findings on the parts plus Warden's findings on the assembly, unified into one artifact. That composition is the suite's story made literal.
1.1 Prior art / positioning¶
- SBOM (software bill-of-materials) is mature; CycloneDX and SPDX now have ML/AI and dataset extensions. Manifest rides these standards rather than inventing a schema.
- AI-BOM as a practice is emerging (regulatory pressure: NIST AI RMF, EU AI Act). Tooling that actually generates an AI-BOM from a real repo — with integrated risk from dedicated scanners — is scarce.
- Manifest's edge: automatic discovery across all AI component types, standards-based output (CycloneDX), integrated risk from Airlock + Warden, and a control-framework mapping for governance. This is the Module-18 GRC-meets-architecture differentiator in tool form.
2. What Manifest discovers¶
Point it at a project directory (a repo). Discoverers statically inspect code and config — never execute it — and each emits components:
- Models (
discover/models.py):from_pretrained("org/name"), model IDs in code/config, local weight files (.safetensors/.bin/.gguf/…), framework model refs. - Datasets (
discover/datasets.py):load_dataset(...), data files, HF dataset refs, data dirs. - MCP servers (
discover/mcp.py):.mcp.json/ client configs → server + tool components. - Prompt templates (
discover/prompts.py): prompt files, template strings, system prompts. - Tools/functions (
discover/tools.py): functions/tools exposed to an agent. - Dependencies (
discover/deps.py):requirements.txt,pyproject.toml,package.json— flag AI/ML libs specifically, but inventory all. - Notebooks (
discover/notebooks.py): parses.ipynbcell source and extractsfrom_pretrained/load_datasetrefs and!pip installpackages → MODEL/DATASET/LIBRARY components, withlocationrecorded aspath#cellNso a finding points at the exact cell. - Agents (
discover/agents.py): statically detects agent-assembly configs (generic agent manifest, OpenAI Assistants, CrewAI) →AGENTcomponents whose metadata captures autonomy, model, and wired tool names. This makes the BOM reflect assemblies, not just parts; the CycloneDX emitter surfaces these asbulwark:agent:*properties (aligned with the emerging CycloneDX Agent BOM, spec issue #895).
Discoverers are additive and independent; the registry runs all and merges into one AIBOM. Adding a component type never touches existing ones.
3. Governance taxonomy (B-codes)¶
Manifest's findings are governance/inventory findings (distinct from Airlock's M*/P* and Warden's
A*, which it imports as B5).
B1 — Undeclared/unpinned component. (MEDIUM) Model/dataset/dep used without a pinned version/hash (non-reproducible, drift risk). Ref: SLSA, supply-chain hygiene.
B2 — Missing provenance. (MEDIUM) Component with no verifiable source/author/hash. Ref: NIST AI RMF (Map/Measure), SLSA.
B3 — License risk. (MEDIUM–HIGH) Restrictive/incompatible/unknown license on a model, dataset,
or dependency (e.g. non-commercial model in a commercial product, copyleft conflict, missing license).
Detect: resolve/licenses.py + compatibility matrix. Ref: license compliance.
B4 — Known-vulnerable dependency. (severity from advisory) A dependency with a known CVE via
OSV. Detect: resolve/vulns.py. Ref: OSV, CWE by advisory.
B5 — High-risk component (from Airlock/Warden). (inherited severity) A model/MCP server Airlock
flagged, or an assembly Warden flagged, surfaced inline on the component. Detect: risk/bridges.py
with --scan-risk. An "assembly" is any mcp-server or agent component with a location — the
bridge previously considered only MCP configs, so a CrewAI crew or an OpenAI Assistants config was
inventoried and then never audited. Severity is inherited from the worst imported finding, which is
what makes --fail-on transitive through the composition; the roll-up is emitted only at HIGH or
above, since M2/M4/M7 fire on essentially every real model and a roll-up on every component would be
wallpaper. Ref: OWASP LLM03:2025 / LLM06:2025.
B6 — Dataset governance gap. (MEDIUM) Dataset without documented source/license/consent (data-provenance / privacy relevance). Ref: NIST AI RMF, data governance.
B7 — Secret/credential reference exposure. (HIGH–CRITICAL) Hardcoded or broadly-scoped secrets referenced in the project. Detect: reuse core secret rules. Ref: CWE-798.
B8 — Unversioned/untracked prompt template. (LOW–MEDIUM) System/prompt templates not under version control or lacking identifiers (change-management gap). Ref: governance best practice.
B9 — Compliance control gap. (advisory) Mapped gaps against a control framework (see §6). Ref: NIST AI RMF; optionally EU AI Act articles.
4. AIBOM IR (Manifest-specific data model)¶
Findings/severity/report/AI come from bulwark_core. Manifest adds the BOM IR, which maps cleanly to
CycloneDX:
class ComponentType(str, Enum):
MODEL="model"; DATASET="dataset"; MCP_SERVER="mcp-server"; PROMPT="prompt"
TOOL="tool"; LIBRARY="library"; FRAMEWORK="framework"; AGENT="agent"
class License(BaseModel):
id: str | None = None # SPDX id if known
name: str | None = None
risk: Literal["ok","restricted","copyleft","unknown"] = "unknown"
class Provenance(BaseModel):
source: str | None = None # hf repo, pypi, url, local
author: str | None = None
version: str | None = None
hash: str | None = None
pinned: bool = False
class Component(BaseModel):
key: str # stable id (type + name + version)
type: ComponentType
name: str
provenance: Provenance = Provenance()
license: License = License()
location: str | None = None # where in the repo it was found
findings: list[str] = [] # Finding ids attached to this component (B*/M*/P*/A*)
metadata: dict = {}
class Relationship(BaseModel):
src: str; rel: str; dst: str # e.g. agent "uses" model; agent "wires" mcp-server
class AIBOM(BaseModel):
project: str
generated_at: datetime
components: list[Component]
relationships: list[Relationship] = []
bulwark_version: str
bom/cyclonedx.py maps AIBOM → CycloneDX JSON, using ML/AI component types and attaching findings as
CycloneDX vulnerabilities/properties. Findings themselves remain bulwark_core.Finding objects in the
ScanResult; the BOM references them by id.
Two identity details matter. AIBOM.add merges field-wise by component key — two discoverers
finding the same model (a .py reference and a notebook cell) is the normal case, so the merge fills
missing provenance, prefers a concrete licence over unknown, and unions findings rather than
first-writer-wins. And models carry a purl — pkg:huggingface/org/name@revision — so they are
identifiable across tools and advisory feeds, not only libraries.
5. Resolution (provenance, licenses, vulns)¶
- provenance.py: resolve source/author/version/hash per component (HF Hub metadata for models/
datasets; PyPI/registry metadata for deps; file hashes for local artifacts). Sets
pinned. - licenses.py: detect SPDX license; classify risk; run a small compatibility check against the project's declared license (flag conflicts → B3).
- vulns.py: query OSV for dependency components (batched, cached,
--offlineskips network) → B4 with advisory severity.
6. Governance layer¶
govern/controls.py maps discovered gaps/findings to two control frameworks: NIST AI RMF
(assess() over the Govern/Map/Measure/Manage functions) and the EU AI Act
(assess_eu_ai_act(), mapping finding categories to Articles 10/11/12/13/14/15). Both produce a
coverage summary and per-control status, are emitted under meta["governance"], and are surfaced by
--govern. Both are advisory (B9) and clearly labeled as guidance, not certification. govern/report.py
emits a governance summary (with a dedicated "EU AI Act mapping (advisory)" section) + a risk
register (component → category → risk → severity → action → owner → status) — exactly the artifact
a security/GRC reviewer wants. owner and status ship as template columns: Manifest cannot infer an
owner from a repository, and a register without them is a list of complaints rather than something a
team can track.
Control status is three-state — ok (nothing mapped), advisory (worst mapped severity is
LOW/MEDIUM), gap (HIGH/CRITICAL) — driven by the worst severity mapped to that control. A binary
ok/gap flipped on a single LOW advisory, so a basically healthy project reported "gap" against all
four NIST functions and the field carried no information. Severity already drives the fail threshold,
the policy profiles, and the SARIF level; it drives this too.
This is the intersection Mohit is positioning for: architecture + GRC. Keep the mapping transparent and sourced (cite the framework), never overclaim compliance.
7. Reports & output¶
- CycloneDX 1.5 JSON (
bom/cyclonedx.py) — primary machine format (interoperable ML-BOM). - SPDX 2.3 JSON (
bom/spdx.py,--format spdx) — for pipelines standardized on SPDX; sanitizedSPDXRef-*ids, packages, andDESCRIBESrelationships. - VEX (
bom/vex.py,--format vex) — a CycloneDX VEX document seeded from the B4 findings: each vulnerable dependency becomes avulnerabilities[]entry with the advisory id/source, a rating, the affected component ref, and ananalysis.state = exploitable— a reviewer flips any entry tonot_affectedwith a justification without re-scanning. - JSON — the full
ScanResult+AIBOM. - HTML/Markdown — human BOM with risk badges per component + governance summary.
- SARIF — governance findings for CI (ruleId = B-code; imported M/P/A findings included).
--fail-on SEVgates pipelines.
7.1 BOM diff (drift)¶
bom/diff.py (diff_boms) compares two AIBOMs: components present in one and not the other are
added/removed; a same-(type,name) component whose key or tracked attributes (version, hash,
license id/risk) changed is changed (e.g. a version bump or re-license). manifest diff ./old ./new
prints the delta and exits non-zero when anything changed, so CI can gate on unexpected drift.
8. AI enrichment (optional; reuse bulwark_core.ai)¶
Off by default; enabled AND --ai; capped; local Ollama default. Uses:
1. Executive BOM summary: turn the inventory + risk register into a short governance narrative.
2. Component-purpose inference: infer what a model/dataset/prompt is for, to sharpen governance.
3. Control-mapping rationale: phrase why a finding maps to a given control (advisory).
4. Risk-register drafting: propose owner/action language for each risk.
AI output tagged source="ai"; never changes deterministic component facts or finding severities;
degrades gracefully.
9. CLI¶
manifest scan <project-dir> [--format cyclonedx|spdx|json|html|sarif|md]
[--fail-on SEV] [--scan-risk] [--govern] [--offline] [--ai]
manifest components <project-dir> # list discovered components (debug discoverers)
manifest diff <old-dir> <new-dir> # AI-BOM drift; exits non-zero on any change
manifest rules list|lint
manifest version
--scan-risk enables the Airlock/Warden bridges; --govern adds the NIST AI RMF + EU AI Act mapping
and the risk register.
10. Testing & fixtures¶
- Benign sample projects.
sample_project_clean/(pinned components, safetensors model, permissive licenses, clean deps) andsample_project_risky/(unpinned model → B1, non-commercial license → B3, a dep with a known OSV advisory → B4, a referenced secret → B7, a pickle model for the B5 bridge, and anexplore.ipynbfor the notebook discoverer). No real secrets; use obvious fake placeholders. - Golden-file tests for CycloneDX and SPDX output shape and for the component inventory.
- Tests for the notebook discoverer, EU AI Act mapping, and BOM diff.
- Bridge tests use Airlock/Warden fixtures so risk attaches deterministically.
- Tests assert on component
typeand findingcategory+severity, not prose.
11. Release & community¶
README leads with a generated CycloneDX AI-BOM of a real sample project plus the risk register — the
"one command, full inventory + risk" moment. The standards angle (CycloneDX ML-BOM) gives instant
credibility with security/governance audiences. GitHub Action emits the BOM as a build artifact.
CONTRIBUTING.md invites discoverer + control-mapping PRs.
12. Roadmap¶
- ✅ v0.1 — discoverers, CycloneDX, provenance/license/OSV, B1–B4/B6–B8.
- ✅ v0.2 — Airlock/Warden bridges, B5.
- ✅ v0.3 — NIST AI RMF mapping, risk register, HTML, CI gate.
- ✅ v0.4 — AI enrichment.
- ✅ v0.5 — notebook discoverer, SPDX output, EU AI Act mapping, BOM diff (drift between versions).
- ⏭️ v0.6+ — more discoverers (JS/TS agents), richer license-compatibility matrix, hosted BOM dashboard.
13. Research / talk angle¶
"An AI-BOM of N public AI repos: how many pin their models, how many ship unknown/ non-commercial licenses, how many wire unscanned MCP servers." A reproducible governance measurement — pairs with Airlock's and Warden's corpus studies into a single narrative about the state of AI supply-chain hygiene. Strong talk material and a natural capstone paper for the suite.
14. References (verify current versions when building)¶
CycloneDX (ML-BOM / AI components) and SPDX (AI/dataset profiles); NIST AI Risk Management Framework;
EU AI Act (for optional mapping); OSV (dependency advisories); OWASP LLM Top 10 (LLM05/LLM06 for
imported risk); SLSA (provenance concepts); SPDX license list. Bulwark siblings: PROJECT_REFERENCE_
AIRLOCK.md, PROJECT_REFERENCE_WARDEN.md.