Release
Release evidence bundles
Komms release evidence is a bounded, revision-scoped archive. It records what was built and tested; it does not convert a simulator, project-controlled second build, or internal review into physical or independent evidence.
The implementation is
scripts/release-evidence.py, with separate
validators for
qualification and
signing. Their regression tests are part of
the local and hosted release gates.
1. Bundle inventory
An evidence bundle contains:
| Record | Purpose |
|---|---|
source.json |
Exact source revision, tag, version, source-date epoch, collector, and build environments |
builders.json |
Bounded public records for every native builder; no stable host identifier or credential |
artifacts.json |
Exact path, size, mode, SHA-256, and supported normalized digest for every package |
release-evidence.json |
Channel, artifact totals, record digests, build environments, and narrowly stated claims |
SHA256SUMS |
Exact inventory of every bundle file except the detached signature |
komms.cdx.json |
Deterministic CycloneDX 1.5 aggregate SBOM for both Cargo workspaces and Android locked dependencies |
android-licenses.json |
Every locked Android coordinate, revision-controlled declared expression, optional verified POM cross-check, and unresolved count |
dependency-policy.json |
Cargo policy results, lockfile digests, Android dependency-lock and verification-metadata results, plus the complete release-toolchain policy and digest |
provenance.json |
Local provenance statement; hosted attestations remain separate |
reproducibility.json |
Exact, normalized, explicitly explained, different, and missing results between two build records |
qualification.json |
Canonical-matrix digest plus named environment and install/upgrade/rollback case rows |
signing.json |
Public fingerprint, exact artifact digests, verifier, and status for every signing role |
residual-risks.json |
Open risks or the accountable release decision |
stable-beta.json |
Consent-based pilot aggregates, final-candidate reruns, P0 audit, support/update plan, rollback, and candidate-only founder decision |
release-notes.md |
User-visible scope and limitations |
SHA256SUMS.sig |
Detached offline release-manifest signature, added only after promotion |
Artifact input is limited to 512 regular files and 16 GiB. Symlinks, path traversal, unsupported filesystem entries, duplicate checksum rows, secret-bearing JSON fields, oversized records, and unrecorded files fail closed. Safe archive extraction applies the same path and size rules. The publication path separately limits the complete draft to 513 assets and 16 GiB before downloading anything.
2. Local validation bundle
Use an exact clean revision and a staging directory containing only intended packages:
revision="$(git rev-parse HEAD)"
epoch="$(git show -s --format=%ct HEAD)"
python3 scripts/release-evidence.py builder-record \
--revision "$revision" \
--builder-id local-controlled-1 \
--os macOS \
--architecture arm64 \
--environment "clean controlled release host" \
--runner-image "named host image and version" \
--isolated \
--tool "rustc=$(rustc --version)" \
--output target/builder.json
python3 scripts/release-evidence.py inventory \
--artifact-dir target/release-artifacts \
--revision "$revision" \
--output target/artifacts.json
python3 scripts/release-qualification.py prepare \
--revision "$revision" \
--version 0.4.2 \
--artifact-manifest target/artifacts.json \
--output target/qualification.json
python3 scripts/release-signing.py prepare \
--revision "$revision" \
--artifact-manifest target/artifacts.json \
--output target/signing.json
python3 scripts/stable-beta-readiness.py prepare \
--revision "$revision" \
--version 0.4.2 \
--artifact-manifest target/artifacts.json \
--release-notes .github/release-body.md \
--output target/stable-beta.json
python3 scripts/android-license-evidence.py inventory \
--repository . \
--policy release/android-license-policy-v1.json \
--revision "$revision" \
--output target/android-licenses.json
python3 scripts/android-license-evidence.py validate \
--repository . \
--policy release/android-license-policy-v1.json \
--record target/android-licenses.json \
--expected-revision "$revision" \
--require-completeThe source-controlled policy makes a clean host deterministic. Passing
--gradle-cache /path/to/modules-2/files-2.1 additionally records and compares
available POM declarations. A mismatch fails the complete gate. The inventory
records upstream declarations; it is neither legal advice nor a license
compatibility approval.
Run both Cargo policy gates and the Android dependency build before recording them as passed. Generate the SBOM and dependency record:
python3 scripts/release-evidence.py sbom \
--repository . \
--revision "$revision" \
--version 0.4.2 \
--android-license-report target/android-licenses.json \
--output target/komms.cdx.json
python3 scripts/release-evidence.py dependency-record \
--repository . \
--revision "$revision" \
--root-cargo-deny passed \
--desktop-cargo-deny passed \
--android-dependency-locking passed \
--android-dependency-verification passed \
--android-license-report target/android-licenses.json \
--output target/dependency-policy.jsonAssemble with --channel validation, then verify:
python3 scripts/release-evidence.py bundle \
--artifact-dir target/release-artifacts \
--output-dir target/release-evidence \
--revision "$revision" \
--version 0.4.2 \
--tag v0.4.2 \
--source-date-epoch "$epoch" \
--builder target/builder.json \
--channel validation \
--sbom target/komms.cdx.json \
--android-licenses target/android-licenses.json \
--dependency-policy target/dependency-policy.json \
--qualification target/qualification.json \
--signing target/signing.json \
--residual-risks release/residual-risks-v1.json \
--stable-beta target/stable-beta.json \
--release-notes .github/release-body.md
python3 scripts/release-evidence.py verify \
--bundle-dir target/release-evidence \
--expected-revision "$revision"Generated evidence belongs under ignored target/ storage until a release
candidate is intentionally retained.
3. Reproducibility measurement
Each build gets its own builder record. Run the same revision from a second clean controlled environment and compare the two evidence manifests:
python3 scripts/release-evidence.py compare \
--first first/release-evidence.json \
--second second/release-evidence.json \
--output target/reproducibility.json \
--require noneexact means byte-for-byte equality. normalized means only the explicitly
implemented ZIP-entry or tar-entry normalization agreed; it is not a claim
that arbitrary differences are harmless. explained requires both final file
digests, a non-empty explanation, and durable supporting evidence. different
and missing remain unexplained and block stable promotion. APK, AAB, IPA, and
signed desktop containers commonly include platform-signing or packaging
metadata, so the record reports exact and unsigned-payload measurements
separately where supported.
A project-controlled second build is a reproducibility measurement, not
independence. independently_verified remains false until a separately
administered environment supplies a reviewed record. Platform artifacts are
described as measured reproducibility unless the evidence actually establishes
bit-for-bit reproduction.
An independent record must name the separate administrator, environment, execution time, HTTPS report location and digest, and two distinct builder identities. Stable validation also requires the report to cover every release artifact exactly once with no unexplained or missing row:
python3 scripts/release-evidence.py validate-reproducibility \
--record reviewed/reproducibility.json \
--artifact-manifest target/release-artifacts.json \
--expected-revision "$revision" \
--require-independent4. Qualification records
The source matrix is
release/qualification-matrix-v1.json.
Every case begins open.
The qualification record binds the exact matrix name, version, and SHA-256. Validation requires every matrix row and case in canonical order; deleting a platform, environment contract, or required transition fails even when the remaining summary would otherwise appear complete.
observedrecords useful simulator, emulator, generic-host, or unsupported behavior without creating a supported claim.passedrequires a named supported environment, exact artifact digest, start/end time, steps, and result.failedretains the actual failed run.blockedidentifies an unavailable credential, device, account, network, or other external precondition.openmeans no run occurred.
The validator rejects a simulator or generic host marked passed. Stable
validation adds --require-complete, which rejects every open, observed,
blocked, or failed case.
5. Promotion and offline signature
A validation bundle is promoted only after the public signing record, qualification record, reproducibility disposition, residual-risk decision, and release notes are complete:
python3 scripts/release-evidence.py promote \
--bundle-dir target/release-evidence \
--output-dir target/promoted-evidence \
--channel beta \
--signing reviewed/signing.json \
--qualification reviewed/qualification.json \
--reproducibility reviewed/reproducibility.json \
--residual-risks reviewed/residual-risks.json \
--release-notes reviewed/release-notes.mdAlpha or Beta promotion requires a verified release-manifest role plus the native
signing role for every platform artifact actually included. Stable promotion
also requires every policy signing role and artifact class, a completely
passed qualification matrix, no unexplained reproduction difference, genuine
independent reproduction evidence, an authorized residual-risk decision, and a
passing stable-beta.json. The stable-beta record must bind the exact artifact
manifest and release notes; its consent pilot, eleven final-candidate rows,
P0-01 through P0-10, support/update window, tested rollback, and candidate-only
founder decision must all pass. The command copies rather than mutates the
validation bundle and recomputes every record digest.
Stable promotion supplies the reviewed record explicitly:
python3 scripts/release-evidence.py promote \
--bundle-dir target/release-evidence \
--output-dir target/promoted-evidence \
--channel stable \
--signing reviewed/signing.json \
--qualification reviewed/qualification.json \
--reproducibility reviewed/reproducibility.json \
--residual-risks reviewed/residual-risks.json \
--stable-beta reviewed/stable-beta.json \
--release-notes reviewed/release-notes.mdStable residual-risk authorization names the exact revision, decision owner,
decision time, durable go/no-go evidence, and a closed or explicitly accepted
disposition for every listed risk. Any open risk blocks promotion:
python3 scripts/release-evidence.py validate-residual-risks \
--record reviewed/residual-risks.json \
--expected-revision "$revision" \
--require-authorizedPromotion never substitutes artifact bytes. When platform signing, notarization, or store packaging changes a package, first assemble a fresh validation bundle from the final package directory and records bound to those new digests. Promoting an unsigned bundle and uploading different signed files is rejected at publication.
Move only SHA256SUMS to the offline signing device, compare the bundle and
key fingerprints, sign it as described in
release security and recovery, and
return SHA256SUMS.sig. Verify the complete bundle locally before packaging
it. Packaging an Alpha, Beta, or Stable bundle fails when the detached signature is
absent. The publication workflow separately performs the cryptographic
signature check against the enrolled public key.
Package the completed directory with the exact top-level layout and name:
python3 scripts/release-evidence.py pack \
--bundle-dir target/promoted-evidence \
--output target/Komms-0.4.2-release-evidence.tar.gzThe release draft must contain exactly the final top-level package files plus
that one archive. Before publication, the workflow safely extracts and verifies
the bundle, then checks every downloadable package name, byte count, and
SHA-256 against artifacts.json:
python3 scripts/release-evidence.py verify-published-artifacts \
--artifact-dir target/final-release-assets \
--manifest target/promoted-evidence/artifacts.json \
--expected-revision "$revision"The retained hosted archive is named
Komms-MAJOR.MINOR.PATCH-validation-evidence.tar.gz; it is deliberately
ineligible for publication. Completed assets are uploaded only after the empty
draft, exact tag, archive digest, and maintainer authorization have been
rechecked. A same-tag asset is never replaced.
No release may be promoted merely by editing the claim booleans. The signing, qualification, reproduction, residual-risk, and stable-beta records remain separately validated inputs.
6. Hosted evidence and retention
The release workflow:
- uses native Windows, macOS, Linux, Android, and iOS Simulator build paths;
- pins every action to a full commit;
- fixes the release Rust, Gradle, Java, cargo-ndk, and checksum-verified XcodeGen versions where applicable, plus the Dockerfile frontend and image bases by registry digest;
- records declared licenses for every locked Android coordinate, distinguishes Gradle-verified POMs from POM digests bound by the evidence record, and fails when a declaration is unknown;
- records each build environment without credentials or stable runner identifiers;
- performs two controlled Linux builds and retains their comparison;
- generates the aggregate SBOM and dependency-policy record;
- produces GitHub artifact attestations for retained validation files; and
- retains candidate, build, comparison, and evidence artifacts for 90 days.
Hosted attestations bind GitHub's run identity to candidate files. They are not the offline release signature, a store signature, physical-device evidence, or an external reproducibility result.
A validation run may be durable evidence for its exact revision once its run URL and artifact digest are recorded in the ledger. Expired workflow artifacts cannot close a durable gate; an authorized release evidence archive must be retained with the release and in the project's recovery archive.