Skip to content
Molto

Bill of materials

molto metadata writes a CycloneDX 1.6 document with no timestamp, so two runs over one graph produce identical bytes.

molto metadata answers one question: what is inside this binary, where did it come from, and under what licence.

molto metadata                  # CycloneDX 1.6 to stdout
molto metadata -o sbom.json     # to a file
molto metadata --include-dev    # also what only [dev-deps] reaches

The output is CycloneDX 1.6 JSON, which Dependency-Track, Grype and Trivy read without translation.

What it contains

Section What is in it
metadata.component Your package: name, version, and whatever [package] declares
components[] Every package the build links, one entry each
dependencies[] The edges, including your own package’s

Each component carries its exact version, the licence its recipe declared, the SHA-256 of the bytes that were fetched, and a molto:source property holding the origin with its revision already resolved — registry+https://…, git+https://…#5a1e8ff…, path+modules/http.

Three things worth knowing

A path dependency has no checksum and no version. Its bytes are whatever is on disk, which is the point of one. It appears with molto:unverified = true, and molto metadata says so on stderr. Nothing is silently omitted: a bill of materials that hides what it could not verify is worse than none.

Development dependencies are out by default. A bill of materials describes what ships. --include-dev adds them with "scope": "excluded" — CycloneDX for “in the graph, not in the artifact”.

The document has no timestamp, and no serialNumber either. Two runs over the same graph produce the same bytes, so it can be committed, diffed and compared between machines. Both fields are optional in the schema, so what comes out is still a conforming 1.6 document.

Where the licences come from

From [package] for your own package, and from each dependency’s recipe.toml [about] table for the rest. A dependency whose recipe states no licence gets no licenses field — an empty string would be a claim, and silence is not one.

If a component in your report has no licence, the fix is upstream: the recipe that publishes it needs an [about] table.

What it costs

Resolving the graph, which is what reads the recipes. In a project that has already been built this touches no network — a published coordinate never changes, so the registry’s previous answer still stands and the sources are already in ~/.molto/cache. In a fresh clone it fetches, exactly as a build would.

It does not read Molto.lock. The lock records versions, origins and checksums and deliberately not licences: that fact already lives in each recipe, and a lock file storing it twice could contradict itself.