Skip to content

feat(downloads): verify versions from image SBOMs - #762

Closed
castrojo wants to merge 30 commits into
mainfrom
fix/download-pages
Closed

feat(downloads): verify versions from image SBOMs#762
castrojo wants to merge 30 commits into
mainfrom
fix/download-pages

Conversation

@castrojo

Copy link
Copy Markdown
Contributor

Summary

  • add the For the Wolves download cards for Dakota, Utah, and Bluefin Server using the shared raptor-card presentation
  • source Bluefin and Dakota version claims from verified published-image SBOM evidence with atomic, fail-closed updates and issue reporting
  • remove unverifiable Flatcar-derived Bluefin Server data and retain an honest GitHub releases CTA with explicit SBOM status

Validation

  • npm run lint (0 errors; existing JSDoc warnings)
  • npm run typecheck
  • npm run test:gate (no new failures; 1222 tests)
  • npm run build
  • focused SBOM/download tests
  • desktop and mobile Chromium checks for /, /dakota/, and /server/, including zero /server-versions.json requests

Notes

  • /dakota/ and /server/ remain unlisted production entry points.
  • Bluefin Server displays no component versions until it publishes a verifiable image SBOM.

Jorge Castro and others added 30 commits August 26, 2026 01:07
Assisted-by: GPT-5.6 Sol via GitHub Copilot CLI

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Assisted-by: GPT-5.6 Sol via GitHub Copilot CLI

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Assisted-by: GPT-5.6 Sol via GitHub Copilot CLI

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Digest-bound ORAS/Cosign collector: resolveImageDigest, discoverReferrers,
verifyImageProvenance, pullSpdxReferrer, collectVerifiedImageSbom.
EvidenceError with codes: image-not-found, missing-provenance,
invalid-provenance, missing-sbom, ambiguous-sbom, invalid-sbom.
Deterministic ORAS fixtures for dakota (SPDX+provenance) and gaming (no SPDX).

Assisted-by: Claude Sonnet 4.5 via GitHub Copilot CLI

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Assisted-by: Claude Sonnet 4.5 via GitHub Copilot CLI

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Reverts pre-existing download-page changes that were accidentally
included in 13d14f1 and the agent-workflow addition from ad5de8c.
Task 2 collector files are untouched.

Assisted-by: Claude Sonnet 4.5 via GitHub Copilot CLI

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- verifyImageProvenance: remove || true; classify no-attestation messages as
  missing-provenance, all other cosign failures as invalid-provenance
- discoverReferrers: wrap command failure as image-not-found; wrap JSON
  parse failure as invalid-sbom
- pullSpdxReferrer: single finally block removes tempdir exactly once
- Tests: replace double-invocation error-code patterns with single
  toThrow(expect.objectContaining(...)); add invalid-provenance, image-not-found,
  and malformed-JSON tests; assert rmSync call count is exactly 1

Assisted-by: Claude Sonnet 5 via GitHub Copilot CLI

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Assisted-by: claude-sonnet-5 via GitHub Copilot CLI

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- spdxPackageVersion: remove highest-version fallback; return undefined on ambiguity
- compareVersions: parseInt instead of Number so '8-ogc1' parses as 8 not NaN
- Fixture hash replaced with SHA-256 of empty string (unambiguous 64-hex value)
- Tests updated: ambiguity regression, suffix ordering, renames highest-version test
- scripts/update-dakota-versions.js: commit SBOM-only implementation so
  update-dakota-versions.test.ts passes from committed state alone

Assisted-by: Claude Sonnet 5 via GitHub Copilot CLI

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Records ambiguity rule: spdxPackageVersion returns undefined on multi-version names
- Records compareVersions NaN guard: use parseInt not Number for -ogc1 suffixes
- Records fixture hash standard: SHA-256 of empty string
- Records commit-order rule: implementation before tests that import it
- Adds router entry in docs/SKILL.md

Assisted-by: Claude Sonnet 5 via GitHub Copilot CLI

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Assisted-by: GPT-5.6 Sol via GitHub Copilot CLI

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Assisted-by: GPT-5.6 Sol via GitHub Copilot CLI

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- verifyRegistry now copies record.product onto every verified/unavailable
  image entry so productStatus(await verifyRegistry(...)) composes directly
  without caller mutation.
- productStatus throws an explicit error on a missing product field instead
  of silently grouping under 'unknown'.
- writeOutputsAtomically stages under destinationRoot/.tmp-* instead of
  os.tmpdir() so renameSync cannot fail with EXDEV across filesystems.
- rmSync cleanup is scoped to the staging subdirectory only, not destinationRoot.
- Added tests: product on verified/unavailable entries, compose test for
  productStatus+verifyRegistry, productStatus throws on missing product,
  mkdtempSync prefix assertion, cleanup scope assertion, --check-only
  semantics (required unavailable nonzero, optional omitted zero).

Assisted-by: Claude Sonnet 5 via GitHub Copilot CLI

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Assisted-by: Claude Sonnet 5 via GitHub Copilot CLI
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…mmit ProductVersionCard

ORAS CLI --descriptor returns JSON natively; --format json is rejected.
Regenerated dakota-versions.json via live wrapper (cosign predicate mismatch
produces correct unavailable shape). Commits untracked ProductVersionCard.vue
required by SectionPicker import.

Assisted-by: Claude Sonnet 4.5 via GitHub Copilot CLI

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Use '--type https://slsa.dev/provenance/v1' instead of the short alias
'slsaprovenance'. Live evidence confirms images attest the full URI; the
short alias causes 'none of the attestations matched the predicate type'
rejection. Both Dakota and Dakota NVIDIA now verify successfully and
public/dakota-versions.json is regenerated with real image/SBOM digests.

Assisted-by: claude-opus-4.8 via GitHub Copilot CLI
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Use a static x86-64-v3 fallback when the current public JSON lacks packages.baseline so regenerated Dakota metadata keeps the hardware baseline.

Assisted-by: Claude Sonnet 5 via GitHub Copilot CLI
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace documentation-cache fetching with direct OCI SBOM verification
via the shared orchestrator. Stable values come from verified Syft
artifacts on bluefin:stable and bluefin-nvidia-open:stable. LTS streams
are status: unavailable until they publish SPDX referrers.

ImageChooser now gates version chips on status === verified, preventing
unknown or stale values from rendering. The spdx-version-extractor
accepts both SPDX versionInfo and Syft version fields.

Assisted-by: Claude Sonnet 5 via GitHub Copilot CLI
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…At and versionInfo regression

- Strip epoch prefix and .fcNN/.elNN dist-tag in normalizeUserVersion
- Serialize checkedAt in stream-versions.yml; update ImageChooser typing
- Add YAML serialization test asserting no literal unknown
- Add versionInfo precedence regression test in shared extractor
- Update sbom-version-extraction skill with Bluefin source and rules

Assisted-by: Claude Sonnet 4.6 via GitHub Copilot CLI

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Assisted-by: Claude Sonnet 4.6 via GitHub Copilot CLI
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Assisted-by: Claude Sonnet 5 via GitHub Copilot CLI

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Consolidate SBOM verification into a single production command
(npm run update:image-versions) that verifies the registry once,
projects both Bluefin and Dakota version data, and atomically writes
all three outputs: stream-versions.yml, dakota-versions.json, and
sbom-audit.json.

Switch update-content.yml to daily schedule, add cosign installer,
and add deduplicated SBOM issue management via buildSbomIssuePlan.
Add sbom-audit.json to all cache save/restore paths for parity.

Assisted-by: Claude Sonnet 5 via GitHub Copilot CLI
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
verifyRegistry no longer skips pendingSbom records — all 9 registry
entries are attempted and appear in audit.images with status, required
flag, and errorCode/error when failing.

productStatus distinguishes required vs optional unavailable: required
unavailable → product unavailable, only optional → degraded.

buildSbomIssuePlan deduplicates by title and lists all affected images
in a single issue body when multiple share the same failure type.

--check-only exits nonzero on ANY unavailable including optional.

Assisted-by: Claude Sonnet 4 via GitHub Copilot CLI
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add image version registry, SBOM sources, fail-closed behavior, and
live-check commands to content-maintenance and validation skills.
Update content-map with generated version data entries.
Fix ImageChooser.vue type narrowing for StreamVersions access.

Assisted-by: Claude Sonnet 4.5 via GitHub Copilot CLI

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Auto-fix import sorting, brace style, and manually split multi-statement
lines to satisfy style/max-statements-per-line rule.

Assisted-by: Claude Sonnet 5 via GitHub Copilot CLI

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Assisted-by: Claude Sonnet 5 via GitHub Copilot CLI

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Assisted-by: Claude Sonnet 5 via GitHub Copilot CLI

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ppings

Ambiguity was extracted and then dropped: a required field that resolved to
two versions silently disappeared from a "verified" image, and the audit kept
no record of it. Every entry now carries ambiguous and rejected evidence.
Ambiguous or missing required fields make the image unavailable; ambiguous or
missing optional fields degrade it, keep the verified values, omit the
unresolved field, and alert. Unavailable entries no longer carry values at
all, so no projection can leak them; projections accept degraded evidence.
Public files keep the verified/unavailable contract the components gate on.

Live evidence resolves three of the four known ambiguities: Dakota mesa pins
freedesktop-sdk.bst:extensions/mesa/mesa.bst (26.0.6), Dakota systemd pins
gnome-build-meta.bst:core-deps/systemd-base.bst (260.2), and Bluefin podman
pins foundBy rpm-db-cataloger with the epoch stripped at projection. Bluefin
mesa reports two builds from one cataloger, so it stays optional, audited,
degraded, and omitted rather than guessed. Trimmed fixtures from the published
SBOMs pin each selector.

Tooling failure is now distinct from evidence failure. A missing binary,
timeout, throttled or unreachable registry, or malformed tool output raises
ToolingError and aborts before any output, cache, or deployment; only a
genuinely absent image, missing SBOM, or rejected provenance sanitizes a
field. Unrecognised tool failures block, because "absent" and "unreachable"
cannot be told apart and only one is safe to publish. An explained-field-loss
guard refuses to promote any output that drops a field the audit does not
explain.

Records with pendingSbom or an empty package map stay unavailable with
pending-mapping even once their image publishes an SPDX referrer, retaining
the resolved digests; publication is not a reviewed mapping.

The github-script step imported a relative path, which resolves against the
action bundle rather than the checkout and would have thrown at runtime. The
logic moved into scripts/lib/sbom-issue-sync.js, loaded through
pathToFileURL(path.join(process.cwd(), ...)); the policy test executes the
real script bodies through a runner that reproduces the action's referrer
semantics, with a negative control proving the old form still fails. Issue
listing paginates instead of capping at one page, the generic run-failure
issue is deduplicated by exact title, titles use the exact audit errorCode,
and bodies carry the workflow run URL plus each image's last successful
verification or "none recorded" — which is why the workflow now restores the
live-data cache first, using the exact save path list.

Documentation records the registry's real owner, the daily workflow as the
scheduled live smoke against the manual read-only check, and resolves the
spec's ambiguous maximum-acceptable-age requirement: daily checkedAt already
proves evidence freshness, and image publication age measures release cadence,
so no invented threshold is added.

Assisted-by: Claude Sonnet 5 via GitHub Copilot CLI
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Assisted-by: GPT-5.6 Sol via GitHub Copilot CLI

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Carry last successful timestamps across repeated failures, pass projection status into Dakota field-loss checks, and route legacy update commands through the atomic orchestrator.

Assisted-by: GPT-5.6 Sol via GitHub Copilot CLI

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@castrojo

Copy link
Copy Markdown
Contributor Author

Closing because this work is intended to remain local for review.

@castrojo castrojo closed this Aug 26, 2026
@castrojo
castrojo deleted the fix/download-pages branch August 26, 2026 15:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant