Expand description
CycloneDX SBOM emission — a build-time Software Bill of Materials for a
compiled ELF. Companion to safety_manifest.rs: where the safety manifest
records how the binary is hardened, the SBOM records what went into it.
§Scope
synth is a compiler, not a linker, so this is a build SBOM — it documents the compilation transaction, not a transitive dependency graph:
metadata.tools— the synth compiler itself (“what built it”).- the input WASM module — a component with SHA-256 + byte size.
- the output ELF binary — a component with SHA-256, size, target triple, and the backend that produced it.
- the WASM module’s imports — each imported function/module/memory/etc.
becomes a component, and the output ELF
dependsOneach of them. This is the closest synth can get to “what’s in the software” without a full linker view.
Explicitly NOT in scope: full transitive scanning of the WASM module,
AIBOM/ML-BOM. See docs/sbom.md.
§rivet #107 linkage
The emitted document is CycloneDX 1.5 JSON. The sibling PulseEngine repo
rivet (issue #107) defines an sbom-record artifact type that ingests a
CycloneDX SBOM:
- id: SBOM-vehicle-control-v1
type: sbom-record
format: cyclonedx
sbom-ref: "sbom/vehicle-control-v1.0.0.cdx.json"
component-count: 142The file synth writes here is exactly what rivet import --format cyclonedx consumes, becoming one sbom-record in the rivet traceability
chain.
§Determinism
For a fixed set of inputs the document is byte-stable except for
metadata.timestamp, which is wall-clock by design (a CycloneDX SBOM
records when the build happened). The serialNumber is derived
deterministically from the output ELF’s SHA-256, so it too is stable for a
given binary.
#1292 (RQ-68-REPRO): when SOURCE_DATE_EPOCH is set the timestamp is THAT
instant instead of the wall clock — the reproducible-builds.org convention,
which keeps the by-design meaning (“when this was built”) while letting two
builds of the same source produce the same SBOM. A malformed value is an
error, as that specification asks, never a silent fall-back to the clock.
#1293: metadata.properties records every SYNTH_* environment variable
SET for the compilation (synth:build-env:<NAME>). A census of the variables
non-test source reads found 14 of 36 that change emitted bytes on a 60-module
sample — a lower bound, since it covered three configs and the values 1 and
0 only — so the list is read from the environment at build time rather than
hard-coded, and cannot drift when a lever is added. When none is set the
field is omitted, so a default build’s SBOM is byte-identical to before.
Path convention: when the compiler emits foo.elf, the SBOM is written to
foo.cdx.json next to it.
Structs§
- Component
- A CycloneDX
component. - Cyclone
DxSbom - A complete CycloneDX 1.5 SBOM document.
- Dependency
- A CycloneDX
dependenciesgraph node. - Hash
- A CycloneDX
hashesentry. - Property
- A CycloneDX
propertiesentry — a namespaced key/value pair. - Sbom
Inputs - Inputs needed to construct a build SBOM. Grouped into a struct so the constructor signature stays readable as the SBOM scope grows.
- Sbom
Metadata - CycloneDX
metadatablock. - Tool
- CycloneDX
metadata.toolsentry — “what built it”.
Functions§
- build_
timestamp_ rfc3339 - RFC-3339 UTC timestamp for “now”, e.g.
2026-05-21T10:30:00Z. - now_
rfc3339 - synth_
build_ environment - #1293: every
SYNTH_*variable in the current process environment, sorted. - timestamp_
from_ source_ date_ epoch - Pure core of
build_timestamp_rfc3339, so it is testable without mutating the process environment.