Skip to content

CRA-Compliant SBOMs in CycloneDX Format

This guide explains how to generate a Software Bill of Materials (SBOM) in a format that is CRA compliant. We will use nix-sbom-helper to add an output to your Nix Flake, that allows you to generate SBOMs continuously and as part of your CI builds.

This guide reflects our current interpretation, as of September 2026, of the EU Cyber Resilience Act (CRA) and supplemental material, such as Technical Guideline TR-03183 published by the German BSI. It is not legal advice.

Note

The CRA requires manufacturers to draw up an SBOM in 'a commonly used and machine-readable format'. Both SPDX and CycloneDX qualify. SBOMs need to be regenerated whenever a component changes, thus we focus on implementing the infrastructure for continuous SBOM generation as part of your development process.

SBOMs as Flake Output in 1 Minute

With Nix SBOM helper, you can generate SBOMs for your Nix Flake outputs with three additional lines.

Add the following to your project's flake.nix:

{
  inputs = {
    # your other inputs here
    nix-sbom-helper.url = "github:cyberus-technology/nix-sbom-helper";
  };
  outputs = {
    self
    # your other stuff here
    , nix-sbom-helper
    , ...
   }: {
    # your outputs
    sboms = nix-sbom-helper.sbomsForFlakeOutputs self;
   };
}

Pin your dependencies with nix flake lock and check the flake.lock file into version control to make everything reproducible.

Now you can run (substitute with an actual output from your Flake):

$ nix build .#sboms.nixosConfigurations.<yourFavoriteSystem>

If your project follows the conventions from Flakes, using nix-sbom-helper.sbomsForFlakeOutputs should be sufficient. Otherwise amend your expressions to use the buildSbom function on your outputs.

Step-by-Step Example

Note

You'll need a Nix installation with flakes enabled to follow along. If your project does not use Flakes, follow the nix-sbom-helper documentation instead.

Your Cyberus Linux System Configuration

This guide assumes you have a flake.nix that defines the your embedded device configuration. A minimal example looks as follows:

{
  description = "Minimal Cyberus Linux system configuration";
  inputs = {
    nixpkgs.url = "https://channels.cyberus-linux.com/channel/cyberus-linux-26.05.tar.xz";
  };
  outputs =
    { self
    , nixpkgs
    , ...
    }:
    let
      system = "x86_64-linux";
    in
    {
      nixosConfigurations = {
        "cyberus-linux-device" = nixpkgs.lib.nixosSystem {
          inherit system;
          modules = [
            ./configuration.nix
          ];
        };
      };
    };
}

Add the Instrumentation

Add the nix-sbom-helper flake input and use the sbomsForFlakeOutputs helper in your configuration:

{
  description = "Minimal Cyberus Linux system configuration with SBOM instrumentation";
  inputs = {
    nixpkgs.url = "https://channels.cyberus-linux.com/channel/cyberus-linux-26.05.tar.xz";
    # ➊ Add the `nix-sbom-helper` input.
    nix-sbom-helper.url = "github:cyberus-technology/nix-sbom-helper";
  };
  outputs =
    { self
    , nixpkgs
    , # ➋ Use the `nix-sbom-helper` input.
      nix-sbom-helper
    , ...
    }:
    let
      system = "x86_64-linux";
    in
    {
      nixosConfigurations = {
        "cyberus-linux-device" = nixpkgs.lib.nixosSystem {
          inherit system;
          modules = [
            ./configuration.nix
          ];
        };
      };
      # ➌ Add the `sboms` output.
      sboms = nix-sbom-helper.sbomsForFlakeOutputs self;
    };
}

Build the SBOM

Now you can use a simple nix build to generate the SBOMs:

$ nix build .#sboms.nixosConfigurations.cyberus-linux-device
[...]

$ ls -l result/
total 9304
-r--r--r-- 2 root root 3034413 Dec 31  1969 <sha256-hash-redacted>-nixos-system-cyberus-linux-YY.MM.yyyymmdd.dirty.cdx.json
-r--r--r-- 2 root root 1007169 Dec 31  1969 <sha256-hash-redacted>-nixos-system-cyberus-linux-YY.MM.yyyymmdd.dirty.csv
-r--r--r-- 2 root root 5482030 Dec 31  1969 <sha256-hash-redacted>-nixos-system-cyberus-linux-YY.MM.yyyymmdd.dirty.spdx.json

This generates multiple formats. For general CRA compliance either CycloneDX or SPDX are acceptable.

Warning

The CRA only provides high-level guidance. Local authorities may have stricter requirements than the CRA, e.g. the German BSI has published Technical Guideline TR-03183 which has stricter requirements than the CRA.

Compliance with TR-03183 is Work in Progress at the moment.

For CRA compliance you must retain the SBOMs for at least 10 years after your product is placed on the market (or for the support period, whichever is longer) and you must continuously update the SBOM during the support period. We recommend generating SBOMs as part of your CI and archiving them properly for long-term storage.

Generate SBOMs with GitLab CI

If you use GitLab to manage your Cyberus Linux configuration, you can utilize GitLab CI to automatically generate the SBOM.

Info

We use the git commit SHA to identify the SBOM in this example. Consider using a release version or build number to make it easier to consume the SBOM downstream.

Use the following stage definition as a starting point.

build:sbom:
  stage: build
  interruptible: true
  image: nixos/nix:2.31.0
  script: |
    nix build .#sboms.nixosConfigurations.cyberus-linux-device
    # Move SBOM to a predictable name, used as an artifact.
    cp -v result/*.cdx.json sbom-${CI_COMMIT_SHA}.cdx.json
  artifacts:
    expire_in: never
    paths:
      - sbom-${CI_COMMIT_SHA}.cdx.json

Once your SBOMs are generated as part of the pipeline, ensure you implement proper archiving for compliant long-term storage. Since this is highly dependent on your organization's setup it is out of scope for this guide.

Note

Consider signing the SBOM as recommended by BSI TR-03183.