The pnpm sbom command generates a software bill of materials for a project from its lockfile. You must pick a format with --sbom-format: cyclonedx or spdx. Flags then control the root component type, dev dependency scope, and output path. It needs a lockfile, and a package manifest is optional.
What the command needs and produces
According to the pnpm documentation, a lockfile is mandatory. A package manifest is optional, but without one the root component is named unknown, gets version 0.0.0 and has no license. The command never creates a manifest for you.
Output is JSON. The documented formats are CycloneDX (current default spec version 1.7) and SPDX 2.3. You can ask for CycloneDX 1.5 or 1.6 with --sbom-spec-version if a downstream tool cannot read 1.7. That flag is just valid with CycloneDX.
The flags worth knowing
| Flag | What it does |
|---|---|
--sbom-format | Required. cyclonedx or spdx |
--sbom-type | library (default) or application for the root component |
--prod, -P | Just dependencies and optionalDependencies |
--dev, -D | Just devDependencies |
--no-optional | Leave out optionalDependencies |
--lockfile-just | Use lockfile data and skip reading the store |
--exclude-peers | Drop peer dependencies and anything reachable just through them |
--out | Write to a file instead of stdout |
Two flags carry metadata for your reader: --sbom-authors and --sbom-supplier, written to the CycloneDX metadata block.
Steps to produce a first file
- Confirm the lockfile is committed and current.
- Decide whether you ship an application or a library, and set
--sbom-typeto match. - Run the command with a format and an output path.
- Open the file and check the root component name, version and type.
- Compare the component count with what you expect from your lockfile.
For a deployed service, a production-just file is usually what a customer wants. For internal license review you may keep dev dependencies, because the CycloneDX output marks components reachable just through devDependencies with scope excluded and a development property. That means a reader can still filter them out.
Where to double check
The default type is library, so an application should set --sbom-type application. Otherwise the root component says something different from what you ship. Also remember that --lockfile-just skips the store, which is fine for a quick inventory but means the file reflects what the lockfile says, not what happens to be installed on a machine.
A lockfile based file is an inventory of declared packages. It does not tell you which packages your code actually calls. Reachability analysis is the step that does that, and it is separate from generation.
A worked example
Picture a small service called orders-api. It has a package.json with 12 direct dependencies and 9 dev dependencies, plus a committed lockfile. These numbers are an illustration, not a measurement of any real project.
You run the command twice. The first run uses --sbom-format cyclonedx with --prod and --sbom-type application. The second run drops --prod. Here is what a careful reviewer would expect to see.
| Run | Includes | What the reader learns |
|---|---|---|
| Production scope | dependencies and optionalDependencies, plus everything they pull in | What ships to users |
| Full scope | The above plus devDependencies | What the build and test tooling touch |
Open both files and compare three things. The root component should say orders-api with the version from package.json and type application. The component count in the second file should be larger. And components reachable solely through devDependencies should carry scope excluded and the development property, as the pnpm docs describe for CycloneDX output.
If the counts are identical, either you have no dev dependencies or the scope flag did not apply. Investigate before you send anything to a customer.
Choosing between CycloneDX and SPDX in this command
The command accepts either, so the real question is who reads the file. Ask your recipient. If they say either is fine, many teams pick CycloneDX for application SBOMs because it has a direct way to mark component scope and to describe dependency relationships, and SPDX when a customer's process is built around licenses. Whichever you choose, write the format and version in your cover note, for example CycloneDX 1.6 or SPDX 2.3.
If a downstream tool cannot read the newest CycloneDX version, set --sbom-spec-version to 1.6 or 1.5. Keep that flag in your script so the choice is visible to the next person.
Common failure modes and how to spot them
- No lockfile. The command needs one. A missing lockfile means the command cannot run, so fix the repo, not the flags.
- Wrong root type. The default is library. A deployed service described as a library confuses readers about what you ship.
- Missing manifest. With no package.json, the root is unknown with version 0.0.0 and no license. A customer who sees that will ask what the file describes.
- Stale lockfile. If package.json changed and the lockfile did not, the SBOM describes the old tree. Run your install with a frozen lockfile in CI to catch this.
- Store versus lockfile differences. By default the command can read the store, while
--lockfile-solelydoes not. Pick one on purpose.
What to record next to the file
An SBOM without context ages badly. Keep a short note beside each file: the date, the commit hash, the pnpm version, the flags you used, and the person or job that ran it. When someone asks in a year why the count changed, that note answers the question in a minute.
From file to decision
Generating the file is step one. Step two is turning it into decisions: which components have known advisories, which of those your code actually reaches, and which fixes are cheap. A scan of the file gives you the first answer. Reachability analysis helps with the second. Keep the SBOM as the input to that process, not the end of it.
Try it yourself
Click through it at your own pace, or open it full screen.
Click into a component and look at how direct and transitive packages are separated.
Where Safeguard fits
Once you have a file, Safeguard can scan the dependencies and SBOM for known issues, and the free scan at gold.safeguard.sh/scan is a quick way to see results for one project.
Quick check
Does pnpm sbom work without a package.json?
Yes, if a lockfile exists. The root component is then named unknown with version 0.0.0 and no license, so add a manifest if the root should say something useful.
Can I get CycloneDX 1.6 instead of 1.7?
Yes. Use --sbom-spec-version 1.6 with the CycloneDX format. The flag was added in pnpm 11.1.0 per the docs.
Does pnpm sbom scan for vulnerabilities?
No. The command produces an inventory. Vulnerability matching is a separate step you run on the file.
Sources
Last reviewed: 19 October 2026.