rscrypto releases are approved by the maintainer and published by CI.
cargo-rail prepares a release pull request and creates the signed tag. GitHub
Actions builds, attests, and publishes the artifacts. A normal release must
never run cargo publish locally.
-
Start from a clean, current
mainafter all intended feature pull requests have merged. Every user-visible change must already have a reviewed.changes/*.mdfile.git switch main git pull --ff-only git status --short just release-check
-
Prepare the release:
just release-prepare
This creates a
rail/release-*branch, commits the generated version and changelog, opens a pull request, refreshes the standalone constant-time tool lockfiles, and pushes that follow-up commit. It does not tag or publish. -
Wait for the release pull request's required
Completecheck. Review the version, changelog, and lockfile diff, then merge it in the GitHub UI. -
Record the exact merged release candidate:
git switch main git pull --ff-only candidate=$(git rev-parse HEAD) -
Wait for
mainCI to pass, then dispatch the expensive release evidence on that commit. Do this before another pull request merges intomain.gh workflow run weekly.yaml --ref main gh workflow run riscv.yaml --ref main -f mode=evidence
Confirm that both runs report
$candidateas their head SHA. If code, dependencies, features, build inputs, or test policy change afterward, rerun both workflows. -
After exact-commit CI and both evidence workflows are green, create and push the signed tag:
test "$(git rev-parse HEAD)" = "$candidate" just release-tag
release-tagrechecks live repository controls, exact-commit CI, and release evidence before allowing the tag. It never publishes to crates.io locally. -
The tag starts the
Releaseworkflow. Approve itscrates-ioenvironment job after the prerequisite jobs pass. CI publishes and verifies the immutable GitHub Release before publishing the same crate through crates.io Trusted Publishing. -
Run the commands in Verification.
| Gate | What it prevents |
|---|---|
| Release pull request | An unreviewed version or changelog mutation reaching protected main. |
main CI |
Tagging a commit that was never proven in its final protected-branch identity. |
| Weekly and RISC-V evidence | Publishing cryptographic claims without the required platform and timing evidence. |
| Signed immutable tag | Moving a released version to different source later. |
| Immutable, attested GitHub Release | Publishing artifacts that cannot be tied back to the tag and build. |
| Environment approval | A tag or compromised workflow publishing to crates.io without a final human decision. |
| Trusted Publishing | Long-lived crates.io credentials becoming a repository secret. |
The repeated pull-request and main checks are intentional. The first answers
"may this change merge?" The second proves "this exact protected-branch commit
may become a release." The expensive Weekly and RISC-V evidence is not repeated
on every pull request; it runs only for a release candidate.
Configure the crate on crates.io:
| Field | Value |
|---|---|
| Repository owner | loadingalias |
| Repository name | rscrypto |
| Workflow filename | release.yaml |
| Environment | crates-io |
After the first successful Trusted Publishing release, enable crates.io
Trusted Publishing Only Mode for rscrypto. That disables traditional API
token publishing for new versions.
Configure the GitHub repository:
- Activate the
protect-mainbranch ruleset described by.github/rulesets/protect-main.json, with no bypass actors. - Activate the
protect-release-tagstag ruleset described by.github/rulesets/protect-release-tags.json, with no bypass actors. It permits a newv*tag but prevents updating or deleting an existing one. - In Settings → General → Releases, enable release immutability as described
by
.github/repository-settings/release-immutability.json. It applies only to releases published after the setting is enabled. - Create an environment named
crates-ioand add the current maintainer as its required reviewer. Permit self-review while the project has one maintainer, but disable administrator bypass. Require independent approval after a second trusted maintainer exists. - Keep long-lived crates.io publish tokens out of repository secrets.
The environment name must match crates.io and
.github/workflows/release.yaml, or the
OIDC token exchange will fail.
Commit a cargo-rail change file with each user-visible change when possible.
Change files live in .changes/; their reviewed bodies become the changelog.
Commit subjects remain engineering history, not release notes.
just release-change patch "Describe the user-visible result."
just release-statusUse minor or major when compatibility requires it. Before preparing a
release, just release-check validates configuration, dependency unification,
pending intent, SemVer advice, and the generated release plan.
Pull-request CI uses cargo-rail's planner to select checks from the actual
changed surfaces. Pushes to main, Weekly, and release preflight run the full
Cargo graph proof because those commits can become release inputs.
release-prepare consumes the change files. After its pull request merges,
release-tag deliberately does not rerun the pending-intent check. Instead, it
validates the materialized release and uses cargo rail release finalize --skip-publish to create and push the signed tag.
To inspect live repository controls without starting a release:
just check-repository-controlsThis is the only routine local check that reads live GitHub settings. It writes
the captured JSON to target/repository-controls.json; normal checks and
pre-push validation remain offline.
Pushing a vX.Y.Z tag starts the Release workflow. Before crates.io can
receive anything, the workflow:
- Verifies the annotated SSH signature, tag target, crate version, and changelog version.
- Revalidates configuration, the unified Cargo graph, dependency policy, audit results, SemVer, and successful CI for the exact commit.
- Requires complete Weekly CT/RSA and RISC-V native/CT evidence from that exact commit and crate version.
- Builds the
.crateonce, reproduces the source archive from the tag, and rejects dirty, private, local-only, or mismatched package contents. - Captures repository controls and writes provenance attestations, an identity
manifest, and
SHA256SUMSfor the artifacts and evidence. - Publishes and verifies the immutable GitHub Release, obtains a temporary crates.io token through OIDC, publishes the same crate, then downloads it from crates.io and verifies its SHA-256.
Any change after the evidence run—including a version-only or release-tooling change—creates a new release candidate and requires fresh paired Weekly and RISC-V evidence. Ancestor binaries are never promoted into an exact-commit constant-time claim.
Re-running the workflow is safe after a partial failure. If crates.io already has the version, the workflow downloads it and compares SHA-256 before touching the GitHub Release. A draft release can be repaired and then published. A published immutable release is never overwritten; the workflow verifies its release attestation and stable crate and source assets before publishing to crates.io. A mismatch is a hard stop.
If the signed-tag key changes, update .github/allowed-signers in a reviewed
commit before creating the next release tag.
Consumers can verify the GitHub Release artifact:
gh release download vX.Y.Z --repo loadingalias/rscrypto \
-p 'rscrypto-X.Y.Z.crate' \
-p 'rscrypto-X.Y.Z-source.tar.gz' \
-p 'rscrypto-X.Y.Z-ct-evidence.tar.gz' \
-p 'rscrypto-X.Y.Z-repository-controls.json' \
-p 'rscrypto-X.Y.Z-release-manifest.json' \
-p SHA256SUMS
sha256sum --check SHA256SUMS
gh release verify vX.Y.Z --repo loadingalias/rscrypto
gh release verify-asset vX.Y.Z rscrypto-X.Y.Z.crate --repo loadingalias/rscrypto
gh release verify-asset vX.Y.Z rscrypto-X.Y.Z-source.tar.gz --repo loadingalias/rscrypto
gh attestation verify rscrypto-X.Y.Z.crate --repo loadingalias/rscrypto
gh attestation verify rscrypto-X.Y.Z-source.tar.gz --repo loadingalias/rscrypto
gh attestation verify rscrypto-X.Y.Z-ct-evidence.tar.gz --repo loadingalias/rscrypto
gh attestation verify rscrypto-X.Y.Z-repository-controls.json --repo loadingalias/rscrypto
gh attestation verify rscrypto-X.Y.Z-release-manifest.json --repo loadingalias/rscrypto
gh attestation verify SHA256SUMS --repo loadingalias/rscrypto
mkdir ct-evidence && tar -xzf rscrypto-X.Y.Z-ct-evidence.tar.gz -C ct-evidence
(cd ct-evidence && sha256sum --check CT-EVIDENCE-MANIFEST.txt)The crate downloaded from crates.io should have the same SHA-256 as the attested release artifact. The release identity manifest is the machine-readable join between the release's source, artifacts, evidence, and toolchain. The repository controls JSON records the expected policies, immutable-release setting, live branch and tag rulesets, effective rules on the default branch, capture time, and release commit. Its validation fields state whether each bypass list and the immutable-release setting were visible to the capturing token. The JSON is evidence of the release-time configuration, not a claim that GitHub settings cannot change later.