airom image scans a container image: it resolves the image’s squashed root filesystem and streams its files through the same detector pipeline as a directory scan.
What works today
A positional reference is accepted only when it is a path to one of those on disk. A reference and
--input are mutually exclusive; supplying neither is a usage error.
The docker save workflow
1
Export the image to a tarball
docker save
2
Scan the tarball
Scan it — no network
3
Write the AIBOM
CycloneDX out
--input reads a saved archive and touches no network. It is the supported path for CI, air-gapped builds, and anywhere a registry pull is not on the table.
Layers are squashed
AIROM applies the image’s layers top→base with whiteout and opaque-directory resolution, exactly once, and scans the resulting effective filesystem. A file deleted by a later layer is gone; a file overwritten by a later layer is seen only in its final form.The AIBOM answers “what’s in the final image”, not “what did each layer contribute”. There is no per-layer attribution in the output.
Bounded memory on any image size
Layer tar streams are consume-once, so each effective file’s bytes are captured during the single squash pass. That capture is strictly bounded:
Memory and disk stay bounded regardless of image size. Temp files are removed when the scan finishes.
--platform
To scan a specific architecture reliably, export that architecture yourself and scan the resulting archive:
Export one platform, then scan it
Ignore globs
--ignore works on image scans and matches against image-root paths. The .gitignore/.airomignore stacks do not apply here — an image has no ignore files to read. Only your --ignore globs filter an image walk.
Skip a vendored directory inside the image
Versions come from installed metadata
An image rarely ships the manifest it was built from.requirements.txt and
pyproject.toml stay behind in the source repo; what lands in the image is
site-packages. So AIROM reads the installed metadata directly —
<pkg>-<version>.dist-info/METADATA (PEP 376) and <pkg>.egg-info/PKG-INFO —
and that is the strongest version evidence there is. A manifest records the
version that was asked for (openai>=1.0); installed metadata records the
one that is there.
CONF reads 0.99. LOCATION shows the code site;
airom scan --format json lists both. Without the metadata half, the VERSION
column is -.
gpt-4o-mini has no version because a hosted model has no installed
metadata to read; its lifecycle is covered by the EOL overlay
instead.
This is what makes the CVE overlay useful on an image. A
component with no version can be listed but never matched against an advisory
database, so a version-less scan reports zero vulnerabilities no matter how
many the image actually has.
METADATA it finds and reports only the ones its AI catalog recognizes — numpy,
click, and certifi are read and dropped. An AIBOM is not an SBOM.
The same applies to any installed environment on disk, not just images: a
docker cp’d site-packages, an unpacked PyInstaller bundle, or a container
filesystem mounted for inspection. Note that .venv/ and venv/ are
skipped by default, so scanning a source checkout
still reads its manifest rather than its virtualenv.
Known limitations
zstd-compressed layers are unsupported
zstd-compressed layers are unsupported
Gzip and uncompressed layers are read. A zstd-compressed layer is recorded as an
Unknown and its contents do not reach the detectors.Image entries are not seekable
Image entries are not seekable
Files are served from a consume-once spool, so detectors that need random access get
ErrNotSeekable. Streaming and header reads are unaffected.Live pull and image digest
Live pull and image digest
Registry and daemon resolution is deferred. The
imageDigest field of the AIBOM source block is not populated by the current pipeline.Example
Image scan, filtered, two outputs