background grid image
Image for post Introducing FlakeBOM, a CLI for generating SBOMs from Nix flakes
Jun 11, 2026 by Dominique Hummel

Introducing FlakeBOM, a CLI for generating SBOMs from Nix flakes

Earlier this year, we introduced Determinate Secure Packages, our first major effort to revolutionize the software supply chain. In a nutshell, it’s a curated package set based on Nixpkgs plus a wide variety of industry-leading features, including SLA-backed CVE remediation, optional FIPS support, and full cache coverage in FlakeHub Cache.

From the beginning, Determinate Secure Packages has provided Nix-powered orgs a secure foundation for their developer workstations, development environments, CI pipelines, production OCI containers and NixOS systems, and more. Today, we’re taking things another step forward by enabling you to derive a wealth of new information about the software that you build on top of that foundation. As its lead developer, I’m pleased to announce the initial release of FlakeBOM, a CLI for generating SBOMs from Nix flakes.

In this post, I’ll tell you what FlakeBOM does, list some of the challenges we faced when building it, and describe how our customers can benefit from the tooling we’ve built around Determinate Secure Packages.

The “what” problem

One of the most difficult things about hardening a piece of software, be it a standalone application or a deployment of an OS, is actually figuring out all the parts that are involved in the process. Though it might seem trivial, there’s a lot more to it than just parsing a lockfile or scanning a container image. Build tools, to give an example, might not end up being shipped in the final product, but they can still play an important part in the security of your software.

The problem space is so complex that there are several projects, both proprietary and open source, whose primary goal is to tell you about the software you’re actually depending on during the various stages of your pipeline. Trivy is a popular example of this. Fortunately, when it comes to Nix, a lot of the heavy lifting is already done as part of Nix’s core design. A simple nix path-info -rS <store path or flake ref> goes a long way toward showing you what your software depends on, but there’s still a ways to go from store path strings to a useful, spec-compliant SBOM.

As a quick note, you’ll see me use the term SBOM (an acronym for Software Bill of Materials) a lot during this post. I won’t go into too much detail because it’s an entire topic in itself, but the gist is that it’s a document designed to list every piece of software involved in a particular end product. In our case, we follow the CycloneDX v1.5 specification in JSON format.

Enter FlakeBOM

When thinking about the “what” problem, we considered making use of some existing off-the-shelf solutions for SBOM generation within the Nix ecosystem. sbomnix and bombon are both great options in this space, but we needed something that can be run on any flake, without any special integration, and can generate an SBOM covering all flake outputs as opposed to just a single derivation.

That’s why we decided to roll our own. The great side effect is that this also allows us to make better use of the additional metadata provided by both Determinate Secure Packages and Determinate Nix’s provenance data. Additionally, we needed something performant that could generate SBOMs spanning many tens of thousands of components, including their dependency graphs, call stack frames, and other metadata.

And so we built FlakeBOM, a brand new CLI written in Rust that directly integrates with Determinate Nix’s C API. This integration results in minimal overhead compared to invoking the nix command, while still enabling it to make full use of powerful Determinate Nix features like parallel evaluation and provenance.

When you run flakebom, its default behavior is to generate an SBOM from the flake in the directory it was called from.

Using FlakeBOM inside a flake
cd /path/to/my/flake
flakebom

It does this by inspecting your flake’s schema and walking through all of the derivations that it comes across. For every derivation listed within your schema, it then recursively collects all of its dependencies.

Within the final SBOM, derivations are represented as “components” with the base data, including their store path, package name, and version (if applicable). Here’s an example of a typical component entry generated by FlakeBOM:

{
"type": "library",
"bom-ref": "/nix/store/y5807xbxfkw654yaqd1ma1hyrfbcv0a8-unzip-6.0.drv",
"name": "unzip",
"version": "6.0",
"description": "Extraction utility for archives compressed in .zip format",
"licenses": [
{
"expression": "Info-ZIP"
}
],
"modified": true,
"pedigree": {
"patches": [
214 collapsed lines
{
"type": "unofficial",
"resolves": [
{
"type": "security",
"id": "CVE-2014-8139",
"name": "CVE-2014-8139",
"source": {
"name": "NVD",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2014-8139"
}
}
]
},
{
"type": "unofficial",
"resolves": [
{
"type": "security",
"id": "CVE-2014-8140",
"name": "CVE-2014-8140",
"source": {
"name": "NVD",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2014-8140"
}
}
]
},
{
"type": "unofficial",
"resolves": [
{
"type": "security",
"id": "CVE-2014-8141",
"name": "CVE-2014-8141",
"source": {
"name": "NVD",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2014-8141"
}
}
]
},
{
"type": "unofficial",
"resolves": [
{
"type": "security",
"id": "CVE-2014-9636",
"name": "CVE-2014-9636",
"source": {
"name": "NVD",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2014-9636"
}
}
]
},
{
"type": "unofficial",
"resolves": [
{
"type": "security",
"id": "CVE-2015-7696",
"name": "CVE-2015-7696",
"source": {
"name": "NVD",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2015-7696"
}
}
]
},
{
"type": "unofficial",
"resolves": [
{
"type": "security",
"id": "CVE-2015-7697",
"name": "CVE-2015-7697",
"source": {
"name": "NVD",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2015-7697"
}
}
]
},
{
"type": "unofficial",
"resolves": [
{
"type": "security",
"id": "CVE-2014-9913",
"name": "CVE-2014-9913",
"source": {
"name": "NVD",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2014-9913"
}
}
]
},
{
"type": "unofficial",
"resolves": [
{
"type": "security",
"id": "CVE-2016-9844",
"name": "CVE-2016-9844",
"source": {
"name": "NVD",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2016-9844"
}
}
]
},
{
"type": "unofficial",
"resolves": [
{
"type": "security",
"id": "CVE-2018-18384",
"name": "CVE-2018-18384",
"source": {
"name": "NVD",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2018-18384"
}
}
]
},
{
"type": "unofficial"
},
{
"type": "unofficial",
"resolves": [
{
"type": "security",
"id": "CVE-2019-13232",
"name": "CVE-2019-13232",
"source": {
"name": "NVD",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2019-13232"
}
}
]
},
{
"type": "unofficial",
"resolves": [
{
"type": "security",
"id": "CVE-2019-13232",
"name": "CVE-2019-13232",
"source": {
"name": "NVD",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2019-13232"
}
}
]
},
{
"type": "unofficial",
"resolves": [
{
"type": "security",
"id": "CVE-2019-13232",
"name": "CVE-2019-13232",
"source": {
"name": "NVD",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2019-13232"
}
}
]
},
{
"type": "unofficial"
},
{
"type": "unofficial",
"resolves": [
{
"type": "security",
"id": "CVE-2022-0530",
"name": "CVE-2022-0530",
"source": {
"name": "NVD",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2022-0530"
}
},
{
"type": "security",
"id": "CVE-2022-0529",
"name": "CVE-2022-0529",
"source": {
"name": "NVD",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2022-0529"
}
}
]
},
{
"type": "unofficial"
},
{
"type": "unofficial",
"resolves": [
{
"type": "security",
"id": "CVE-2021-4217",
"name": "CVE-2021-4217",
"source": {
"name": "NVD",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2021-4217"
}
}
]
}
]
},
"externalReferences": [
{
"type": "website",
"url": "http://www.info-zip.org"
},
{
"type": "build-meta",
"url": "file:///nix/store/kr185f35ipbfin6dz324iqx30hqar6x1-source/pkgs/by-name/un/unzip/package.nix:117"
}
],
"properties": [
16 collapsed lines
{
"name": "nix:narinfo:deriver",
"value": "/nix/store/y5807xbxfkw654yaqd1ma1hyrfbcv0a8-unzip-6.0.drv"
},
{
"name": "nix:narinfo:store_path",
"value": "/nix/store/v1v33fn6gn9srx2d22fbiiyl5ssyq792-unzip-6.0"
},
{
"name": "syft:package:type",
"value": "nix"
},
{
"name": "nix:narinfo:references",
"value": "/nix/store/1k1wn8807yizgz3ghnbd4k6zsc0dzfkr-CVE-2014-9913.patch /nix/store/2cq4hsc1v8ylccspw8351r72s56w1fia-CVE-2015-7697.diff /nix/store/54s896iyh3ixmh7nwbrgk12vwcx9r9h1-stdenv-linux.drv /nix/store/5z0x69dr11bzbai07xx9884vrn23qblv-bzip2-1.0.8.drv /nix/store/6np2acjv1nxcg0xzsv9a76wyrpxznkna-CVE-2014-8141.diff /nix/store/6zqn6w9rwkgfa6z1hpagnh5xhz2dag6m-CVE-2015-7696.diff /nix/store/705g035ind4dzrnxwjaw5gp6wy32bkar-implicit-declarations-fix.patch /nix/store/97d26l91h0db8h0qkmhxwi5d8shrilv6-CVE-2016-9844.patch /nix/store/aqzmd8va5a2333slqx0948mrd4wq73kj-CVE-2019-13232-2.patch.drv /nix/store/b46z951hms2p35wwryf1vadra0gxisn1-28-cve-2022-0529-and-cve-2022-0530.patch.drv /nix/store/cciw7lgkldvx25d77cxpjhh1iw4xghd9-setup-hook.sh /nix/store/d9b2qrrq32jzdsdx4y33inzrra5n5z5n-CVE-2014-8140.diff /nix/store/h2chxyk2qv4r6gh7dcmris3lz3v1vdvg-CVE-2019-13232-3.patch.drv /nix/store/hid24pdxx1fjffk8fc1fpdx7isw3b5ra-bash-5.3p3.drv /nix/store/l622p70vy8k5sh7y5wizi5f2mic6ynpg-source-stdenv.sh /nix/store/na3i78df0mypyxg0yqrgfqkfb0a7m83x-06-initialize-the-symlink-flag.patch.drv /nix/store/nb5wh9pdb3mrl5nbxlskfpxxh0ghpfil-CVE-2019-13232-1.patch.drv /nix/store/p2bwsjwmr3r985z5dj0llqcamqjbqp9f-unzip60.tar.gz.drv /nix/store/p46prhgmv7ibjh9igfkbc6zfxbbi6sk5-dont-hardcode-cc.patch /nix/store/p55a764pi2f4kkx3adb43bxb2dnb4z6r-CVE-2018-18384.patch /nix/store/pdcj2chp5c2gvm2jc3shbajfc62kbx1i-CVE-2014-9636.diff /nix/store/rdkdki1f24q8mqgnbsyk7gmh28c027ks-CVE-2014-8139.diff /nix/store/shkw4qm9qcw5sc5n1k5jznc83ny02r39-default-builder.sh /nix/store/w3pm1ypv02i8skmnv1h1iagpqf3r75ll-CVE-2021-4217.patch"
}
],
"evidence": {
37 collapsed lines
"callstack": {
"frames": [
{
"module": "checks"
},
{
"module": "x86_64-linux"
},
{
"module": "py"
},
{
"module": "propagatedBuildInputs"
},
{
"module": "[0]"
},
{
"module": "buildInputs"
},
{
"module": "[3]"
},
{
"module": "buildInputs"
},
{
"module": "[2]"
},
{
"module": "nativeBuildInputs"
},
{
"module": "[0]"
}
]
}
}
}

As you can imagine, this approach already covers a good amount of information, though even a store-based approach is susceptible to certain blind spots. The most common example involves derivations that bundle together multiple dependencies. Think of utilities like fetchNpmDeps for JavaScript projects and fetchCargoVendor for Rust projects. These produce single derivations that contain all of a particular package’s language-specific dependencies, but they would end up as single components within the SBOM, since Nix doesn’t know anything about the contents of a derivation at evaluation time.

This is where FlakeBOM’s direct access to the Nix C bindings comes in handy. We’ve built FlakeBOM with the ability to detect derivations that are candidates for containing these types of “vendored dependencies” and realise them on demand. Afterwards, FlakeBOM parses the individual lockfile(s) and includes all packages it finds as sub-components of the given derivation within the SBOM.

As a bonus, since packages from sources like cargo, npm, pypi, and similar platforms are easily identifiable, FlakeBOM automatically includes the associated pURL within the SBOM, which vulnerability scanners like Trivy can then use to great effect. This also works for regular derivations. Here’s a (truncated) example of what this looks like:

{
"type": "library",
"bom-ref": "/nix/store/f528wwa3dx6jj38x50lh8jb44i1a0p3f-npm-deps.drv",
"name": "npm-deps",
"modified": false,
"pedigree": {
"patches": []
},
"properties": [
16 collapsed lines
{
"name": "nix:narinfo:deriver",
"value": "/nix/store/f528wwa3dx6jj38x50lh8jb44i1a0p3f-npm-deps.drv"
},
{
"name": "nix:narinfo:store_path",
"value": "/nix/store/ba46gxkpm5lgp1icw4nipzb84lk90245-npm-deps"
},
{
"name": "syft:package:type",
"value": "nix"
},
{
"name": "nix:narinfo:references",
"value": "/nix/store/0pb4rqqfr380p7qjgq76n4rjdszxz7h8-prefetch-npm-deps-0.1.0.drv /nix/store/6by1j0c8lflw0k7qc55f0xqqcn0c6xym-zstd-1.5.7.drv /nix/store/9hqj01wgmvkyc6a96a7v0l2ybaqsm30j-stdenv-linux.drv /nix/store/h3xkspap93l2qd4cvb56f1zsf3j3yyg5-bash-5.3p9.drv /nix/store/l622p70vy8k5sh7y5wizi5f2mic6ynpg-source-stdenv.sh /nix/store/shkw4qm9qcw5sc5n1k5jznc83ny02r39-default-builder.sh /nix/store/v34as0sb64zgknq0ip0safhcq4v5yfcc-source.tar.zstd.drv"
}
],
"components": [
{
"type": "library",
"name": "@aashutoshrathi/word-wrap",
"version": "1.2.6",
"purl": "pkg:npm/%40aashutoshrathi/word-wrap@1.2.6"
},
{
"type": "library",
"name": "@azure/msal-browser",
"version": "4.2.1",
"purl": "pkg:npm/%40azure/msal-browser@4.2.1"
},
// ...
],
"evidence": {
"callstack": {
20 collapsed lines
"frames": [
{
"module": "checks"
},
{
"module": "x86_64-linux"
},
{
"module": "chromium-fips"
},
{
"module": "/nix/store/kyl5vwg5s6dkgf03lpc4cmaw5znqwz9h-chromium-147.0.7727.137.drv"
},
{
"module": "/nix/store/bnf1v153b9s046n2lliv7ivmgcf0ml0m-chromium-unwrapped-147.0.7727.137.drv"
},
{
"module": "/nix/store/f528wwa3dx6jj38x50lh8jb44i1a0p3f-npm-deps.drv"
}
]
}
}
},

This makes FlakeBOM a prime candidate for auditing not just Nix-related projects but also any project that uses Nix as its build system. As of this writing, FlakeBOM supports the discovery of vendored dependencies for fetchNpmDeps, fetchPnpmDeps, and fetchCargoVendor, as well as the crane build system.

FlakeBOM is also incredibly performant! We routinely run it on the entire Determinate Secure Packages secure subset, which includes over 10,000 individual components at the time of writing (not including vendored dependencies), where FlakeBOM manages to generate an SBOM that is ~60MiB in size in under two minutes. This is made possible by Determinate Nix’s parallel evaluation.

Better metadata coverage

Collecting components is great, and collecting them quickly is even better, but we also need to make sure not to sacrifice quality in favor of quantity. If you’re planning to feed the SBOMs generated by FlakeBOM against Nixpkgs into a traditional vulnerability scanner, you may find the results lacking. This is because upstream Nixpkgs is missing uniquely identifying information for most of its packages. Though there is an ongoing effort to improve this, it is not a top priority.

In Determinate Secure Packages, however, we rely on being able to correctly identify the packages within our secure subset. And so a core part of our workflow is to add identifiers, such as CPEs, to packages within our secure subset.

In cases where we manually mitigate a vulnerability, recommend a workaround, or otherwise seek to provide additional information, we add this information directly to the package metadata within Determinate Secure Packages. We’ve built FlakeBOM with support for this, so when you run it against Determinate Secure Packages or downstream flakes using it as their nixpkgs input, this information is included as part of the SBOM’s Vulnerability Exploitability eXchange information. Here’s an excerpt from one of our recent SBOMs for Determinate Secure Packages:

{
"id": "CVE-2026-43213",
"source": {
"name": "NVD",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-43213"
},
"workaround": "If Realtek RTL8852BE/RTL8852CE WiFi hardware is present, prevent the rtw89_pci module from being loaded.",
"advisories": [
{
"title": "Upstream fix",
"url": "https://git.kernel.org/linus/957eda596c7665f2966970fd1dcc35fe299b38e8"
},
{
"title": "NVD",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-43213"
},
{
"title": "RedHat",
"url": "https://access.redhat.com/security/cve/cve-2026-43213"
}
],
"analysis": {
"state": "in_triage",
"justification": "requires_environment",
"response": [
"workaround_available"
],
"detail": "rtw89_pci driver available (CONFIG_RTW89_PCI=m), but requires Realtek RTL8852BE/RTL8852CE WiFi hardware"
},
"affects": [
{
"ref": "/nix/store/ll1h3wnwyqy6zxbwm9rlj8cifncvczfl-linux-6.12.91.drv",
"versions": [
{
"range": "vers:nix/6.12.91",
"status": "unknown"
}
]
}
]
}

FlakeBOM also supports merging your own VEX metadata into the SBOM it generates in one single step, so feel free to bring your own analysis!

Closing thoughts

While there are still some areas of improvement, and more features to be added, we believe that FlakeBOM is ready for general adoption, and we ourselves use it in our workflows on a daily basis. For more details on FlakeBOM’s different features, as well as best practices, head on over to its dedicated documentation.

FlakeBOM is available today exclusively to our Determinate Secure Packages customers. If you’d like to see how our secure package set and FlakeBOM could fit into your infrastructure, you can schedule a demo or ask questions at sales@determinate.systems or find us on Discord.


Share
Avatar for Dominique Hummel
Written by Dominique Hummel

Dominique is a (mainly) Rust and Nix engineer who also has a strong passion for additive manufacturing and EDM. He is also an active contributor to Nixpkgs and maintainer of packages such as the Linphone suite and the Blackmagic kernel drivers.