mikronova/modgunn
.nornir/sbom-guide.md
# sbom — guide
> **Class:** `guide` · audience: someone emitting a CycloneDX SBOM. The *what* is in
> [`sbom-design.md`](sbom-design.md).
## f0 — emit a CycloneDX 1.5 document
```rust
use modgunn::sbom::{to_cyclonedx, SbomComponent, SbomInput, SbomVuln};
let input = SbomInput {
subject: "my-app".into(),
components: vec![
SbomComponent { ecosystem: "cargo".into(), name: "serde".into(),
version: "1.0.0".into(), license: Some("MIT".into()) },
],
vulns: vec![
SbomVuln { ecosystem: "cargo".into(), name: "some-crate".into(), version: "1.2.3".into(),
ids: vec!["RUSTSEC-2024-0001".into()], summary: "use-after-free".into() },
],
};
let doc = to_cyclonedx(&input); // serde_json::Value (CycloneDX 1.5)
println!("{}", serde_json::to_string_pretty(&doc)?);
```
## f1 — build a purl directly
```rust
let p = modgunn::sbom::purl("cargo", "serde", "1.0.0"); // "pkg:cargo/serde@1.0.0"
```
## Gotchas
- This is the **emit** side only; to *read* the resolved component set for a repo use
`Warehouse::read_sbom_components` (see [`warehouse-guide.md`](warehouse-guide.md)).
- Output is byte-compatible with nornir's prior cargo SBOM — nornir's `to_cyclonedx` delegates
here, so keep the field shapes stable.