Releases and Verification
This document covers the Nexus release flow, artifact requirements, and consumer verification commands. Do not consume an image as a release artifact unless the signature and attestations verify for that exact digest.
Versioning
Section titled “Versioning”Versions are git tags on main of the form v0.y.z. While nexus is pre-1.0:
- A minor bump (
y) may include breaking changes. - A patch bump (
z) is non-breaking.
vortex-* shared crates are versioned in lockstep across the workspace. Release
notes are generated from the Conventional Commits
log; feat: and fix: entries are grouped. See Changelog
and Getting Started with Contributing.
Release flow
Section titled “Release flow”A release starts when a protected v* tag is pushed to GitLab.
The tag pipeline:
- Verifies the tag is protected and points at default-branch history.
- Builds each runtime image from the tagged commit.
- Generates a CycloneDX SBOM for each image.
- Scans each image and SBOM; High or Critical findings fail the release unless an approved exception is checked in.
- Signs each image digest with the Nexus cosign release key.
- Attaches the SBOM, SLSA-style provenance, and vulnerability-scan predicates as cosign attestations.
- Verifies every image signature, attestation, and decoded predicate content.
- Generates a human-readable LLM release report from verified release notes, image metadata, SBOMs, scan reports, and provenance predicates.
- Publishes the GitLab Release with the report, image digests, artifact links, and verification instructions.
The release job publishes only after the verification job succeeds. If signing, SBOM attestation, provenance attestation, scan attestation, verification, or the required LLM release report is missing, the tag pipeline fails and the tag is not a consumable release.
Release artifact requirements
Section titled “Release artifact requirements”Each released image must have:
- A version tag and immutable digest in the GitLab Container Registry.
- A cosign key signature for the digest.
- A CycloneDX SBOM attestation.
- A provenance attestation linking the digest to the source repository, commit, tag, and GitLab CI job.
- A vulnerability-scan attestation for the image scan report.
- A vulnerability scan result that satisfies the release gate.
- A release report that links to each image’s SBOM, image scan, SBOM scan, and provenance predicate.
The expected registry path is:
registry.gitlab.com/vortexaq/nexus/vnexus-<image>:vX.Y.ZAlways verify by digest after resolving the tag.
Verification Key
Section titled “Verification Key”The release public key is checked in at deploy/pipeline/signing/cosign.pub in the source repository.
Use the release notes for the exact image name and digest.
COSIGN_PUBLIC_KEY=deploy/pipeline/signing/cosign.pubIf the public key changes, treat it as release-signing evidence that needs reviewer confirmation before consumers rely on artifacts signed after the rotation.
Consumer verification
Section titled “Consumer verification”Install cosign, then set the release values from the GitLab Release page:
IMAGE=registry.gitlab.com/vortexaq/nexus/vnexus-<image>VERSION=vX.Y.ZDIGEST=sha256:<digest-from-release-notes>REF="${IMAGE}@${DIGEST}"COSIGN_PUBLIC_KEY=deploy/pipeline/signing/cosign.pubVerify the image signature:
cosign verify "$REF" \ --key "$COSIGN_PUBLIC_KEY"Verify the SBOM attestation:
cosign verify-attestation "$REF" \ --type cyclonedx \ --key "$COSIGN_PUBLIC_KEY"Verify the provenance attestation:
cosign verify-attestation "$REF" \ --type slsaprovenance \ --key "$COSIGN_PUBLIC_KEY"Verify the vulnerability-scan attestation:
cosign verify-attestation "$REF" \ --type https://anchore.com/grype/report/v1 \ --key "$COSIGN_PUBLIC_KEY"To inspect the SBOM predicate after verification:
cosign verify-attestation "$REF" \ --type cyclonedx \ --key "$COSIGN_PUBLIC_KEY" \ | jq -r 'if type == "array" then .[0].payload else .payload end' \ | base64 -d \ | jq '.predicate'To inspect provenance:
cosign verify-attestation "$REF" \ --type slsaprovenance \ --key "$COSIGN_PUBLIC_KEY" \ | jq -r 'if type == "array" then .[0].payload else .payload end' \ | base64 -d \ | jq '.predicate'Do not retag a verified image and treat the new tag as verified. The digest is the artifact identity.
CI reference
Section titled “CI reference”Developer CI tiers, local reproduction commands, security gates, runner requirements, and manual test guidance live in CI/CD Pipeline.
LLM Release Report
Section titled “LLM Release Report”The tag pipeline generates dist/pipeline/release/report.md before publishing the GitLab Release.
The report is written by an OpenAI-compatible LLM using dist/pipeline/release/evidence.json as its source of truth.
The evidence file contains the Conventional Commit release notes, commits since the previous tag, extra-scrutiny candidates, image digests, SBOM summaries, scan summaries with per-vulnerability findings (id, severity, affected package, installed version, fix availability, and advisory link), and artifact links.
It also includes bounded evidence from the STRIDE threat model, compliance map, telemetry tiering guidance, and ADRs so the report can explain security decisions, accepted compromises, compensating controls, and residual risks for security review.
Configure these protected CI variables before cutting a release tag:
LLM_RELEASE_REPORT_BASE_URL: base URL for the OpenAI-compatible API, without/chat/completions.LLM_RELEASE_REPORT_MODEL: model name used for the report.LLM_RELEASE_REPORT_API_KEY: masked secret for the report endpoint.COSIGN_PRIVATE_KEY_FILE: protected file variable containing the release signing private key.COSIGN_PUBLIC_KEY_FILE: protected file variable containing the release signing public key.COSIGN_PASSWORD: protected masked variable for the encrypted private key.
The LLM is not trusted to create artifact references.
CI appends a deterministic artifact index with links to SBOM, image scan, SBOM scan, and provenance files for every released image, and a deterministic Vulnerability Findings section with per-CVE detail whenever the LLM narrative omits one.
The report must include a Security Decisions, Compromises, And Residual Risk section; reviewers should treat it as a readable evidence aid, not as a substitute for reviewing the cited source documents and artifacts.
Self-Contained PDF Evidence Report
Section titled “Self-Contained PDF Evidence Report”The report job also renders dist/pipeline/release/report.pdf, a single self-contained document for reviewers who need release evidence without artifact-link access.
It contains the executive summary, pipeline verification status, per-CVE vulnerability findings, the changelog and commit list, the full narrative report, the threat model, compliance map, telemetry tiering document, all ADRs, per-image digests and artifact paths, and the complete SBOM component inventory (name, version, type, license) for every image.
The PDF is generated deterministically from evidence.json, report.md, the raw scan and SBOM files, and the checked-in security documents; the machine-readable JSON artifacts remain the canonical evidence.
PDF generation is best-effort: if it fails, the job still publishes the Markdown report and raw artifacts.
Cutting a release
Section titled “Cutting a release”- Ensure
mainis green and the## [Unreleased]changelog section is accurate. - Confirm
v*tags are protected in GitLab project settings. - Move the Unreleased entries into a dated
## [0.y.z]section and commit. - Tag from current
main:git tag v0.y.z && git push origin v0.y.z. - Wait for the GitLab tag pipeline to build, scan, sign, attest, verify, generate the release report, and publish.
- Confirm the GitLab Release lists every image digest, artifact links, and the public-key verification instructions.
- Confirm the report’s “Major, Breaking, Or Extra Scrutiny Required” section is reasonable before announcing the release.
- From a clean machine, run the consumer verification commands above for at least one image before announcing the release.
