Build and review local release evidence¶
Use this runbook to inspect Vexcalibur’s schema-1 self-evidence for one exact commit. The commands do not mutate GitHub, PyPI, or another external service: they do not create a tag, GitHub Release, attestation, or package upload.
They do change the local checkout and tool caches. Expect .venv/, dist/,
src/vexcalibur/_version.py,
tests/integration/csaf-validator/node_modules/, the requested bundle under
build/, and uv, Go, and npm cache entries. Temporary evidence workspaces are
removed on exit. make clean removes the repository-generated build output and
validator installation; remove .venv/ or prune tool caches separately only
when you no longer need them.
For the credentialed schema-2 publication path, use Publish Vexcalibur to PyPI.
Prepare a clean release checkout¶
Use Linux and the tool versions in .tool-versions. You also need GNU Make,
Git, sha256sum, the Go version selected by
tests/integration/openvex-go/go.mod, and the locked Node dependencies for the
CSAF validator.
From the repository root:
git status --short
test -z "$(git status --porcelain)"
uv sync --frozen
make csaf-validator-install
Stop if Git reports any path. A dirty checkout is not a release-evidence input.
Dependency setup may download exact packages. VEX generation uses the reviewed local-findings provider, but this process is not a general network sandbox.
Everything under release-evidence/ is intended to become public. Do not place
embargoed advisories, private SBOM data, tokens, customer identifiers, or
internal URLs there.
Check the reviewed snapshot¶
Display the two digests that the review binds:
sha256sum uv.lock release-evidence/findings.json
uv run --frozen python scripts/release_evidence.py validate-review \
--review release-evidence/review.json \
--findings release-evidence/findings.json \
--lock uv.lock
The current zero-finding review prints:
production 0
A digest mismatch is a review failure. Do not update review.json merely to
make the command pass; review the complete changed lock or findings file first.
Build once and run the conformance gate¶
Build the exact clean commit, select the only wheel, and exercise both evidence fixtures:
uv build --clear --no-create-gitignore --no-sources
mapfile -t wheels < <(find dist -maxdepth 1 -type f -name "*.whl" | sort)
test "${#wheels[@]}" -eq 1
export VEXCALIBUR_WHEEL="${wheels[0]}"
make release-evidence-check
The gate generates the production and synthetic bundles twice in distinct temporary directories and compares every byte. It also:
verifies clean, full-commit wheel SCM metadata
installs the SHA-256-bound wheel from its own dependency metadata while the lock export constrains every resolved dependency by version and hash
validates CycloneDX output
runs the pinned official OpenVEX parser
runs the pinned CSAF schema and mandatory-test suite
compares normalized assertions across all generated VEX formats
Success ends with:
release-evidence production and synthetic fixture checks passed
The synthetic fixture is conformance data, not a vulnerability claim. Its
reserved .test URL and review_kind: synthetic_fixture prevent accidental
publication as production evidence.
Create an inspectable schema-1 bundle¶
Make the commit-derived timestamp explicit and generate into a fresh directory:
export RELEASE_SHA="$(git rev-parse --verify HEAD)"
export SOURCE_DATE_EPOCH="$(git show -s --format=%ct HEAD)"
RELEASE_EVIDENCE_OUTPUT=build/release-evidence make release-evidence
For a zero-finding review, the file list is:
SHA256SUMS
findings.json
manifest.json
review.json
runtime-constraints.txt
sbom.cdx.json
vex.cdx.json
Verify the bundle and its checksum inventory:
uv run --frozen python scripts/release_evidence.py verify-bundle \
--bundle-dir build/release-evidence
(
cd build/release-evidence
sha256sum --check SHA256SUMS
)
Inspect manifest.json. For the empty production snapshot, require:
uv run --frozen python - <<'PY'
import json
from pathlib import Path
manifest = json.loads(Path("build/release-evidence/manifest.json").read_text())
assert manifest["schema_version"] == 1
assert manifest["review"]["assertion_count"] == 0
assert manifest["formats"]["openvex"]["status"] == "omitted"
assert manifest["formats"]["csaf"]["status"] == "omitted"
assert manifest["source_tree_clean"] is True
PY
Do not upload this local bundle to an existing release. The release workflow creates the broader schema-2 asset set from isolated jobs and refuses manual replacement.
Update public findings¶
Use this sequence only after a maintainer has reviewed a public finding:
Add the finding to
release-evidence/findings.json. Select exactly one component, explicitly setanalysis_statetoin_triage, and provide a public source and honest analysis text.Review the entire new file and calculate its SHA-256.
Increment
analysis_revision; updatereviewed_at,reviewed_by, the findings digest, andconclusioninreview.json.If
uv.lockchanged, review the entire lock and update its digest in the same new revision.Run the complete conformance gate and inspectable-bundle checks again.
A nonempty production bundle must contain vex.cdx.json,
vex.openvex.json, and vexcalibur-vex.json. Do not promote a finding to
resolved, exploitable, false_positive, or not_affected until a separate
evidence and approval policy exists for that stronger claim.
Recover from local failures¶
Failure |
Safe response |
|---|---|
Dirty-tree rejection |
Commit the intended change or remove unrelated local state, then rebuild |
Timestamp mismatch |
Derive the epoch from the exact commit again |
Review digest mismatch |
Review and revise the bound input, or restore the reviewed bytes |
Wheel SCM mismatch |
Rebuild once from the exact clean commit; never relabel another wheel |
Constraint grammar failure |
Regenerate from the committed lock; never remove hash or binary-only enforcement |
Missing OpenVEX or CSAF validator |
Install the pinned prerequisites and rerun |
Cross-format mismatch |
Keep all output unpublished, fix the renderer or comparator, and add a regression test |
Output directory already exists |
Verify or remove only that generated directory, then rerun; the generator never overwrites it |
Generated files have no external effect. Roll back reviewed-input changes with a normal Git revert and rebuild from the resulting exact commit.