# Per-Layer SBOM Export API ## Module Scanner ## Status VERIFIED ## Description Per-layer SBOMs stored as individual CAS artifacts with API endpoints to retrieve layer-specific SBOMs (GET /scans/{id}/layers, GET /scans/{id}/layers/{digest}/sbom with format param), content negotiation, immutable caching (ETag, Cache-Control), and CLI commands (stella scan layer-sbom, stella scan recipe). ## Implementation Details - **API Endpoints**: - `src/Scanner/StellaOps.Scanner.WebService/Endpoints/LayerSbomEndpoints.cs` - `LayerSbomEndpoints` with `GET /scans/{id}/layers` (list layers) and `GET /scans/{id}/layers/{digest}/sbom` (retrieve per-layer SBOM with format negotiation) - `src/Scanner/StellaOps.Scanner.WebService/Endpoints/ScanEndpoints.cs` - Scan endpoints integrating per-layer SBOM access - **Layer SBOM Service**: - `src/Scanner/StellaOps.Scanner.WebService/Services/ILayerSbomService.cs` - `ILayerSbomService` interface for layer SBOM operations - `src/Scanner/StellaOps.Scanner.WebService/Services/LayerSbomService.cs` - `LayerSbomService` manages per-layer SBOM retrieval with content negotiation (SPDX, CycloneDX) and immutable caching - `src/Scanner/StellaOps.Scanner.WebService/Services/SurfacePointerService.cs` - `SurfacePointerService` tracks surface-level pointers for layer SBOMs - **Layer SBOM Composition**: - `src/Scanner/__Libraries/StellaOps.Scanner.Emit/Composition/LayerSbomComposer.cs` - Composes per-layer SBOMs - `src/Scanner/__Libraries/StellaOps.Scanner.Emit/Composition/SpdxLayerWriter.cs` - SPDX format layer SBOM writer - `src/Scanner/__Libraries/StellaOps.Scanner.Emit/Composition/CycloneDxLayerWriter.cs` - CycloneDX format layer SBOM writer ## E2E Test Plan - [ ] Call `GET /scans/{id}/layers` and verify a list of container layers with digests and sizes is returned - [ ] Call `GET /scans/{id}/layers/{digest}/sbom?format=spdx` and verify a valid SPDX SBOM is returned for the specific layer - [ ] Call `GET /scans/{id}/layers/{digest}/sbom?format=cyclonedx` and verify a valid CycloneDX SBOM is returned - [ ] Verify ETag and Cache-Control headers are set for immutable caching of per-layer SBOMs - [ ] Verify content negotiation via Accept header works as an alternative to the format query parameter - [ ] Verify requesting a non-existent layer digest returns 404 --- ## Verification | Check | Result | |-------|--------| | Tier 0 - Source files exist | PASS | | Tier 1 - Build + code review | PASS | | Tier 2 - Integration tests | PASS | | Verified | 2026-02-13T18:10:00Z |