ContextSec releases fail closed. A tag is not sufficient authorization to publish.
- GitHub Immutable Releases is enabled for the repository. The release job proves this before building by calling
GET /repos/{owner}/{repo}/immutable-releases; a missing credential, 404, orenabled != truestops the release. - The
releaseenvironment requires a manual reviewer and accepts onlyv*tags. Its environment secretRELEASE_ADMIN_TOKENmust have repository Administration read permission only, because GitHub's immutable-release status endpoint requires admin read access andGITHUB_TOKENcannot request that permission. mainprotection and thev*tag ruleset remain active.- The release tag is an annotated or signed tag whose peeled commit is exactly the current remote
mainHEAD. An ancestor ofmainis not releasable.
After approval, the job has 30 minutes. It reuses the same complete security proof as pull requests, verifies the immutable-release precondition, verifies exact-main identity, builds the archive twice, and emits release-evidence.json. That evidence binds the source commit, tool/detector/checker versions and semantic digests, verification-coverage summary, workflow run, and release archive digest.
The archive, evidence, and checksum manifest are covered by GitHub artifact attestations. Local and re-downloaded verification pins the release workflow identity, exact source commit, and GitHub-hosted runner before publication. The final step verifies GitHub's immutable Release attestation, which binds the published tag and all assets.
The attestation-availability loop can wait up to 10 minutes. The 30-minute job timeout intentionally leaves room for checkout, builds, attestations, draft verification, and service latency.