Vendored SBOM schemas + offline validation
Elpe emits one SBOM format: CycloneDX 1.5. The per-target lockfile
(elpe.<triple>.lock) is that CycloneDX document (graph included), and
elpe build -sbom writes elpe.<name>.build.cdx.json for a built closure.
Elpe does not emit SPDX: SPDX is a commodity conversion of CycloneDX, not a
package manager’s job (the downstream consumers — AboutCode/ScanCode/
VulnerableCode — ingest CycloneDX natively).
This directory vendors the official JSON schemas so we can prove — offline, in CI, with no network — that Elpe’s output conforms, and that its SPDX conversion is itself schema-valid. This is the SuperSafe WP Tx.4 “done when”: the SBOM passes an external schema validator.
Files
| File | Source | Notes |
|---|---|---|
cyclonedx-1.5.schema.json | github.com/CycloneDX/specification @ 1.5, schema/bom-1.5.schema.json | Elpe’s own output format; $refs the two files below by relative path |
spdx.schema.json | same, schema/spdx.schema.json | CycloneDX’s SPDX-license-expression enum (a $ref target) |
jsf-0.82.schema.json | same, schema/jsf-0.82.schema.json | CycloneDX’s signature schema (a $ref target) |
spdx-2.3.schema.json | github.com/spdx/spdx-spec @ tag v2.3, schemas/spdx-schema.json | validates the converted SPDX export, not an Elpe-emitted document |
The two CycloneDX $ref targets are vendored alongside the main schema and
wired into a local referencing registry (keyed by each file’s $id) so
validation never resolves a $ref over the network.
Running
# validate a lock (which IS the CycloneDX SBOM) AND its SPDX conversion:
./validate.sh --from-lock ../elpe.x86_64-unknown-linux-gnu.lock
# validate specific files: *.cdx.json / *.lock get the CycloneDX + SPDX-export
# treatment; *.spdx.json is validated as SPDX:
./validate.sh path/to/elpe.hello.build.cdx.json
# no args: every committed CycloneDX SBOM at the repo root
# (elpe.*.lock excluding the TOML *.outputs.lock, plus any *.cdx.json)
./validate.sh
validate.sh runs two nix-shells: validate.py (schema validation, in
python3Packages.jsonschema + referencing) and syft (the CycloneDX→SPDX
converter, Go — a CI/dev tool only, never on Elpe’s build path). The only host
requirement is Nix — no pip, no network. To run validate.py directly, make
jsonschema (>= 4.18, for the referencing registry API) importable yourself.
What is validated
For each CycloneDX document, validate.sh proves both:
- Conformance — the document validates against the official CycloneDX 1.5
schema. Covers the per-target locks (322-component
elpe.x86_64…etc., graph included) and build SBOMs (elpe build -sbom: the content-addressed closure, each node carrying itspkg:elpe/<name>@<hash>identity plus its upstreampkg:deb/ubuntu/...source PURL as anelpe:source:purlproperty). - Interop / SPDX export —
syft convertturns the CycloneDX into SPDX 2.3, which then validates against the official SPDX schema, with the dependency graph preserved (CycloneDXdependencies→ SPDXDEPENDENCY_OF). This is the concrete proof that “SPDX is just a conversion”, so Elpe needn’t emit it.
Nix-free CI path (dogfooding)
validate.sh / merge.sh pull their tools from Nix. The same three
operations also run with every tool provisioned by Elpe itself — the
dogfooding proof that Elpe can bootstrap its whole SBOM toolchain — through two
committed recipes, driven by ci-native.sh:
# needs `elpe` on PATH + a reachable backend (ELPE_BACKEND, default :50051);
# no Nix, no pip.
./ci-native.sh # both: validate+convert, then multi-arch merge
./ci-native.sh validate # examples/sbom-ci only
./ci-native.sh merge # examples/sbom-merge only
| Recipe | Does | Tools, all elpe-provisioned |
|---|---|---|
examples/sbom-ci | validate CDX 1.5 → syft convert to SPDX 2.3 → validate SPDX 2.3 | python3+jsonschema+referencing (Ubuntu plucky/main debs), syft (pinned GitHub .deb via deb_url) |
examples/sbom-merge | merge two per-arch locks → one multi-arch CycloneDX → validate | cyclonedx-cli (pinned GitHub binary via fetch_bin), python3+jsonschema+referencing |
Complementary, not redundant. This path is not hermetic — it drives a
running backend and fetches tool debs over the network — so it is the
counterpart to, not a replacement for, the hermetic nix flake check
(flake.nix checks.sbom-validate). The Nix gate proves the committed
SBOMs conform offline; ci-native.sh proves Elpe can provision the
toolchain with no Nix at all. Run the native path where a backend is
available (dev, a backend-equipped CI runner); run the Nix path in an air-gapped
check. The two recipes validate small committed sample locks (sample*.cdx.json)
rather than the full per-target locks — local_src excludes elpe.*.lock by
design, and gating the real locks is the Nix check’s job.