Release-evidence reference

This page defines Vexcalibur’s repository-maintained evidence formats. They are not part of the package’s pre-1.0 Python API.

Reviewed inputs

release-evidence/review.json and the synthetic conformance review use this closed-world schema:

Field

Constraint

schema_version

Integer 1

review_kind

production or synthetic_fixture

analysis_revision

Positive integer, advanced for each review

reviewed_at

Extended RFC 3339 UTC timestamp ending in Z

reviewed_by

Nonempty public attribution; repository review history supplies provenance

inventory.path

uv.lock

inventory.sha256

Exact lock SHA-256 as four colon-delimited groups of 16 lowercase hexadecimal characters

inventory.coverage

cross-platform-reference-runtime

findings.path

release-evidence/findings.json for production or tests/fixtures/release-evidence/findings.json for the synthetic fixture

findings.sha256

Exact findings SHA-256 in the same grouped form

policy.allowed_analysis_states

Array containing only in_triage

conclusion

Nonempty review conclusion and qualifications

Unknown and duplicate JSON keys are errors. findings.json follows the local-findings contract, with additional evidence rules:

  • its root contains only findings;

  • every finding explicitly uses in_triage;

  • every finding selects exactly one product by PURL or component reference;

  • canonical vulnerability/product pairs are unique; and

  • production source URLs and analysis text are suitable for public release.

After selector resolution, the reviewed assertion count must equal the canonical CycloneDX assertion count. Equivalent PURL and component-reference selectors cannot inflate the result.

Normal validation accepts only the production input path. The repository’s synthetic input is accepted only when validate-review or scripts/generate-release-evidence.sh receives --allow-synthetic. Its local manifest records evidence_kind: synthetic_fixture and intended_use: ci_conformance_only; it is never eligible for a publication inventory. Production local evidence records evidence_kind: production and intended_use: release_evidence_candidate.

Shared manifest records

Manifest SHA-256 values use 64 lowercase hexadecimal characters without the review input’s colon grouping. These record shapes are reused below:

Record

Exact fields and meaning

Artifact

name, sha256, and nonnegative byte size

Release

commit, purl, source_date_epoch, timestamp, and version

Review summary

analysis_revision, assertion_count, conclusion, findings_sha256, review_sha256, and state_counts

The release commit is a lowercase 40-character Git object ID. purl is pkg:pypi/vexcalibur@VERSION; timestamp is the UTC value derived from the nonnegative commit epoch. The review summary does not duplicate reviewer attribution: reviewed_by and reviewed_at remain in the separately bundled and hashed review.json. assertion_count is nonnegative, and state_counts maps each analysis state to its count in sorted-key order; the current policy therefore permits only in_triage, or an empty object for zero findings.

Every formats object has exactly cyclonedx, openvex, and csaf. A generated record contains artifact, assertion_count, conformance, and status: generated. The exact conformance values are:

Format

Artifact

Conformance value

CycloneDX

vex.cdx.json

CycloneDX 1.6 schema passed

OpenVEX

vex.openvex.json

official OpenVEX Go parser passed

CSAF

vexcalibur-vex.json

CSAF 2.0 strict schema and mandatory tests passed

CycloneDX is generated even with zero assertions. In that case, each other format record contains only reason and status: omitted, and omitted_formats contains matching {format, reason} records. The reason is:

SPECIFICATION requires at least one vulnerability finding; this reviewed snapshot contains zero findings and makes zero VEX assertions.

SPECIFICATION is OpenVEX 0.2.0 or CSAF 2.0 VEX profile.

Local bundle: manifest schema 1

scripts/generate-release-evidence.sh creates a local bundle. Every bundle contains:

File

Contract

sbom.cdx.json

Normalized CycloneDX 1.5 reference-runtime SBOM

runtime-constraints.txt

Exact, hash-locked runtime requirements

review.json

Byte-identical reviewed input

findings.json

Byte-identical reviewed input

vex.cdx.json

CycloneDX 1.6 VEX, including a valid zero-assertion document

manifest.json

Schema-1 release, review, format, generator, validation, and artifact records

SHA256SUMS

Sorted digest inventory for every other file

A nonempty review also produces vex.openvex.json and vexcalibur-vex.json. Empty reviews omit them as described above.

The generated manifest has exactly these top-level fields:

Field

Exact contract

schema_version

Integer 1

evidence_kind

The reviewed input’s production or synthetic_fixture kind

intended_use

release_evidence_candidate for production or ci_conformance_only for a synthetic fixture

source_tree_clean

Boolean recorded at generation; false is explicitly non-publishable

release

Shared release record

inventory

coverage, limitation, lock_sha256, sbom, and sbom_specification

generator

distribution, version, wheel_filename, wheel_sha256, wheel_source_commit, wheel_source_dirty, and uv_version

review

Shared review-summary record

formats

Exact format records described above

omitted_formats

Empty array or the required OpenVEX and CSAF omission records

validation

Exact validation record below

artifacts

Filename-sorted artifact records for every payload file, excluding manifest.json and SHA256SUMS

The inventory values are coverage: cross-platform-reference-runtime, sbom: sbom.cdx.json, and sbom_specification: CycloneDX 1.5. limitation is:

uv.lock records the project's cross-platform reference runtime; it is not a claim about every environment-specific consumer resolution.

The generator values include distribution: vexcalibur, the installed package version, the wheel filename and digest, its exact clean source commit, wheel_source_dirty: false, and the uv version. The validation record has exactly these fields and values:

Field

Value

cross_format_assertion_equivalence

passed, or not_applicable_without_assertions for an empty review

installed_local_wheel

passed

production_state_policy

only_in_triage

sbom_cyclonedx_1_5_schema

passed

vex_cyclonedx_1_6_schema

passed

vulnerability_provider_selection

offline_local_findings_only

This manifest binds one wheel and commit but does not claim that publication occurred.

Publication inventory: manifest schema 1

The release workflow transports a second schema-1 manifest before finalization. It is a candidate-free oracle, not a local evidence manifest or a published release asset. Its directory contains exactly findings.json, review.json, runtime-constraints.txt, sbom.cdx.json, uv.lock, manifest.json, and SHA256SUMS.

Its manifest has exactly artifacts, inventory, inventory_kind, release, review, schema_version, source_tree_clean, and uv_version. The exact constraints are:

  • schema_version is 1, inventory_kind is publication_oracle, and source_tree_clean is true;

  • release and review use the shared record shapes;

  • inventory contains the same coverage, limitation, SBOM name and specification as the local manifest, plus lock: uv.lock and the digest of that bundled lock;

  • artifacts contains sorted records for the five inventory payload files; and

  • uv_version is a nonempty string.

Only production reviewed inputs can create this inventory. The finalizer copies the five payload files into the publication bundle, then replaces this transient manifest and checksum inventory with the schema-2 records.

Publication bundle: manifest schema 2

The reusable release-validation workflow creates one flat asset directory. Every publication bundle contains:

File

Contract

uv.lock

Exact reviewed lock bytes

sbom.cdx.json

Normalized CycloneDX 1.5 SBOM regenerated from that lock

runtime-constraints.txt

Strict runtime installation contract

review.json and findings.json

Exact reviewed inputs

vex.cdx.json

Byte-identical output from the installed wheel and pinned Action

vexcalibur-VERSION-py3-none-any.whl

Exact checked wheel later eligible for PyPI

vexcalibur-VERSION.tar.gz

Exact checked source distribution later eligible for PyPI

manifest.json

Closed-world schema-2 publication record

SHA256SUMS

Sorted digest inventory for every other release asset

vex.openvex.json and vexcalibur-vex.json are additionally present when the review contains at least one assertion.

The schema-2 verifier rejects unknown fields. The top-level fields are exactly:

Field

Exact contract

schema_version

Integer 2

evidence_kind

production

intended_use

immutable_release_candidate

source_tree_clean

Boolean true

release

Shared release record; tag must equal vVERSION

inventory

coverage, limitation, lock, lock_sha256, sbom, and sbom_specification

review

Shared six-field review-summary record

generator

Exact distribution record below

formats and omitted_formats

Exact generated or zero-assertion records described above

validation

Exact validation record below

publication

Exact publication and provenance record below

artifacts

Filename-sorted records for every asset except manifest.json and SHA256SUMS

The inventory is the publication-inventory record, including lock: uv.lock. The generator contains exactly distribution, sdist_filename, sdist_sha256, uv_version, version, wheel_filename, wheel_sha256, wheel_source_commit, and wheel_source_dirty. The distribution is vexcalibur; the expected filenames are vexcalibur-VERSION.tar.gz and vexcalibur-VERSION-py3-none-any.whl; the source commit equals the release commit; and wheel_source_dirty is false.

The schema-2 validation record contains the six schema-1 fields plus action_local_wheel_equivalence: passed. All other values and the zero-assertion cross-format exception are unchanged.

publication has exactly these fields:

Field

Exact contract

asset_contract

flat_immutable_github_release

release_tag

Exact vVERSION tag

payload_digest_algorithm

sha256_canonical_artifact_records_v1

distributions

Exactly one sdist and one wheel record, each containing kind, name, sha256, and size

build

actions_artifact_name: dist-COMMIT, job: build, payload_sha256, and workflow: .github/workflows/release-validation.yml

inventory

actions_artifact_name: release-inventory-COMMIT, job: publication-inventory, and payload_sha256

direct_generation

actions_artifact_name: direct-vex-COMMIT, job: direct-vex, and payload_sha256

action

Exact Action provenance record below

The action record contains exactly:

Field

Value

actions_artifact_name

action-vex-COMMIT

commit

Full pinned companion Action commit

constraints

runtime-constraints.txt

job

action-vex

output_equivalence

byte_for_byte

package_spec

file_uri_with_sha256_fragment

payload_sha256

Canonical payload digest of the generated VEX files

repository

vexcalibur-dev/vexcalibur-action

Schema-2 creation currently requires Action commit cc570fb0ab80df3f4b1e31c0608b95c0707d5b66. Verification with an explicit expected Action commit requires that exact value. Historical verification without one accepts only a commit in the verifier’s compiled allowlist for schema 2; the manifest cannot extend that allowlist.

For each producer, the finalizer sorts the relevant {name, sha256, size} records by filename, serializes them with the repository’s canonical JSON serializer, and records the SHA-256 as payload_sha256. The inventory payload covers the five reviewed inventory files, the build payload covers the wheel and source distribution, and the direct and Action payloads cover the generated VEX files. The latter two digests must be equal.

GitHub Actions archive digests are checked while artifacts cross jobs. They are deliberately absent from the published manifest because the archive envelope is transport metadata and may differ on a recovery run.

Runtime-constraint grammar

Both local and publication bundles use the same strict header:

--require-hashes
--only-binary :all:

Every following logical requirement has an exact name==version pin and at least one --hash=sha256:... continuation. Direct URLs, includes, index or find-links directives, editable requirements, non-exact specifiers, and any directive that weakens binary-only hash checking are rejected.

Archive limits

Candidate archives are bounded before metadata is trusted:

Limit

Value

Maximum evidence file

32 MiB

Maximum archive members

10,000

Maximum total expanded archive size

128 MiB

Maximum metadata member

1 MiB

Maximum wheel SCM metadata

64 KiB

Absolute paths, parent traversal, duplicate members, links, devices, special files, encrypted wheel entries, ambiguous metadata, and version/source mismatches are errors.

Integrity files

SHA256SUMS uses the GNU sha256sum form:

LOWERCASE_SHA256__TWO_SPACES__FILENAME

Entries are filename-sorted. The checksum file includes manifest.json but not itself. The manifest excludes itself and SHA256SUMS, avoiding a circular digest while the checksum file binds the manifest to all other assets.

Verify a local schema-1 bundle:

uv run --frozen python scripts/release_evidence.py verify-bundle \
  --bundle-dir build/release-evidence

Verify a schema-2 publication bundle against an exact tag and commit:

RELEASE_TAG=v0.4.0
git fetch origin "refs/tags/$RELEASE_TAG:refs/tags/$RELEASE_TAG"
RELEASE_SHA="$(git rev-parse --verify "$RELEASE_TAG^{commit}")"

uv run --frozen python scripts/release_evidence.py verify-publication \
  --bundle-dir build/publication-assets \
  --release-tag "$RELEASE_TAG" \
  --release-sha "$RELEASE_SHA"

The schema-1 verify-bundle command checks that SHA256SUMS, manifest artifact records, file sizes, and bundle bytes agree. It does not independently enforce the generator’s expected local schema-1 file set, rerun format validation, or prove the semantic correctness of that manifest; generation and CI provide those gates. verify-publication-inventory separately enforces the transient schema-1 oracle contract.

Publication verification is deliberately stricter. It enforces the closed-world schema-2 file and manifest contracts and rechecks reviewed inputs, bundled-lock/SBOM semantics, runtime constraints, distribution metadata and SCM identity, format decisions, stable payload digests, and production policy. The release workflow separately reproduces lock-derived exports, runs the pinned official OpenVEX and CSAF validators, and compares the GitHub-hosted asset bytes after publication.

Protected release-note tag payload

The release-note payload is not part of either evidence manifest. It is the message of the protected bot-authored annotated release tag. The publisher serializes one compact canonical JSON object with exactly these fields:

Field

Constraint

schema_version

Integer 1

tag

Exact vMAJOR.MINOR.PATCH release tag

release_notes

Exact secret-scanned release-note string

release_notes_sha256

SHA-256 of the UTF-8 release-note bytes as 64 lowercase hexadecimal characters

The tag ref must point to an annotated tag object. That object must directly target the expected release commit and name the automation bot’s GitHub noreply identity as tagger. Creation and immediate pre-publication checks parse the closed JSON object and require its exact field values. JSON key order or trailing whitespace is not a trust input; the embedded note bytes and digest are.

Recovery reads the tag through GitHub’s API, enforces the ref, object, target, tagger, closed-world payload schema, tag value, and notes digest, and writes the recovered note bytes from release_notes. If a draft or immutable release already exists, its body must match those bytes. The recovered notes then pass through the ordinary digest comparison and secret scan again. A mutable release body is never accepted as the recovery authority.

Repository CLI subcommands

scripts/release_evidence.py exposes these maintainer interfaces. Arguments in brackets are optional; every other argument shown is required.

Subcommand and arguments

Purpose

timestamp --epoch INT

Render a nonnegative epoch as the canonical UTC release timestamp

normalize-sbom --input PATH --output PATH --release-version VERSION --timestamp TIME --lock-sha256 DIGEST

Normalize one uv CycloneDX 1.5 export

validate-review --review PATH --findings PATH --lock PATH [--allow-synthetic]

Validate the reviewed inputs, bindings, and evidence policy

validate-wheel --wheel PATH --release-sha COMMIT

Verify clean, full-commit wheel SCM metadata

hashed-file-uri --file PATH

Print an absolute file URI with a SHA-256 fragment

validate-cyclonedx --document PATH --spec-version {1.5,1.6}

Validate a CycloneDX document against the selected bundled schema

compare-formats --cyclonedx PATH --openvex PATH --csaf PATH

Compare canonical assertions across all three VEX formats

finalize --bundle-dir PATH --release-sha COMMIT --release-version VERSION --source-date-epoch INT --lock PATH --wheel PATH --review PATH --findings PATH --uv-version VERSION --source-tree-clean {true,false}

Write and integrity-check a local schema-1 manifest and checksum file in a prepared staging directory

verify-bundle --bundle-dir PATH

Verify generic schema-1 checksum and artifact-record integrity

prepare-publication-inventory --output-dir PATH --release-sha COMMIT --release-version VERSION --source-date-epoch INT --lock PATH --review PATH --findings PATH --constraints PATH --sbom PATH --uv-version VERSION --source-tree-clean {true,false}

Create and verify the schema-1 publication oracle

verify-publication-inventory --inventory-dir PATH --release-sha COMMIT --release-version VERSION

Revalidate the oracle against an expected release identity

finalize-publication --output-dir PATH --inventory-dir PATH --wheel PATH --sdist PATH --direct-output-dir PATH --action-output-dir PATH --release-tag TAG --action-commit COMMIT --expected-wheel-sha256 DIGEST --expected-sdist-sha256 DIGEST

Assemble and verify the flat schema-2 asset set

verify-publication --bundle-dir PATH --release-tag TAG --release-sha COMMIT [--action-commit COMMIT]

Verify a complete schema-2 publication bundle

prepare-publication-inventory and finalize-publication require a new output directory and remove a partially created one after failure. The higher-level scripts/generate-release-evidence.sh also refuses to replace its requested local bundle directory. Verification commands do not create output directories; normalize-sbom and finalize are lower-level writers and must be used only with an intentionally prepared target.