Find the AI in your code, and prove it.
AIROM scans a filesystem, git repo, container image, or Kubernetes workload and generates an AI Bill of Materials — the models, prompts, datasets, embeddings, vector databases and frameworks your software actually uses — with file:line evidence behind every entry.
No agent, no daemon, no account, no telemetry. Weights are identified by magic bytes — never loaded, deserialized, or run.
┌──────────────────┬────────────────────────┬─────────┬─────────────┬───────┬────────────────────┬──────────┐ │ KIND │ NAME │ VERSION │ PROVIDER │ CONF │ LOCATION │ EVIDENCE │ ├──────────────────┼────────────────────────┼─────────┼─────────────┼───────┼────────────────────┼──────────┤ │ embedding-model │ text-embedding-3-large │ - │ openai │ 0.85 │ src/rag.py:6 │ 1 occ │ │ framework │ langchain │ 0.2.16 │ langchain │ 0.95 │ requirements.txt:2 │ 1 occ │ │ hosted-llm │ gpt-4.1 │ - │ openai │ 0.85 │ src/rag.py:15 │ 1 occ │ │ library │ openai │ 1.51.0 │ openai │ 0.985 │ requirements.txt:3 │ 2 occ │ │ local-model-file │ tiny.gguf │ - │ local │ 0.95 │ models/tiny.gguf │ 1 occ │ │ prompt │ system.txt │ - │ - │ 0.8 │ prompts/system.txt │ 1 occ │ │ rag-pipeline │ rag-pipeline │ - │ - │ 0.6 │ src/rag.py:6 │ 1 occ │ │ vector-db │ chroma │ 0.5.5 │ chroma │ 0.985 │ requirements.txt:4 │ 3 occ │ └──────────────────┴────────────────────────┴─────────┴─────────────┴───────┴────────────────────┴──────────┘
Sooner or later, someone asks why
“Your AIBOM says this service uses gpt-4.1. Why? Where?”
Most tools can’t answer. They describe a model you named in a registry, or they never look at your code. AIROM is evidence-first: every component it emits carries the proof.
- Occurrences
- Every sighting —
file:line, the matched snippet, and the enclosing function or class when known. - Detection method
- Which technique found it: source-code analysis, AST fingerprint, manifest analysis, binary analysis, hash comparison, config analysis, or filename.
- Confidence
- A calibrated 0–1 score, on the component and on each individual occurrence.
- Detector ID
- The stable rule that fired, e.g.
rules/openai/model-literal.
That lands as CycloneDX evidence.occurrences[] — the field other AIBOM tools
leave empty. Every row is a claim you can walk back to a line of source.
It says what it doesn’t know
A version it couldn’t resolve stays empty instead of guessing. A model outside the lifecycle catalog carries no claim, never a quiet “supported”.
A declared range is not a release
>=1.0,<2 is recorded as a constraint, not a version — which is what
stops the CVE overlay matching advisories against a release nobody confirmed is installed.
Byte-identical output
The same tree produces the same document at any worker count. Determinism is an invariant, enforced by tests, not a side effect.
What it finds
Across Python, JavaScript, TypeScript, Go, Java, Rust, C#, Kotlin and SQL.
| Hosted models | OpenAI, Anthropic, Gemini, Bedrock, Azure OpenAI, Cohere, Mistral, Groq — model IDs and SDK call sites. |
| Local weights | GGUF, safetensors, ONNX, PyTorch, SavedModel, TFLite, HDF5, TensorRT — identified by magic bytes and bounded header parsing. Never loaded or run. |
| Frameworks | LangChain, LlamaIndex, CrewAI, Agno, AutoGen, Semantic Kernel, CAMEL, MetaGPT, Letta, Crawl4AI, FastMCP, Transformers, and more. |
| Local inference & training | vLLM, llama.cpp, GPT4All, Ollama, DeepSpeed, Unsloth. |
| Vector databases | Chroma, Milvus, Qdrant, Pinecone, Weaviate, FAISS, Redis, pgvector — including SQL schemas and a server-side pgvector install. |
| Prompts & datasets | Prompt files and templates, CSV/JSONL/Parquet signatures, load_dataset(), Hugging Face and Kaggle references. |
| Everything else | Generation parameters bound to their call site, serving infrastructure, and RAG pipelines stitched into one component. |
Dependencies are read from manifests, lockfiles, installed metadata, and even PyInstaller binaries — so a frozen app with no source on disk still produces an inventory.
Beyond inventory
An inventory is the floor. These are the things you can do once you have one.
Risk detection
Load-time code-execution surfaces — pickle imports, Keras Lambda layers, GGUF template
gadgets, unsafe torch.load — as CycloneDX vulnerabilities and SARIF.
CVE overlay
Your AI dependencies against OSV.dev, with real CVSS scores and a fail-closed gate.
Refuses under --offline rather than reporting a quiet nothing.
Model lifecycle
Hosted models matched against a curated catalog of provider retirement announcements. Nothing inferred from naming patterns.
Compliance mapping
NIST AI RMF and OWASP Agentic controls as CycloneDX attestations — met, gap, or manual, each linked to the evidence behind it.
AIBOM diff
The semantic delta between two scans, so AI becomes a pull-request gate. Refuses to gate across tooling drift rather than blaming a PR for a rule change.
Signed rule updates
New frameworks and fresh retirement dates reach you without a new binary, over a signed update channel. Scans themselves never fetch.
One scan, every format
Writers are pure projections of one graph — no re-derivation, no drift between them.
| Format | Flag | Notes |
|---|---|---|
| CycloneDX 1.6 / 1.7 | cyclonedx | ML-BOM with modelCard, evidence.occurrences[], vulnerabilities and compliance attestations. |
| SPDX 3.0.1 | spdx | JSON-LD across the AI, Dataset, Software and Security profiles. |
| SARIF 2.1.0 | sarif | One result per occurrence — lands as GitHub Code Scanning alerts. |
| OpenVEX 0.2.0 | vex | Over the CVE overlay. Only ever asserts affected. |
| Native JSON / YAML | json · yaml | The two lossless formats. Round-trip the whole graph. |
| Table · Markdown | table · compliance | Human review in a terminal, and a Markdown compliance report. |
Gate a build
# many formats, one pass airom scan . -o cyclonedx=bom.json -o spdx=bom.spdx.json # fail the build on what matters airom scan . --exit-code 1 --fail-on "risk:high|cve:critical"
In GitHub Actions
- run: pip install airom - run: airom scan . -o sarif=airom.sarif - uses: github/codeql-action/upload-sarif@v3 with: { sarif_file: airom.sarif }
Built like a security tool
- No model execution, ever. Weights are identified by magic bytes and bounded header parsing. Nothing is loaded, deserialized, or run.
- Fuzzed parsers. Every binary header parser is fuzzed in CI and must return errors, never panic.
-
No surprise network access.
--offlineasserts it globally. -
Signed releases.
CGO_ENABLED=0, cosign-signed, shipping an SBOM and a self-scanned AIBOM.
Where it stands
v0.3.6 — early but real. The pipeline, detectors, writers and overlays are implemented and tested. Expect rough edges.
Known gaps, each also surfaced in the affected flag’s --help:
caching is not implemented, live registry and daemon image pulls are not available
(use airom image --input <archive>), and live-cluster scanning is not
available (use airom k8s --manifests <dir>).
The full ledger — what’s complete, what’s deferred, and how AIROM compares to adjacent tools — is in project status.
Scan something
One command, one static binary. Point it at a repo and see what comes back.
pip install airom && airom scan .