sbom
Create a CycloneDX 1.6 JSON SBOM from your locked dependency tree. Does not evaluate licence policy.
sbom creates a CycloneDX 1.6 document that describes all locked
dependencies. It does not evaluate licence policy.
licence_audit sbom # pretty JSON to stdoutlicence_audit sbom --output=sbom.json # pretty JSON to a filelicence_audit sbom --offline # skip Hex fetch, omit licence fieldslicence_audit sbom --reproducible # reproducible outputlicence_audit sbom --vulns # embed OSV vulnerabilitieslicence_audit sbom --license-text # embed canonical SPDX licence textDocument contents
For each locked dependency, sbom creates a component with this information:
a package URL, a SHA-256 hash for Hex packages, the package description,
declared licences, and external references. The package URL is pkg:hex/...
for Hex and pkg:github/owner/repo@<commit> for GitHub git dependencies. The
external references can identify the tarball, source, home page, and
documentation. Project configuration supplies
information for the root component. A dependencies graph represents the
dependency tree.
The tool vendor identifies licence_audit’s publisher. The document omits
metadata.authors because the tool does not know who created the BOM.
For a Git dependency with a manifest path, the purl includes the encoded
package subpath, for example
pkg:github/example/beryl@<commit>#packages/beryl_protocol.
--reproducible reads gleam.toml only from that package subdirectory.
Missing package metadata is an error; repository-root metadata is not a fallback.
The immutable metadata cache key includes the package path.
All locked dependencies remain in the document. Components reachable from
[dependencies] in the project’s gleam.toml have scope required; development-only
components have scope optional. A valid file without [dependencies], or
with an empty table, makes all dependency components optional. Invalid TOML
or a dependencies value that is not a table causes exit code 2.
Each licence choice has the property acknowledgement: "declared" because the
data comes from package metadata, not a source scan. A single valid compound
SPDX declaration with AND, OR, or WITH becomes an expression choice.
Plain recognised IDs remain license.id entries. Custom names and malformed
expressions remain license.name entries.
If a package declares multiple licences, sbom creates one independent
license entry for each declaration, even if an entry contains an expression.
Hex does not specify an AND or OR relationship between entries, so sbom
does not combine them. Expression parsing is limited to 64 KiB, 1024 tokens,
and 64 nested parentheses; declarations above these limits remain names.
These are declared licences, not concluded licences from a source scan.
licence_audit does not inspect dependency source to conclude licences or
extract copyright holders. Use a source scanner when you need that evidence.
Embedded licence text
--license-text adds canonical SPDX License List text to recognised licence
IDs on dependencies and the root component:
{"license":{"id":"Apache-2.0","text":{"contentType":"text/plain","content":"..."},"acknowledgement":"declared"}}The text comes from the same fixed SPDX revision and shared cache as
notices, not from package source. It is reference text,
not package-specific copyright or attribution evidence. Default output does
not include this text.
IDs match without regard to case. Each declared ID receives its own text.
Expression choices and name entries have no embedded text. Custom
LicenseRef-* references and unknown names remain names. The command does
not select an expression alternative or combine texts.
This flag requires network access on a cache miss and conflicts with
--offline. It works with --reproducible because the SPDX revision is
fixed. Use --no-cache to bypass the text cache. --cache-path relocates the
metadata cache only; use XDG_CACHE_HOME to relocate both caches.
A failed text request, invalid SPDX response, or missing text for a recognised
ID exits 2 before writing the SBOM. Optional package metadata enrichment
remains unchanged: a dependency without licence metadata has no licence text.
Supported sources
sbom supports only hex and GitHub git sources. It fails on a
dependency source that it cannot convert to a valid purl. Unsupported sources
include path dependencies and non-GitHub git dependencies. --offline skips
licence requests but continues to validate purls. The vulns
command has different behavior: it identifies and skips unsupported sources.
Completion and optional enrichment
Default sbom generation exits 0 when optional Hex or local Git dependency
metadata is unavailable. It warns and omits the unavailable fields.
--offline intentionally skips Hex enrichment. This differs from a licence
audit, where unavailable package metadata makes the report incomplete (exit 2).
These items are required. If one fails, the command exits 2:
- root metadata,
- valid purls for supported sources,
- SBOM generation and file writes.
How --reproducible and --offline combine:
| Flags | Dependency metadata | If metadata is unavailable |
|---|---|---|
--reproducible |
Read from the locked source archives. | Exit 2. |
--reproducible --offline |
None. All enrichment, including local Git metadata, is skipped. Licence fields are omitted. | Not applicable. Can exit 0. |
Embedded vulnerabilities
--vulns queries OSV.dev and adds the results to the CycloneDX
vulnerabilities array. Each advisory has an id, an OSV source link,
ratings, and an affects list. The list refers to affected components by
bom-ref or purl. Tools such as Dependency-Track can read this
VEX-style document. Advisory data changes over time, so you cannot use
--vulns with --offline or --reproducible.
An OSV batch request failure exits 2. An individual advisory-detail failure
warns and retains its ID with unknown severity; generation still exits 0.
In contrast, check --vulns exits 2 when details are unavailable because it
cannot evaluate the severity gate.
Ratings use the advisory’s resolved severity, including none for a valid
CVSS score of 0.0. A recognised OSV database_specific.severity overrides
the vectors. Raw vectors and their CVSS methods remain in the document.
Reproducible output
By default, sbom uses a random serialNumber and the current time for
timestamp. The output from two runs is different. With
--reproducible without --offline, the command reads Hex metadata from each
checksum-verified package archive and Git metadata from the archive at the
locked commit. It omits mutable Hex publisher data, uses a content hash for
serialNumber, and gets the timestamp from SOURCE_DATE_EPOCH, or uses the Unix epoch if
the variable is not set. The same project files, lockfile, tool version,
options, and timestamp produce byte-identical output.
The resolved timestamp is part of the serial hash. A different timestamp
therefore produces a different serialNumber.
Locked source archives use the same archive limits as notices:
64 MiB per download and 256 MiB of tar data per extraction, including Hex’s
outer and inner archives. Archives at the limits are accepted. An oversized
download or invalid archive exits 2 when locked metadata is required.
The limits apply to archive sizes, not total VM memory.
The command reuses the shared metadata cache. Entries from
locked archives do not expire. Use --cache-path=DIR to override the cache
directory, or --no-cache to fetch the metadata again.
Flags
Both stdout and --output=PATH use pretty-printed JSON. When writing a file,
stdout stays empty.
Generate a CycloneDX 1.6 JSON SBOM from manifest.toml. Does not evaluate licence policy. Includes all dependencies with their CycloneDX scope. JSON output stays plain in every color mode.
Usage:
licence_audit sbom [--flags]Flags:
| Name | Type | Default | Description |
|---|---|---|---|
--cache-path |
STRING |
Override the licence metadata cache directory | |
--color |
STRING |
auto |
Colorize output: auto|always|never (default auto; alias: –colour) |
--license-text |
BOOL |
false |
Embed canonical SPDX text for recognised licence IDs (requires network on cache miss; conflicts with –offline) |
--manifest |
STRING |
Read manifest from PATH | |
--no-cache |
BOOL |
false |
Bypass the on-disk caches |
--offline |
BOOL |
false |
Skip Hex metadata fetch; with –reproducible, skip all dependency enrichment including Git |
--output |
STRING |
Write SBOM to PATH instead of stdout | |
--quiet |
BOOL |
false |
Suppress progress output (alias: -q) |
--reproducible |
BOOL |
false |
Reproducible output from locked package archives (unless –offline): serialNumber is a content hash and timestamp comes from SOURCE_DATE_EPOCH (default 1970-01-01T00:00:00Z) |
--verbose |
BOOL |
false |
Show detailed progress output (alias: -v) |
--vulns |
BOOL |
false |
Query OSV.dev and embed a CycloneDX vulnerabilities array (requires network; conflicts with –offline and –reproducible) |