An OBOM, or Operations Bill of Materials, is a CycloneDX document that lists what is actually running on a host, virtual machine or container: the operating system, installed packages, services and configuration. An SBOM describes what you built. An OBOM describes what you operate, so you can answer incident and drift questions about live systems.
CycloneDX describes OBOM as a "full-stack inventory of runtime environments, configurations, and additional dependencies." In the schema it is not a separate format at all. It is an ordinary CycloneDX BOM whose metadata.lifecycles says operations, filled with the component types, services and data entries that describe a running system. Below: the fields, how it differs from deployed and runtime SBOMs, how to generate one with the open source cdxgen tool, and how to use it. If you want the broader map of SBOM types first, start with our post on the six SBOM types.
Key takeaways
- An OBOM is a CycloneDX BOM produced in the
operationslifecycle phase, which the CycloneDX 1.5 schema added on 26 June 2023. There is no separate OBOM file format and no OBOM schema. - The schema says an operations BOM "will generally encompass multiple SBOMs" plus HBOMs: the host level view that ties them together.
- It records more than packages: the OS, runtimes, drivers, files, configuration, services and endpoints.
- SPDX 3.0.1 has no OBOM type. Its closest values are the
deployedandruntimeSBOM types, taken from a CISA document published on 21 April 2023. - The cdxgen project ships an
obomcommand, an alias forcdxgen -t os, built on osquery. It supports live Linux, Windows and macOS hosts, and its README lists Linux container images and Linux or Windows VMs as OBOM targets. - OBOMs answer incident scoping ("which hosts run this version right now?") and drift questions (today against last week, or against the source image).
- Limits: complete collection usually needs root, the output is sensitive host data, and every OBOM goes stale as soon as the host changes.
Timeline: how the OBOM idea took shape
The term grew out of CycloneDX schema changes rather than a single announcement. The dates below come from the CycloneDX specification releases on GitHub, the CycloneDX 1.5 release notice, the CISA SBOM types document and the SPDX 3.0.1 model pages.
| Date | Event | What it means for OBOMs | Source |
|---|---|---|---|
| 12 January 2022 | CycloneDX 1.4 released | Component types already include operating-system, container, device, firmware and file, but there is no lifecycle field | CycloneDX specification releases and 1.4 schema |
| 21 April 2023 | CISA publishes "Types of Software Bill of Material (SBOM) Documents" | Defines Deployed and Runtime SBOM types, the closest SBOM cousins of an OBOM | CISA document; date as cited by SPDX 3.0.1 |
| 26 June 2023 | CycloneDX 1.5 released | Adds metadata.lifecycles with the operations phase, plus platform, device-driver and data component types | CycloneDX 1.5 announcement and schema |
| 9 April 2024 | CycloneDX 1.6 released | Lifecycle phases and OBOM relevant component types carried forward; adds cryptographic-asset | CycloneDX specification releases and 1.6 schema |
| 21 October 2025 | CycloneDX 1.7 released | Same lifecycle enum and component types as 1.6 for OBOM purposes | CycloneDX specification overview and 1.7 schema |
| 10 December 2025 | Current ECMA-424 edition listed as published | CycloneDX is also an Ecma International standard | CycloneDX specification overview page |
We compared the enumerations in the 1.4 to 1.7 JSON schemas directly.
How does CycloneDX define an OBOM?
CycloneDX's capability page frames OBOM as an operational view of "configurations, operating systems, hardware, and software dependencies active in staging or production environments," bridging development and operations teams.
The binding definition sits in the schema. In CycloneDX 1.5 and later, metadata.lifecycles is an array of phases that say when the data in the BOM was captured. There are seven predefined phases: design, pre-build, build, post-build, operations, discovery and decommission. A producer can also declare a custom phase with a name and description.
The schema's operations description says the BOM represents inventory that is running and operational, in staging or production, and will generally cover multiple SBOMs plus HBOMs for the hardware. It names OBOM explicitly.
Three consequences follow. An OBOM validates against the normal CycloneDX schema, so any CycloneDX parser can read one. "OBOM" is a claim the producer makes, not something a validator can check: three npm packages labelled operations are still schema valid. And the lifecycle is an array, so one document can declare both post-build and operations; consumers should not assume a single value.
How is an OBOM different from an SBOM, a deployed SBOM and a runtime SBOM?
This is the question most people actually have, because the words overlap. CISA's April 2023 document defines six SBOM types: Design, Source, Build, Analyzed, Deployed and Runtime. SPDX 3.0.1 copies those six into its SbomType vocabulary and says so explicitly. Neither list contains an "operations" type.
CISA defines a Deployed SBOM as an inventory of software present on a system, which may assemble other SBOMs and combine analysis of configuration options and execution behaviour in a deployment environment. It defines a Runtime SBOM as one generated by instrumenting the running system to capture only components present, including external call outs and dynamically loaded components.
An OBOM overlaps with both, but it is broader in scope. A deployed or runtime SBOM is still a software inventory. An OBOM in the CycloneDX sense is meant to include configuration, services, the operating system and links to hardware.
| Question | Build or source SBOM | Deployed SBOM (CISA) | Runtime SBOM (CISA) | OBOM (CycloneDX) |
|---|---|---|---|---|
| What is the subject? | One application or artifact | Software installed on a system | Software observed running on a system | A running environment: host, VM, container or cluster node |
| When is it produced? | At build or release | After install or deploy | While the system runs | During operations, on a schedule or after change |
| Includes OS packages? | Rarely, unless the artifact is an image | Yes | Only those observed in use | Yes |
| Includes configuration? | No | Sometimes | Sometimes | Yes, as data components of type configuration and as properties |
| Includes services and endpoints? | Sometimes, for the app's own APIs | Rarely | External call outs | Yes, in services |
| Links to hardware? | No | No | No | Yes, by design, through BOM-Link or a merged host view |
| SPDX 3.0.1 equivalent | source or build | deployed | runtime | None; nearest is deployed plus runtime |
| CycloneDX lifecycle | pre-build or build | post-build or operations | operations | operations |
For a deeper comparison of the build time and runtime ends of that spectrum, see runtime SBOM vs build time SBOM. If your question is about the US federal angle, our post on OMB M-26-05 and the SBOM of the runtime production environment covers the footnote that asks cloud producers for exactly this kind of inventory on request.
What does an OBOM record, and in which fields?
Because an OBOM is a normal CycloneDX document, the useful question is which parts of the schema carry operational facts. Here is the mapping we use, with the exact field names from the CycloneDX 1.6 JSON schema. Everything below also exists in 1.7.
| What you want to record | CycloneDX field | Notes |
|---|---|---|
| That this is an operational inventory | metadata.lifecycles[].phase = operations | Added in 1.5. Can sit beside other phases |
| When it was collected | metadata.timestamp | Treat as the "as of" time; an OBOM without it is hard to use for drift |
| The system it describes | metadata.component | Often the operating system or a named host; give it a stable bom-ref |
| Operating system | component type = operating-system | The schema defines it as an OS regardless of deployment model, whether physical, VM or image |
| Language runtimes and execution platforms | component type = platform | Added in 1.5: a runtime that interprets or executes software |
| OS and language packages | component type = library or application, with purl | Package URLs make OBOM entries comparable to SBOM entries |
| Drivers and firmware | device-driver, firmware | device-driver added in 1.5 |
| Files outside package ownership | type = file | Useful for hand installed binaries |
| Containers on a host | type = container | A container component can nest its own components |
| Configuration values | type = data, then data[].type = configuration | The schema describes configuration as parameters or settings used by other components |
| Where something was found | evidence.occurrences | Records each install location without baking a path into the identifier |
| Listening services and endpoints | services[] with endpoints, authenticated, x-trust-boundary | x-trust-boundary says whether use of the service crosses a trust zone |
| Which component uses which | dependencies[] | Often sparse in OBOMs; see below |
| How complete the inventory is | compositions[].aggregate | Values include complete, incomplete and unknown |
| Links to the app SBOMs and the HBOM | externalReferences[] with type = bom, or BOM-Link URNs | The schema describes the bom reference type as "SBOM, OBOM, HBOM, SaaSBOM, etc" |
| Tool specific facts | properties[] name and value pairs | cdxgen uses cdx: prefixed names for host details |
Two of those rows need a caution.
Dependencies are thin. The cdxgen OBOM lessons say plainly that an OBOM "has no dependency graph," and that unlike a container BOM it does not link files to their runtime with edges. An operating system's package manager knows what is installed, not always which running process uses what. If you need the edges, generate an SBOM per application and link it from the OBOM.
Configuration is where OBOMs earn their name, and where they vary most. CycloneDX gives you a data component of type configuration and free form properties, but it does not define a standard list of configuration keys. Agree on a property naming convention, or stick to one tool's documented taxonomy, before you diff OBOMs from different generators.
A minimal OBOM example
Here is a small, hand written OBOM for a single Ubuntu VM. It validates against both the CycloneDX 1.6 and 1.7 JSON schemas; we checked it with a JSON Schema validator using the official schema files from the CycloneDX specification repository. The package versions are illustrative.
{
"$schema": "http://cyclonedx.org/schema/bom-1.6.schema.json",
"bomFormat": "CycloneDX",
"specVersion": "1.6",
"serialNumber": "urn:uuid:3e671687-395b-41f5-a30f-a58921a69b79",
"version": 1,
"metadata": {
"timestamp": "2026-10-23T09:00:00Z",
"lifecycles": [
{ "phase": "operations" }
],
"component": {
"bom-ref": "host:web-01",
"type": "operating-system",
"name": "ubuntu",
"version": "24.04",
"description": "Production VM web-01, staging subnet"
}
},
"components": [
{
"bom-ref": "pkg:deb/ubuntu/openssl@3.0.13-0ubuntu3.5?arch=amd64&distro=ubuntu-24.04",
"type": "library",
"name": "openssl",
"version": "3.0.13-0ubuntu3.5",
"purl": "pkg:deb/ubuntu/openssl@3.0.13-0ubuntu3.5?arch=amd64&distro=ubuntu-24.04"
},
{
"bom-ref": "pkg:deb/ubuntu/nginx@1.24.0-2ubuntu7.1?arch=amd64&distro=ubuntu-24.04",
"type": "application",
"name": "nginx",
"version": "1.24.0-2ubuntu7.1",
"purl": "pkg:deb/ubuntu/nginx@1.24.0-2ubuntu7.1?arch=amd64&distro=ubuntu-24.04"
},
{
"bom-ref": "config:sysctl",
"type": "data",
"name": "sysctl-hardening",
"data": [
{
"type": "configuration",
"name": "net.ipv4.ip_forward",
"contents": {
"properties": [
{ "name": "value", "value": "0" }
]
}
}
]
}
],
"services": [
{
"bom-ref": "svc:nginx-https",
"name": "nginx",
"endpoints": [ "https://web-01.internal:443/" ],
"authenticated": false,
"x-trust-boundary": true
}
],
"dependencies": [
{
"ref": "host:web-01",
"dependsOn": [
"pkg:deb/ubuntu/nginx@1.24.0-2ubuntu7.1?arch=amd64&distro=ubuntu-24.04"
]
},
{
"ref": "pkg:deb/ubuntu/nginx@1.24.0-2ubuntu7.1?arch=amd64&distro=ubuntu-24.04",
"dependsOn": [
"pkg:deb/ubuntu/openssl@3.0.13-0ubuntu3.5?arch=amd64&distro=ubuntu-24.04"
]
}
],
"compositions": [
{ "aggregate": "incomplete", "assemblies": [ "host:web-01" ] }
]
}
A few choices in that example are deliberate:
lifecyclessaysoperations. Without it, a consumer has no machine readable way to tell this from a container image SBOM. We confirmed that the schema rejects an invented phase name such asruntimein the predefinedphasefield; use a custom phase object withnameif you need one.- The purls carry
archanddistroqualifiers, so they match the ones in image SBOMs. - The sysctl value is a
datacomponent. The schema says thedataarray should be present only on components of typedata. compositionssaysincomplete, which is honest for a three component file.- The dependency edges are hand written. We added two to show the field. Generated OBOMs usually have few or none, for the reason given above.
To validate a file like this yourself, the CycloneDX project's own tools include a validator. cdxgen ships cdx-validate (cdx-validate -i obom.json), and any JSON Schema validator can check a file against bom-1.6.schema.json if you also load the spdx.schema.json and jsf-0.82.schema.json files it references.
How cdxgen builds an OBOM
cdxgen is the CycloneDX generator. Its repository now lives at github.com/cdxgen/cdxgen (the old CycloneDX organisation path redirects there), and its README lists OBOM among the supported BOM types: "For Linux container images and VMs running Linux or Windows operating systems." Everything in this section comes from that README and the project's OBOM lessons document.
How it works, in short:
obomis an alias forcdxgen -t os. The README says live system mode supports Linux, Windows and macOS, though some macOS tables need elevated privileges and Full Disk Access.- Collection is powered by osquery, installed with cdxgen's binary plugins. cdxgen runs platform specific default queries kept in the project's
data/queries*.jsonfiles. - The Linux profile includes hardening snapshots (
sysctl_hardeningandmount_hardening), Secure Boot certificate inventory, and GTFOBins enrichment for privileged and network active runtime rows. - An optional
trustinspectorhelper adds macOS code signing, Windows Authenticode and WDAC policy data. - The output is large: the README says several minutes and thousands of components.
- Default spec version is 1.7. Pass
--spec-version 1.6if a consumer needs 1.6. The README says 1.4 and 1.5 are only available as downgrades throughcdx-convert. - A built in audit can run in the same pass.
--bom-auditwith--bom-audit-categories obom-runtimeevaluates built in OBOM rules and can fail a pipeline on a severity threshold.
One detail matters for the lifecycle field. In the cdxgen source we read, the hidden --lifecycle option accepts pre-build, build and post-build only. Check what your version writes into metadata.lifecycles, and add operations yourself if you need consumers to see it. The walkthrough below shows how.
Step by step: generate an OBOM for a Linux VM
Start on a staging VM that mirrors production. osquery is read only, but a deep scan takes minutes and touches every package database.
Step 1. Create a private output directory. An OBOM contains user names, key snapshots, sudoers content and listening ports.
mkdir -p ~/obom && chmod 700 ~/obom
cd ~/obom
Step 2. Install the obom binary and verify its checksum. The cdxgen README recommends the standalone release binaries for production use and shows a download plus sha256sum -c pattern. The obom release binary bundles the platform plugin package with the OS inventory helpers, so you do not need Node.js. Set VERSION to the current release tag from the project's releases page.
VERSION="vX.Y.Z" # current tag from github.com/cdxgen/cdxgen/releases
ASSET="obom-linux-amd64" # obom-linux-arm64 on ARM hosts
BASE_URL="https://github.com/cdxgen/cdxgen/releases/download/${VERSION}"
curl -fsSLO "${BASE_URL}/${ASSET}"
curl -fsSLO "${BASE_URL}/${ASSET}.sha256"
sha256sum -c "${ASSET}.sha256"
chmod +x "${ASSET}"
./"${ASSET}" --help
The README also documents npm (@cdxgen/cdxgen), Homebrew and winget installs.
Step 3. Run a first, plain collection.
./obom-linux-amd64 -o obom.json
Step 4. Check what you got before you trust it.
# How many components, and of which types?
jq '.components | length' obom.json
jq -r '.components[].type' obom.json | sort | uniq -c | sort -rn
# What does the document say about its own lifecycle and spec version?
jq '{spec: .specVersion, lifecycles: .metadata.lifecycles, ts: .metadata.timestamp}' obom.json
If package counts look low, the most common reason is permissions. The cdxgen OBOM lessons say that when a package database exists but cannot be read, for example because the scan did not run as root, those packages are missing and a degradation is recorded. On Alpine and other musl based systems there is a second catch: the plugin bundles for musl ship no osquery, so cdxgen reads only the apk installed database and the OBOM has no OS component.
Step 5. Re run with depth and the runtime audit. This is the profile the cdxgen OBOM lessons recommend as the default host collection for security work.
sudo ./obom-linux-amd64 -o obom.json --deep --bom-audit --bom-audit-categories obom-runtime
sudo chown "$USER" obom.json && chmod 600 obom.json
Run it with elevated rights only where your change process allows that, and only on hosts you are responsible for.
Step 6. Mark the document as an operations BOM if your generator did not.
jq '.metadata.lifecycles = ((.metadata.lifecycles // []) + [{"phase":"operations"}] | unique)' \
obom.json > obom.ops.json && mv obom.ops.json obom.json
jq '.metadata.lifecycles' obom.json
Step 7. Validate and, if you need SPDX for a consumer, convert.
cdx-validate -i obom.json
cdx-convert -i obom.json -o obom.spdx.json # CycloneDX JSON to SPDX 3.0.1 JSON-LD
The README describes cdx-convert as converting CycloneDX JSON to SPDX 3.0.1 JSON-LD; its guide also shows --to 1.6 for writing an older CycloneDX version for legacy consumers. Expect loss in the SPDX direction: SPDX 3.0.1 has no operations lifecycle and no direct equivalent for every CycloneDX property the OBOM carries.
Step 8. Store it with its context. Name the file after the host and the collection time, for example web-01.2026-10-23T0900Z.obom.json, and keep a hash.
f="web-01.$(date -u +%Y-%m-%dT%H%MZ).obom.json"
mv obom.json "$f"
sha256sum "$f" > "$f.sha256"
If it will leave the team, sign it with cdx-sign and check it with cdx-verify.
Step by step: generate an OBOM for a container image
For a container image there are two routes, and the difference is useful.
Route A: scan the image from the outside. This is what most teams already do for image SBOMs.
# From a registry
cdxgen -t docker registry.example.com/shop/api:1.42.0 -o image.json
# Or from a saved tarball, for offline or staged scans
docker pull registry.example.com/shop/api:1.42.0
docker save -o /tmp/api.tar registry.example.com/shop/api:1.42.0
cdxgen /tmp/api.tar -t docker -o image.json
The README says that container and rootfs BOMs also report how many executables and shared libraries sit outside OS package ownership, in the metadata properties cdx:container:unpackagedExecutableCount and cdx:container:unpackagedSharedLibraryCount. Those two numbers are an honest measure of how much of the image your package based inventory cannot explain.
Route B: run obom inside a running container from that image. This is the operational view: what the container looks like after it has started, been configured and possibly been changed.
The cdxgen OBOM lessons describe exactly this comparison. Since cdxgen versions after 13.5.0, an OBOM names OS packages the same way an image scan does, with the distro vendor as the purl namespace and arch and distro qualifiers. So you can compare the two lists directly. The image scan also lists source packages tagged source, which the OBOM does not, so leave those out:
jq -r '.components[] | select((.tags // []) | index("source") | not) | (.purl // empty)
| select(startswith("pkg:deb/") or startswith("pkg:rpm/") or startswith("pkg:apk/"))' image.json \
| sort -u > image-purls.txt
jq -r '.components[] | (.purl // empty)
| select(startswith("pkg:deb/") or startswith("pkg:rpm/") or startswith("pkg:apk/"))' host.json \
| sort -u > host-purls.txt
diff image-purls.txt host-purls.txt && echo identical
Here host.json is the OBOM collected inside the container. Any line in the diff is drift between what you shipped and what is running: a package someone installed by hand, an upgrade run inside the container, or an entrypoint script that pulled something at start up. The lessons also warn that OBOMs from cdxgen 13.5.0 or earlier used different purl shapes (subpaths, missing qualifiers, rpm versions without release), so regenerate old OBOMs before you diff them against new ones.
For golden images and offline disks, cdxgen also has a rootfs mode (-t rootfs) with a rootfs-hardening audit category that reads files without the system running.
Using an OBOM in incident response
An SBOM answers "which of our products include this component?" An OBOM answers the operational version of the same question: "which of our hosts are running it right now, and what else is going on there?" For the product side of that workflow, see SBOM incident response: finding affected products fast.
Scoping: which hosts carry the affected version?
If you keep OBOMs for a fleet in one directory, a single jq pass gives you a host list. Replace the package name with the one in the advisory, then compare the versions against its fixed version.
PKG="openssl"
for f in obom-store/*.obom.json; do
jq -r --arg p "$PKG" --arg f "$f" '
.components[]
| select(.name == $p)
| [$f, .version, (.purl // "")] | @tsv' "$f"
done | sort -k2
Because OBOM purls carry the distro and architecture, the result tells you which hosts need the distribution's fixed package. Hand built binaries are harder. The cdxgen lessons use PHP as the worked example: a PHP built from source is not in the package tables, so the OBOM probes a fixed set of directories (one level deep, at most 64 files, nothing executed) and records unowned binaries as file components plus a pkg:generic/php component that vulnerability rules can match.
Triage: what does the host look like around the finding?
The cdxgen OBOM lessons suggest reviewing startup and persistence surfaces first, because early compromise persistence tends to show up there. On Linux that list includes systemd_units, crontab_snapshot, authorized_keys_snapshot, sudoers_snapshot, ld_preload, elevated_processes and privileged_listening_ports. The project's REPL, cdxi, has shortcuts for these categories:
cdxi web-01.2026-10-23T0900Z.obom.json
# then, inside the REPL:
.osinfocategories
.elevated_processes
.privileged_listening_ports
.sudo_executions
That is a triage aid, not a detection product: a snapshot from one moment, collected by a tool that an intruder on the same host could interfere with.
Keep the investigation off the host
A timestamped, hashed OBOM from before an incident is a clean baseline, and new rules can be applied to it later without touching the host again, as the cdxgen README shows:
cdx-audit --bom web-01.2026-10-23T0900Z.obom.json --direct-bom-audit --categories obom-runtime
Using an OBOM for drift review
Drift is the gap between what you meant to run and what is running. OBOMs make it measurable, as long as you compare like with like.
Image versus container. Route B above compares the image SBOM with an OBOM taken inside a running container. A non empty diff on a container that should be immutable is a finding by itself.
Today versus last week. The cdxgen lessons recommend tracking baseline drift by comparing periodic OBOMs. A simple version for packages:
pkgs () { jq -r '.components[] | select(.purl) | .purl' "$1" | sort -u; }
diff <(pkgs web-01.2026-10-16T0900Z.obom.json) <(pkgs web-01.2026-10-23T0900Z.obom.json)
For configuration, compare the names and properties of data components the same way.
Policy gating. The OBOM lessons show a pipeline step that fails on high severity host posture findings:
obom -o obom.json --bom-audit --bom-audit-categories obom-runtime --bom-audit-fail-severity high
Their suggested policy: block on critical and high, ticket medium with an SLA, trend low.
Two cautions. Compare OBOMs made by the same cdxgen version, flags and user; a non root run that cannot read the package database looks like mass package removal. And diff identities (purls, data names, service names), not whole documents, because process IDs and ephemeral ports change on every boot.
How an OBOM pairs with an HBOM for a single host view
The CycloneDX schema's own description of the operations phase says an operational BOM will generally include HBOMs for the hardware. There are two ways to get there.
Merge them in one document. The cdxgen README describes hbom --include-runtime, which produces a merged HBOM and OBOM view for the current host. It joins hardware and runtime records only on strict topology links, such as an interface name, the driver module for a device, storage to runtime mapping and explicit Secure Boot trust matches, and it adds a host-topology audit pack. If hardware collection reports missing utilities or permission issues, the README suggests hbom diagnostics before re-running with --privileged.
hbom -o host.json --include-runtime
Link separate documents. Keep the HBOM, the OBOM and the per application SBOMs as separate files and point between them. CycloneDX BOM-Link uses the URN form urn:cdx:serialNumber/version#bom-ref, which lets an OBOM entry for the NIC driver point at the NIC component in the HBOM without copying it. The externalReferences type bom covers whole document links. The BOM-Link capability page highlights the benefit: targeted access to one BOM without exposing unrelated or sensitive data in another.
Merge when one team owns the host and wants one file to diff. Link when hardware, application and host data come from different owners on different schedules. For the hardware side, our HBOM complete guide and how to create an HBOM in CycloneDX cover the fields and the build, and HBOM vs SBOM explains where the two meet.
Limits: privilege, sensitive data and refresh cadence
Privilege
Good OBOMs need read access to package databases and system tables, and some sources need administrator or root rights. The cdxgen documentation calls out several cases: package databases that a non root user cannot read, and macOS tables that need elevated privileges and Full Disk Access. Its permissions guide adds that, for the hardware collector used in a merged host view, a non administrator over SSH or WinRM is denied every WMI query on Windows. The safe pattern is a dedicated collection account with the minimum rights your OS needs, used by an approved job, rather than ad hoc sudo from a personal account.
Sensitive data in the output
An OBOM is a map of a host for whoever reads it. Depending on the profile, it can include user and session inventories, SSH authorized keys snapshots, sudoers rules, listening ports, running processes, startup entries and configuration values. cdxgen deliberately keeps home directory paths out of purls and bom-ref values and records them in evidence.occurrences and a path property instead, but they are still in the document.
Treat OBOMs like other host security data: restrict access by default, keep them out of public repositories and vendor visible tickets, strip the user and persistence tables before sharing, and set a retention period. BOM-Link helps, because you can share an application SBOM without the host OBOM that links to it.
Refresh cadence
Every OBOM is a point in time snapshot, and CISA notes for runtime SBOMs that some information appears only after a system has run for a while. A workable cadence: collect after every change window, weekly for long lived hosts (daily for internet facing ones), on demand during incidents, and at image build time for containers. For short lived containers and autoscaled nodes, scan the image and sample running instances for drift rather than collecting from every instance.
Coverage and trust
A package database does not list software installed by copying files, and a compromised host can lie to a collector running on it. The compositions field lets producers say "incomplete"; use it, and read it in OBOMs you receive.
Where Safeguard fits, honestly
Safeguard does not generate OBOMs, does not run host or VM inventory collection, and does not provide device management, endpoint protection or HBOM generation. Building the operational inventory described in this post is work for an open source collector such as cdxgen and for your own operations process.
The overlap is the software inside the images and applications an OBOM points to. Safeguard's SBOM generation and analysis is generally available: it produces and analyses SBOMs in SPDX (JSON, XML, RDF) and CycloneDX (JSON, XML), exports to more than 30 formats including CSV, Excel and PDF, and can rescan continuously on push, on a schedule or on release. From the CLI you can scan a container image (safeguard scan --image <name>) or an SBOM file you already have (safeguard scan --sbom <file>), and the platform shows which of your projects contain a given component and version. CSAF and VEX upload is also generally available, so a supplier's "not affected" statement sits next to the finding. Policies and gates, also generally available, let you evaluate an image against your rules before it is promoted, which reduces how much an OBOM later has to find. We have not built OBOM specific views, so the host posture properties a collector like cdxgen writes are not interpreted by Safeguard.
Common mistakes
- Calling any image SBOM an OBOM. An image scan is a post-build view of an artifact. It becomes operational evidence only if it was collected from, or verified against, what is actually running.
- Leaving out
metadata.lifecycles. Without it, consumers cannot tell an OBOM from any other CycloneDX file. Addoperationsexplicitly. - Collecting without root and not noticing. Missing package databases look like a clean, small host. Check the component counts and any recorded degradations before trusting a low number.
- Diffing OBOMs from different tool versions. cdxgen changed its purl shapes after 13.5.0. A diff across versions is mostly noise.
- Treating the OBOM as a dependency graph. Most OBOMs have few or no edges. Use per application SBOMs for "what uses what."
- Sharing a full OBOM with a vendor or auditor. It can expose accounts, keys snapshots and listening ports. Share the minimum, or link to a narrower BOM.
- Assuming SPDX conversion is lossless. SPDX 3.0.1 has no operations type and no field for every CycloneDX property. Keep the CycloneDX original as the record.
FAQ
What does OBOM stand for?
Operations Bill of Materials: a CycloneDX BOM produced in the operations lifecycle phase that inventories a running environment.
Is an OBOM the same as a runtime SBOM?
Not quite. CISA's Runtime SBOM type covers software observed in a running system, including dynamically loaded components. An OBOM in CycloneDX also covers configuration, services and hardware links. In practice the two overlap, and an OBOM is one concrete way to produce the SBOM "of the runtime production environment" that OMB M-26-05 lets agencies request from cloud providers.
Is there an OBOM schema or file format?
No. An OBOM is a normal CycloneDX document. The operations lifecycle phase, added in CycloneDX 1.5, is what marks it. It validates against the standard 1.5, 1.6 or 1.7 schema.
Does SPDX support OBOMs?
Not by name. SPDX 3.0.1's SbomType vocabulary has deployed and runtime as the nearest equivalents, and some OBOM fields have no direct SPDX counterpart.
How do I generate an OBOM for a Linux server?
With cdxgen, run obom -o obom.json --deep --bom-audit --bom-audit-categories obom-runtime with enough rights to read the package databases, then check and timestamp the output.
Can I generate an OBOM for a Docker container?
Yes, two ways. Scan the image with cdxgen -t docker <image> for the shipped inventory, and run obom inside a running container from that image for the operational inventory. Diffing the package purls from the two shows drift.
What is the difference between an OBOM and an HBOM?
An HBOM lists physical hardware. An OBOM lists what runs on a system and references the HBOM underneath; hbom --include-runtime merges the two.
Do I need root to generate an OBOM?
For a complete one, usually yes, because a non root run can silently miss packages. Use a dedicated, minimum privilege collection account.
How often should I regenerate an OBOM?
After every change, on a schedule for long lived hosts (weekly to start, daily for exposed systems), and during incidents. For containers, scan the image and sample running instances for drift.
Is OBOM data sensitive?
Yes. It can include accounts, SSH key snapshots, sudo rules and listening ports, so restrict access and share only what a recipient needs.
Fix it without waiting for upstream
An OBOM tells you which hosts and containers are running a vulnerable package, but it does not ship the fixed build. For the images and open source dependencies you build yourself, that last step still runs through your normal supply chain, and the fit with the workflow below is direct there and indirect for operating system packages on long lived VMs.
Whatever the vulnerability, most of the risk sits in two gaps. The first is the time between a fix being published and that fix actually running in your production builds. The second, and worse, is the time before any fix exists at all, while a maintainer works out a patch and a release schedule. Waiting through both gaps is a habit, not a requirement. Safeguard gives teams three ways to close them.
1. Autonomous remediation: pull requests, not tickets
When a fixed version exists, Safeguard's Griffin models open a tested, policy gated pull request that moves the affected dependency to the safe version. You can run it on one repository or in bulk across every project that carries the vulnerable version. Remediation covers eight categories, including dependencies, licenses, security posture and findings. Reachability analysis ranks the vulnerable code paths your application actually calls first, so the first pull requests you merge are the ones that matter most. Autonomous remediation ships switched off, with every category set to Manual, so your team decides how much runs on its own and what still needs a human approval.
2. Gold Open Source: the safe version in seconds, free
gold.safeguard.sh is free and needs no login. Search any CVE or package to see affected and fixed versions, CVSS, EPSS, CISA KEV status and a recommended safe version, or use the "Am I affected?" checker. The same data is available through a free read API, README status badges and RSS threat feeds. The safeguard-sh/gold-scan-action GitHub Action can fail a build on a known exploited vulnerability, on any known vulnerability, or never, so a version you have just removed cannot quietly come back in next week's merge.
3. Gold Registry: start from components that are already fixed
registry.safeguard.sh provides hardened, drop in replacements for upstream packages and container images. Every Gold artifact is held continuously to zero exploitable critical or high CVEs, zero malware and a completed license check, with SLSA L3 or higher provenance, a Sigstore signature and a public evidence page you can hand to an auditor. An artifact loses its Gold badge if it regresses. Teams can point their own package directory at the registry so hardened equivalents resolve without changing how developers install anything.
When upstream has no fix yet, or will not ship one, Safeguard first works to get the fix accepted upstream. If the issue is severe or upstream will not move, Safeguard builds a fork, fixes the vulnerability there and publishes a hardened Gold build, so you are not stuck on a maintainer's release schedule. You can also request a hardened, compatibility tested build of a specific package. This fork and patch flow is in early access today, so ask which packages it covers before you plan around it.
| Your situation | What to use |
|---|---|
| A fixed version exists | Autonomous remediation opens the upgrade pull request; Gold Open Source confirms the safe version |
| You need to know today whether you are affected | Gold Open Source lookup and "Am I affected?" checker, free and without login |
| You want vulnerable versions to stop shipping | The Gold scan GitHub Action in CI, plus Safeguard policies and gates |
| Upstream has no fix yet, or will not ship one | A custom Gold request; fork and patch (early access) |
| You want to start clean on new projects | Pull hardened components and images from Gold Registry |
One honest limit: when the vulnerable code lives in a vendor appliance, a hosted service or an operating system runtime you do not build yourself, the fix still has to come from that vendor or distribution. The steps above apply to the open source dependencies and container images you build and ship, which is where most teams carry the largest and least tracked share of their exposure.
See how Safeguard would help
The first demo is a real terminal session with the Safeguard CLI. It reads a vulnerability finding and its advisory, asks before each command, upgrades a dependency, runs the tests with your approval and pushes nothing. When an OBOM diff shows that a running container carries a vulnerable library version from its image, this is the flow that fixes the application or image definition so the next build ships the safe version.
Click through it at your own pace, or open it full screen.
The second demo sets up the local runner on your own machine, approves it with a device code and runs scans from the terminal. That suits an operations workstation where you scan the images behind your OBOMs, or an SBOM file you already hold, without sending your host inventory anywhere. The OBOM itself still comes from your collector.
Click through it at your own pace, or open it full screen.
The third demo connects Claude to the Safeguard MCP server with OAuth and lets you choose which tools Claude can use. Once connected, you can ask which of your projects contain a component named in an advisory, then cross check that list against the hosts your OBOM search turned up. The host side of the answer stays in your own OBOM store.
Click through it at your own pace, or open it full screen.
To try it on your own projects, download Safeguard at safeguard.sh/download and read the CLI documentation.
Quick check
Which CycloneDX field marks a BOM as an OBOM?
metadata.lifecycles, with a phase of operations. It was added in CycloneDX 1.5, and there is no separate OBOM schema.
Why can a non root OBOM collection look like a clean host?
If the collector cannot read the package database, those packages are simply missing from the output. A small component count can mean missing permissions rather than a lean system, so check counts and recorded degradations first.
Sources
- CycloneDX, Operations Bill of Materials (OBOM) capability
- CycloneDX, BOM-Link capability
- CycloneDX, Specification overview
- CycloneDX, Introducing OWASP CycloneDX v1.5
- CycloneDX specification releases on GitHub
- CycloneDX 1.4 JSON schema
- CycloneDX 1.5 JSON schema
- CycloneDX 1.6 JSON schema
- CycloneDX 1.7 JSON schema
- cdxgen README
- cdxgen, OBOM lessons for SOC, IR and compliance teams
- cdxgen, Permissions
- cdxgen, CLI options source (bin/cdxgen.js)
- cdxgen releases
- CISA, Types of Software Bill of Material (SBOM) Documents (PDF)
- CISA, Software Bill of Materials
- SPDX 3.0.1, SbomType vocabulary
Last reviewed: 24 October 2026.