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 human-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 initial 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 with exact hash-locked dependencies and a SHA-256-bound wheel URI;
validates CycloneDX output;
runs the pinned official OpenVEX parser;
runs the pinned CSAF schema and mandatory-test suite; and
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:
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.