Repository navigation
Conversation
Set release_version to 0.4.0-rc.5 (stable_version 0.3.7 and the prerelease channel unchanged) and turn the Unreleased CHANGELOG section into the 0.4.0-rc.5 section, dated when the tag is cut. Add the merged changes it did not yet describe (installer verification bound to the requested tag; pinned validator build and stable-must-ship-RC-validator; per-report release gate; candidate-bound readiness evidence; CI token and gate hardening), migration notes and known limitations. The consumer-canary candidate pin stays on the published v0.4.0-rc.4 under check-canary-pins.sh's bootstrap rule until the rc.5 tag exists; it must be repinned in the designation change after verification. Signed-off-by: ErenAri <erenari27@gmail.com>
scripts/rc-preflight.sh reports each prerequisite of the RC sequence as VERIFIED, MISSING or PENDING with its evidence: metadata, recorded distribution decisions, tag and approved commit, tag build, staged assets, SHA256SUMS membership, validator identity against the lock, optional assets against the tagged policy, publication, designation on main, canary runs in their 20-30h/68-78h windows, scheduled campaigns, rollback drill, and the operator-held notes. It only issues GETs (plus downloading the SHA256SUMS text), never dispatches, writes or executes anything, aborts on a lookup failure instead of reporting it as pending, and never turns elapsed time into VERIFIED. Candidate binding stays with production-readiness-report.sh. scripts/rc-preflight_test.sh covers it in CI. Signed-off-by: ErenAri <erenari27@gmail.com>
CHANGELOG 0.4.0-rc.5: drop the readiness-sounding opening, scope the validator byte-identity rule to the recorded tree, state the readiness bindings as enforced (Falco validator binding reported only; an earlier candidate's incident note accepted), and date the "all undecided" note. rc-preflight.sh: - abort (exit 3) on any non-JSON API body, undecodable file contents or failed asset download instead of reporting a status; - a designated release-candidate canary must name the candidate's tag, commit, image digest (from the attested evidence), publication time and rcN artifact/report names, with no other candidate's names left; - canary and campaign runs count only from the time the designation landed on main (PR merge time), and a canary run only if a job after its plan job succeeded; - a rollback drill run stays PENDING: its candidate is not visible here. Tests cover each case. Signed-off-by: ErenAri <erenari27@gmail.com>
- A designated release-candidate canary must also use exactly the candidate commit in its published-action job; a pin left on another candidate keeps the designation PENDING. - A missing consumer-canary candidate marker is reported instead of ending the script silently; unparsable candidate evidence aborts with exit 3. - Canary windows accept a run started up to an hour before the window when its plan job accepted it (proven by an executed later job). - CHANGELOG known-limitations wording names the stable-blocking decisions. Tests cover the new cases. Signed-off-by: ErenAri <erenari27@gmail.com>
…chain rc-preflight.sh built its workflow-pin parser with GOTOOLCHAIN=local and GOSUMDB=off, so it worked only where Go at go.mod's version (1.26.9) is first on PATH. With an older Go on PATH (go 1.22 on the operator workstation) every post-publication run aborted (exit 3), and rc-preflight_test.sh failed 31 checks outside CI's setup-go image. Keep the build offline (GOPROXY=off, GOWORK=off, -mod=vendor) but leave GOTOOLCHAIN as the caller set it and keep checksum verification, so Go can switch to a go.mod toolchain already in the module cache. Verified with go 1.22 on PATH: the parser builds with go1.26.9 and the build makes no network connections. The helper now exits 2, with nothing on stdout, on a usage error or an unreadable file instead of printing PENDING, so the shell aborts rather than reporting a pin status it never judged. Tests: TestRunExitCodes; rc-preflight_test.sh checks the build environment and that an unusable Go aborts the report. Signed-off-by: ErenAri <erenari27@gmail.com>
- A candidate pin on a step or job whose if is literally false (false,
${{ false }}, 0, '', null) never runs, so it is PENDING, not VERIFIED.
- The build env pins GONOPROXY and GONOSUMDB to a pattern nothing matches,
so a caller GOPRIVATE or GONOPROXY cannot make Go download an unverified
toolchain despite GOPROXY=off.
- The canary's CANDIDATE_* bindings are read from the workflow-level env
node by the helper (env mode), and a job or step env may not rebind them;
run text can no longer stand in for them.
- Merge keys and case variants of jobs/steps/uses/if/env are rejected.
- The commit in a pin must be spelled exactly; the owner/repo match is
ASCII case-insensitive only.
- Correct the fetch comment and the build comment, which overstated that
no network is ever contacted.
Signed-off-by: ErenAri <erenari27@gmail.com>
- Any if that reads no context and calls no function is a constant; only the literal true runs. This covers every falsy spelling GitHub accepts (-0, 0.0, 0e0, 0x0, NaN, '', null, ...) instead of a fixed list. - A job that needs a job that can never run is skipped unless its if calls always(), failure() or cancelled(); the skip is carried through chains of needs. A needs value that is not a name or a list of names is rejected. - Case variants of workflow keywords (now including needs) are rejected only in the workflow, job and step mappings; env names, inputs and matrix keys are user data. - The canary designation detail shows the value the parser found instead of the expected one, and the build abort names GOSUMDB=off as a cause. Signed-off-by: ErenAri <erenari27@gmail.com>
…heck check-canary-pins.sh always ran check_pin stable "v<stable_version>": the stable pin had to carry that exact version comment and equal the commit the tag resolves to. With no bootstrap for the stable pin (DEF-1), the stable release PR (stable_version = release_version = X.Y.Z, release_channel stable) fails required CI because vX.Y.Z is not a tag yet, and the vX.Y.Z tag build fails too, because the pin would have to name the tagged commit itself, which that commit cannot contain. docs/production-release-process.md already says to repin the stable step once the new stable tag exists; the check made that sequence impossible to follow. Mirror the existing candidate bootstrap for the stable pin. With release_channel stable, while vX.Y.Z positively does not exist, or resolves to the commit under check (the tag build), the stable pin may instead name the most recent published stable release before X.Y.Z: the newest vA.B.C, A.B.C < X.Y.Z, listed by the GitHub releases API as non-draft and non-prerelease, whose tag resolves to the pinned full SHA. As for the candidate, a missing, mutable or short pin fails, and every lookup failure fails closed: an unreachable or malformed tag answer, a pinned tag that does not resolve, a failed or partial API listing, or a malformed line. Once vX.Y.Z exists at any other commit the rule is strict exactly as before, so the first commit after the tag must carry the repin. The prerelease channel's stable pin stays strict, and the candidate rules are unchanged. check-canary-pins_test.sh: section 27 no longer asserts that the stable pin never bootstraps; it now tags the new stable at another commit and asserts the previous stable pin is still refused there. Sections 29-38 cover the stable PR, the tag build (also with the v0.4.0 draft staged and after its publication), the strict commit after the tag and its repin, pins on the new or a later version, an older-than-latest stable, an rc, a draft, a release marked prerelease, a tag without a release, a mismatched SHA, a missing tag, mutable and short refs, a missing pin, each lookup failure, and the unchanged prerelease channel. The gh stub also answers the paginated release listing, for the exact projection the script requests. The new sections are red against the unmodified script and green here. The release process doc spells out the stable PR and tag build phase. Signed-off-by: ErenAri <erenari27@gmail.com>
…rser - An if that is empty or YAML null is no condition (GitHub runs it as success()), and true is matched case-insensitively, so such pins count. - Only always() and !cancelled() let a job run after a skipped need, and a status function inside a quoted string no longer counts. - Job IDs in needs compare case-insensitively; job IDs that differ only by case, needs on an unknown job, on the job itself or in a cycle are rejected, as GitHub rejects them. - The CHANGELOG states that conditions calling functions are left to the runner; doc comments no longer contain '' (gofmt turned it into a curly quote). Signed-off-by: ErenAri <erenari27@gmail.com>
The Stable Release runbook omitted two gates the stable tag build enforces (every blocks_stable distribution decision recorded; the stable validator byte-identical to the graduated RC's), and a v* tag cannot be reused after a refused build. It also had the operator record the promotion marker after checking the readiness report, while the report reads that record as an input; the record now comes first and the promotion dispatch stays a separate decision. Signed-off-by: ErenAri <erenari27@gmail.com>
…flight - The designated release-candidate canary must have a schedule cron that fires inside each timed window the plan job accepts, [T0+20h, T0+31h) and [T0+68h, T0+79h); the previous candidate's dated crons left in place would otherwise pass every other designation check while no timed canary can ever count. The helper gains a schedule mode with a small cron matcher (values, ranges, steps, lists, JAN-DEC/SUN-SAT names, the day-of-month/day-of-week OR rule); anything else is refused. - campaign-candidate.json on main must also record this release's SHA256SUMS and candidate-evidence digests, which the scheduled campaigns bind. - CHANGELOG: correct the rc.5 notes against the code: the Action's dot-directory rule is unchanged (now documented), only path and option inputs became literal (command is still run by the guest shell), the relink kit carries only the validator's own sources, the registry-ref rule needs a slash after the host, name:tag refs are affected too and the Action never pulled implicit short refs, the incident note's rejection is operator-asserted, the installer hardening is already live on main, and BPFCOMPAT_DOC_DIR/sudo behaviour. Signed-off-by: ErenAri <erenari27@gmail.com>
The manual verification commands were weaker than the installer they sit next to. cosign verify-blob accepted any tag's signature through an '@refs/tags/v' regexp, gh attestation verify had no --source-ref, and the README's container check used an unanchored identity regexp that any workflow SAN containing 'github.com/Kernel-Guard/bpfcompat' satisfies. - Blob signatures use the exact identity release-artifacts.yml@refs/tags/$VER, as scripts/install.sh does. - gh attestation verify pins --signer-workflow and --source-ref "refs/tags/$VER" (README, verifying-releases, SBOM check). - The README container check uses the exact signer of the pinned 0.3.7 image (ghcr-image.yml@refs/tags/v0.3.7) and says that 0.3.5-0.3.7 images were signed by ghcr-image.yml, while 0.3.8-rc.2, 0.4.0-rc.4 and later release-workflow images are signed by release-artifacts.yml. README and quickstart pin @v0.3.7, so behaviour that ships only from v0.4.0-rc.4 (promotion evidence, SBOM signature, Action attestation verification) or v0.4.0-rc.5 (license bundle, relink kit, build record, Action validator pinning, command-mode kernel-release INFRA_ERROR, missing artifact paths never pulled) now carries that version. Also: the arm64 CLI in the asset table; BPFCOMPAT_DOC_DIR and /usr/libexec/bpfcompat in the installer notes; image tag example 0.3.7; "the validator's own C sources" for the relink kit; the pinned validator build, stable==RC validator and distribution-decision gates in the production support boundary. Signed-off-by: ErenAri <erenari27@gmail.com>
The artifact input description now says that a workspace path whose first directory contains '.' or ':' (for example .output/) must be prefixed with ./, or it is treated as a remote reference. The binary input description says that an executable at that path in the workspace is used instead of the verified prebuilt CLI. Both behaviours are unchanged and already documented in docs/compatibility-contract.md; only description strings change. Signed-off-by: ErenAri <erenari27@gmail.com>
DEF-2, found by the independent review of DEF-1's fix: the release- consistency gate requires the stable PR's docs to name vX.Y.Z, while scripts/check-doc-pins.sh refused any tag-form pin whose tag does not exist, so the stable PR could not pass required CI even with the canary-pin fix. While release.yaml says release_channel stable, a tag-form doc pin on exactly v<stable_version> is accepted when the remote positively has no such tag (git ls-remote --exit-code returns 2). An unreachable remote, another version, a SHA pin or the prerelease channel still fail. The check gains its first regression test, run in CI. The runbook also states the stable order and that no other stable release may be published between the stable PR's CI and the tag build, because the stable pin may only name the newest published stable below X.Y.Z. Signed-off-by: ErenAri <erenari27@gmail.com>
production-readiness-report.sh required the Falco report's run.started_at to postdate the candidate's generated_at. The runner writes run.started_at when it assembles the report, after every target ran (runner.go:308-311); in each preserved real report it is minutes later than the first target's start. A run that began before the candidate was built could therefore be accepted as starting after it, contradicting the written rule that the Falco run must start after the candidate was built. The run now starts at the earliest of run.started_at and every target's started_at, and a report with a target that records no start time is refused. The manifest still names the report's run.started_at, unchanged. Signed-off-by: ErenAri <erenari27@gmail.com>
…led verification The gh path probed only `gh attestation --help`, but its tag-bound check also needs `gh attestation verify --signer-workflow` (gh 2.51.0) and `--source-ref` (gh 2.68.0), with gh's errors discarded. So a host with an older gh and no cosign got either "requires cosign or GitHub CLI (gh)" (gh < 2.49, although gh is installed) or "GitHub attestation verification FAILED" (gh 2.49-2.67), which reads like a tampered release. When cosign is absent and gh is present, ask gh's own help whether it has the attestation command and both flags (capability, not version parsing) and, if not, refuse naming what it lacks and the remedy (gh >= 2.68.0 or cosign). Still fail-closed; cosign is still preferred whenever present; a real verification failure still says FAILED; the cosign identity, the --source-ref value, checksum, license-bundle and sudo logic are unchanged. The tag guard matched line by line, so a BPFCOMPAT_VERSION carrying a valid tag on one line of a multi-line value passed it and was stopped only by curl. Reject any character a tag cannot hold before the regex, so the whole value is checked. install_test.sh: the gh shim can play gh < 2.49 / < 2.51 / < 2.68; new cases for each (and an unparsable version), for cosign still winning over an old gh, and for newline-bearing tags. Signed-off-by: ErenAri <erenari27@gmail.com>
…began Review follow-up for the tag-bound verification docs. - The gh attestation commands now need --source-ref, so the docs state the minimum: gh 2.68.0 (--signer-workflow arrived in 2.51.0, --source-ref in 2.68.0). An older gh stops with "unknown flag" without verifying anything; use cosign or upgrade. gh 2.97.0 and 2.102.0 fixed published advisories in how these values are matched, so 2.102.0 or later is recommended. - Promotion evidence, the SBOM signature and the Action's attestation check are published from v0.4.0-rc.2 and v0.3.8-rc.1 (not v0.3.7), not from v0.4.0-rc.4 as the qualifiers said. - The README lists every release-artifacts.yml-signed image (0.3.8-rc.1, 0.3.8-rc.2, 0.4.0-rc.2, 0.4.0-rc.3, 0.4.0-rc.4) and notes that the 0.3 and latest aliases carry the 0.3.7 signer while they point at 0.3.7. - BPFCOMPAT_DOC_DIR: the bundle goes to <BPFCOMPAT_DOC_DIR>/<tag>/. - The validator==RC and distribution-decision gates are stable-only. - Checksum checks use --ignore-missing where only some assets are downloaded; only bpfcompat-linux-amd64 carries an SBOM attestation. - action.yml: the artifact description also names a localhost first directory, as is_remote_artifact does. Signed-off-by: ErenAri <erenari27@gmail.com>
Fourth exact-head review findings: - A schedule entry with any key besides cron (GitHub's timezone, for one) is refused: only cron is evaluated, in UTC, so a timezone would move the real firing time outside the window while the check said VERIFIED. - Cron fields are stricter: names only in their own field, digits only (no +N), steps bounded by the field's range (a huge step crashed the helper), the documented a/n form, day of week 0-6, and a day field starting with * is unrestricted for the day-of-month/day-of-week rule, as in cron. - A campaign-candidate.json that is JSON but not an object aborts the report (exit 3) instead of ending it with a truncated jq error. - CHANGELOG: an empty if is no condition; the incident note's run is named, not checked. Signed-off-by: ErenAri <erenari27@gmail.com>
Review of the previous commit: the runner writes no started_at for a target it never runs, such as an optional profile the default runner cannot boot (status unsupported; fail in v0.3.7), and the shipped expanded matrix has three. Refusing every target without started_at would refuse such a legitimate report. Now every required or passing target must record started_at, and the earliest recorded start is the run's start. The runbook describes the rule, and the test gains a positive case shaped like a real report (targets start before run.started_at) with an unbooted optional profile. Signed-off-by: ErenAri <erenari27@gmail.com>
Written policy requires the rollback evidence to be the note rollback-drill.yml writes and the incident note to link the deliberately rejected promote-release run. The readiness report checked only that each was a well-formed run ID, so (verified by reproduction) a run ID that does not exist, or a successful ci.yml run, was accepted with attestations verified. With attestation verification on, the drill run must now be a successful rollback-drill.yml run on main and the incident run a completed promote-release.yml run that did not succeed; an unreadable run fails. Which rejection qualifies stays the operator's choice; a successful or unrelated run never does. The test-only bypass is unchanged. Signed-off-by: ErenAri <erenari27@gmail.com>
… stable window DEF-3, found by the independent review of f61c291's simulated stable PR: the docs/integrations templates are pinned by commit SHA (their README makes that policy), so they cannot name vX.Y.Z before its tag exists, and check-doc-pins.sh's one-version rule then refused the stable PR ("docs pin more than one bpfcompat version"). During the stable window -- the stable PR while vX.Y.Z is absent, and the vX.Y.Z tag build itself -- exactly one other documented version may sit beside v<stable_version>: the newest earlier stable release tag on the remote. Every later commit is strict, so the templates are repinned with the consumer canary once the tag exists (now stated in the runbook). check-release-consistency_test.sh no longer hard-codes 0.3.7 as the stable channel's version; it reads stable_version from release.yaml, so the stable PR does not have to edit the test. Signed-off-by: ErenAri <erenari27@gmail.com>
…ll is made Review follow-ups for the gh capability probe: - A gh that cannot run at all (every command fails, e.g. an unreadable ~/.config/gh/config.yml) was told it "lacks the 'gh attestation' command" and to upgrade. When `gh attestation --help` fails, the installer now also runs `gh --help`; if that fails too it refuses with "installed gh could not run 'gh --help' (<gh's first error line>); fix gh or install cosign". Still fail-closed; nothing else changes. - Comment: `gh attestation` exists, hidden, from gh 2.47.0 (public from 2.49.0). Behaviour is unchanged: on 2.47/2.48 the flag probe already names --signer-workflow and --source-ref as missing. install_test.sh: removing or neutralising one gh verify call used to go unnoticed (only the whole gh block and the bundle call were guarded). The gh stub now records every verify call; the gh-path install cases assert that exactly the installed assets (CLI, validator, and the license bundle when listed) were verified, each with the release workflow and --source-ref refs/tags/<tag>, and the cosign cases that gh verified nothing. New cases swap only the CLI, or only the validator, for bytes attested by another tag's run (SHA256SUMS regenerated to match), so each call must refuse on its own. Also: a gh that cannot run, with and without cosign, and a root `--help` in the stub as real gh has. Mutation check (scratch copies of install.sh): replacing a verify call with `true`, turning its `die` into a warning, or dropping its --source-ref, for each of the three calls, fails this suite (9/9); the previous suite caught 2/9. Signed-off-by: ErenAri <erenari27@gmail.com>
…and time Review of e033fe6: the drill lookup accepted a fork's pull-request run from a branch named main, any old rollback-drill run, and a note whose Completed time was unrelated to the run (2099). The drill run must now be a dispatched run of this repository that started after the candidate was built, with the note's Completed time inside it; the incident run must be a dispatched promote-release run of this repository. The runbook says the lookups need the network and an authenticated gh. Signed-off-by: ErenAri <erenari27@gmail.com>
Review of 35d6ebe: the Action falls back to a source build when gh cannot verify attestations only in the v0.4.0 release candidates; the v0.3.8 candidates' Action fails the job instead. Also name the error an older gh prints before 2.49.0 (unknown command "attestation"). Signed-off-by: ErenAri <erenari27@gmail.com>
gh 2.47.0 and 2.48.0 already run the hidden attestation command, so they fail with unknown flag, like every gh before 2.68.0. Signed-off-by: ErenAri <erenari27@gmail.com>
Review of 2273204 (full stable-sequence simulation): - DEF-4: check-release-consistency_test.sh still asserted that the current tree is on the prerelease channel, so the stable PR's required CI, the push to main and the v0.4.0 tag build's quality gates would fail. It now reads release_channel from release.yaml. - The stable window's second documented version is now the release the consumer canary's '# bpfcompat-pin: stable' step names, which check-canary-pins.sh verifies is the newest published stable release, so the two checks cannot disagree (a stable tag without a published release no longer qualifies), and no remote tag listing is involved. - Only commit-SHA pins count toward that second version; a tag-form pin on the previous release is refused as before. - The ci.yml step comment and the runbook describe the window precisely; docs name an unreleased version until promotion, not only until the tag. Signed-off-by: ErenAri <erenari27@gmail.com>
…s does Review of b9149e7: on a canary line with two version comments ("# v0.3.7 # v0.3.6"), check-canary-pins.sh read the first and check-doc-pins.sh the last, so the stable window could admit templates on a different release than the verified canary pin. Both now read the first, and a test pins it. Signed-off-by: ErenAri <erenari27@gmail.com>
…fication On the gh path every `gh attestation verify` failure was reported as "GitHub attestation verification FAILED for <asset>", with gh's output sent to /dev/null. A capable gh that is not logged in, holds a rejected token, or cannot reach the GitHub API got the same tampering-like words as a release whose attestation does not match (review S2). The installer now keeps four outcomes apart, and every one still refuses (exit 1, nothing installed, temp dir removed, no checksum-only fallback, no retry): 1. gh lacks the attestation command or flags: the existing help probe, "installed gh <ver> lacks ...", unchanged. 2. No usable credentials: "GitHub authentication needed for attestation verification: gh is not logged in (<gh's first line>)", or "GitHub rejected gh's credentials (HTTP 401)". 3. GitHub API out of reach: "GitHub API unavailable for attestation verification: no HTTP response | HTTP 5xx | HTTP 429, rate limited | HTTP 403, rate limited (<gh's first line>)". 4. Anything else, including no attestation for the digest (404), another signer workflow or source ref, or a digest mismatch: the existing "GitHub attestation verification FAILED for <asset>", unchanged. Classes 2 and 3 rest only on positive evidence from one read-only request made before the first verify call: `gh api --method GET --include repos/<repo>/attestations/sha256:<CLI digest>?per_page=1`, the endpoint that call queries, through the same gh, host, token and proxy. gh exits 4 only from its root auth check (internal/ghcmd/cmd.go, pkg/cmd/root/root.go at v2.68.0 and v2.102.0), which also guards `attestation verify` (only --bundle lifts it). With --include the first stdout line is the status line gh builds from the response, and a transport failure exits 1 with empty stdout. A 403 counts only with GitHub's X-Ratelimit-Remaining: 0 or Retry-After header. Any other answer leaves the decision to the verify calls, unchanged (same assets, --repo, --signer-workflow, --source-ref refs/tags/<tag>), whose failures other than exit code 4 always read "verification FAILED". gh's verify text is never parsed: with stdout not a terminal it prints only its final error, worded differently by 2.68 and 2.102 for the same 401 or 404, and on 2.102 a dead proxy fails before the API fetch with a Sigstore message. Probing before verifying, not after a failure, means a refusal can never be relabelled as an auth or network problem when the verify itself spends a nearly exhausted rate limit or a token expires in between. Cost: one extra GET on the gh path (about 240 KB for v0.3.7's CLI, next to about 22 MB of downloads). The cosign path is unchanged and still runs no gh command. gh's words are quoted as one line with control characters removed, also in the "could not run 'gh --help'" refusal (review R2-N2), and the gh_err and gh_ver captures tolerate an imported pipefail (R2-N1). install_test.sh: the gh stub answers the probe from the attestation DB and can play a gh that is not logged in (exit 4), holds a rejected token (401), gets no response, a 503, a 429, a rate-limited 403 (also with coloured headers) or a plain 403, a verify that exits 4, and a refusing verify whose stderr claims a network or auth error. 35 new cases check the class named, the absence of the other classes' wording, nothing installed, the installer's temp dir removed, no control bytes, exactly one probe made before any verify call, the cosign path untouched in every mode, and imported pipefail. The 45 existing cases pass unchanged; against f8809a9's install.sh the new suite fails 24 cases. All 34 mutants of the change are caught (a refusal reworded as network or auth, a die removed on the auth or network path, a 404 taken for an outage, the probe ignored or removed, S1 regressions on the new calls). Real gh 2.68.0 and 2.102.0 (official tarballs) with the real v0.3.7 assets: no token and an invalid token give class 2, a dead HTTPS proxy class 3, a modified CLI or rc.4's assets served as v0.3.7 class 4, and the real release installs. f8809a9 said "FAILED" for the first three. Signed-off-by: ErenAri <erenari27@gmail.com>
…date A tag's own copies of these checks are what run for that release (the release build, its promotion, and the Action pinned to it), so they must be in the commit that is tagged. - promote-release: compare SHA256SUMS.sig, SHA256SUMS.crt and the SBOM json/sig/crt in the draft with the candidate run's copies, and verify the cosign signatures (both blobs and the image) with the exact release-artifacts.yml@refs/tags/<tag> identity instead of any v* tag. The identity lines are the ones 4a51550 ships on the v0.3.8 line (v0.3.8-rc.1, v0.3.8-rc.2). The evidence attestation and verify-release-assets.sh are bound to the tag ref and expected commit. - verify-release-assets.sh: require BPFCOMPAT_EXPECTED_SOURCE_SHA and BPFCOMPAT_EXPECTED_SOURCE_REF and pass them as --source-digest and --source-ref (as in 4a51550). The tag build's two gates pass the run's commit and ref; maintenance-evidence already did. - action.yml: before verifying prebuilt assets, check that gh offers --signer-workflow, --source-ref and --source-digest, and resolve the pinned commit (the SHA pin itself, or git ls-remote of the tag, peeled). If either is unavailable, take the existing warned source build; never an unbound prebuilt install and never a job failure. - release-artifacts stable gate: require a completed rollback drill of the release's own candidate (drill alias and image digest), next to the incident term it already checks. - tests: new release-workflow-gates_test.sh (stable gate cases and the promotion steps' run bodies against stub cosign/gh/docker, plus caller wiring), bound-identity cases in verify-release-assets_test.sh, and tag/SHA-pin, ls-remote failure, gh capability and real-verifier cases in the Action tests. Signed-off-by: ErenAri <erenari27@gmail.com>
The RC canary installs through the gh path, where one transient 5xx or dropped connection at the read-only API probe ended the install, although gh's own verify retries a 5xx. The probe now makes up to three attempts, 3 s apart (6 s of waiting at most), for a 5xx or no HTTP response only, and the last attempt decides. A 429 or rate-limited 403 is not retried: GitHub asks the client to wait longer than an installer should, and asking again sooner spends more of the limit. 401, 404 and 200 are answers. Every outcome still refuses or verifies as before. Text quoted from gh is reduced to printable ASCII by an allow-list (octal, because busybox tr has no [:print:] class), so C1 controls such as a raw or UTF-8 encoded CSI no longer pass. Tests: retry sequences (5xx then 200; 502, 503, 200; no response then 200; 5xx then 401, 429 or 404; no response then a rate-limited 403 or a refusal) with exact attempt counts and waits; C1 bytes in both quoting places; and the defensive branches no case pinned: a probe exit other than 1 or 4 with no output, exit 1 with output that is no status line, HTTP/1.1 status lines, and a verify exit other than 0, 1 or 4. Signed-off-by: ErenAri <erenari27@gmail.com>
The promotion cases only used signatures from v0.4.0-rc.4 and v9.9.9-rogue, which no regexp derived from the v0.4.0-rc.5 identity matches. A regexp of the identity, even start-anchored, therefore passed every case, although it accepts a signature from a newly created tag such as v0.4.0-rc.50-rogue. - release-workflow-gates_test.sh: sign SHA256SUMS, the SBOM and the image, one site at a time, by four identities that are close to but not exactly this tag's release run: a tag extending the release tag, a tag differing only at a dot, the release tag in another repository, and another workflow on the release tag. For the blobs the run artifact and the draft agree, so the identity check is the only barrier. Each must be refused for the identity reason, and every identity must be used at all three sites. - verify-release-assets_test.sh: a tag ref under another ref and a tag ref with a trailing path are usage errors, which pins both anchors of the ref check. No workflow or script changes; the exact --certificate-identity already refuses every new case. Signed-off-by: ErenAri <erenari27@gmail.com>
Every curl in the prebuilt step has --connect-timeout and --max-time, but the git ls-remote that resolves a tag pin to its commit had no bound: a stalled HTTPS connection would hold the consumer's job until the job timeout instead of taking the warned source build. Run it under timeout 60, the bound of the tags API call in the same step. An expired limit is an ls-remote failure, which already takes the warned source build; verification and the prebuilt install are unchanged. git's low-speed limit (GIT_HTTP_LOW_SPEED_*) was not used: it aborts a stalled transfer but not a stalled TLS handshake, which libcurl only ends at its 300 s connect timeout. The Action test records, for every ls-remote, the prompt setting and the command line of the process that ran git, and requires timeout(1) with a limit of at most 300 s, the step's longest curl bound. Signed-off-by: ErenAri <erenari27@gmail.com>
On the gh path SHA256SUMS is unsigned, and gh attestation verify matches an artifact by digest only. So a party able to change release assets could delete the validator or license-bundle line (a CLI-only install with a false "not published" note), or serve one genuine asset of the tag under another asset's name, and the install still passed. The CLI's verify call now also prints the subjects of its verified provenance (--format json --jq, as "<sha256> <name>" lines), which name every asset the tag's release run attested. The installer refuses, as a failed verification naming the mismatch, when SHA256SUMS and those subjects disagree on whether the validator or the license bundle exists, or when a file to be installed is not attested under its own name. That call's stderr is still kept for the auth classification. The cosign path is unchanged. Tests: the gh stub prints the subjects of every statement that holds the verified digest. Refused: the validator line deleted (v0.3.7 and rc.5 shapes), the bundle line deleted, the arm64 CLI served as the amd64 CLI, the CLI served as the validator, an arm64 validator line added on an aarch64 host, and bytes from another statement of the run served as the validator or as the CLI; each also refused by cosign. Installed: a CLI-only install, an aarch64 host and a re-run release with two statements. Signed-off-by: ErenAri <erenari27@gmail.com>
Written policy says a canary is a run of the temporary release-candidate-canary workflow, the timed ones from its dated schedule, and that its plan job admits T+24h only 20-30 and T+72h only 68-78 whole hours after publication. Readiness checked only fields the canary evidence reports about itself and made no run lookup. A ci.yml, branch, fork, dispatched, failed or nonexistent run was accepted, and a partial re-run of a failed in-window run, which keeps the plan job's earlier decision, could write evidence long after the window closed (F6). With attestation verification on, readiness now reads publication (T0) from the candidate release, which must be published, not a draft, and not before generated_at. For each canary it reads the run, its attempts and the chosen attempt's jobs: - the run is a successful release-candidate-canary.yml run of this repository from workflow_dispatch (manual) or schedule (timed, on main); - the evidence's completed_at falls inside a successful attempt, in which the "Assemble RC canary evidence" job itself ran and succeeded; - that attempt started at or after T0, and a timed attempt started before its window closed (T0+31h, T0+79h); a first attempt, which ran its own plan job, also completed after the window opened (T0+20h, T0+68h). The attempt, its start, the run's branch and T0 are recorded in the JSON and Markdown. No written rule names the manual dispatch's branch, so it is recorded, not enforced. A re-run started inside the window is accepted, as the windows are written; whether re-runs should count stays an open operator decision. The bypass mode is unchanged. The test's gh stub answers the new lookups in this same commit, because the test runs in ci.yml and in the release quality gates. The runbook no longer says the plan job alone bounds re-runs and describes the new checks. Signed-off-by: ErenAri <erenari27@gmail.com>
…0-rc.5 landing branch Source head: 69cebea. Merged unchanged; no conflict resolution. Signed-off-by: ErenAri <erenari27@gmail.com>
…0.4.0-rc.5 landing branch Source head: 2269647. Merged unchanged; no conflict resolution. Signed-off-by: ErenAri <erenari27@gmail.com>
…0.4.0-rc.5 landing branch Source head: 4cb4516. Merged unchanged; no conflict resolution. Signed-off-by: ErenAri <erenari27@gmail.com>
…0.4.0-rc.5 landing branch Source head: 5794ee4. Merged unchanged; no conflict resolution. Signed-off-by: ErenAri <erenari27@gmail.com>
…0-rc.5 landing branch Source head: e1cbd97. Merged unchanged; no conflict resolution. Signed-off-by: ErenAri <erenari27@gmail.com>
… run lookups Integration-only commit on the v0.4.0-rc.5 landing branch; not part of any source PR. Reconciles #268 (fdae550) with claude/release-provenance-binding (5794ee4). #268's production-readiness-report.sh looks up the candidate release and every canary run, attempt and evidence job in attested mode, and records candidate.published_at and each canary's workflow_run_attempt, attempt_started_at and head_branch. The provenance branch's stable gate in release-artifacts.yml ("Stage production readiness evidence") read only `.canaries | length == 3`, so a readiness record generated by a checkout from before those lookups (main 402d7c9, #268 7f7a4ef) still passed the v0.4.0 tag build. The gate now also requires every canary's workflow_run_attempt to be a positive integer and its attempt_started_at a UTC timestamp, and candidate.published_at a UTC timestamp. The attested report writes these only after the lookups succeed (production-readiness-report.sh:390-391, :492-504, :600-602, :858 at fdae550); the attestation bypass writes null and older reports omit the keys. head_branch is not required: the manual canary's branch is recorded, not enforced. No other gate term changes. scripts/release-workflow-gates_test.sh: the fixture is regenerated as the unedited attested output of this branch's production-readiness-report.sh for production-readiness-report_test.sh's input. 14 new refusals: no lookups at all, the bypass's nulls with attestations_verified true, and each field absent, null or malformed on one or every canary. 2 new acceptances: an in-window re-run recorded as attempt 2, and a manual canary dispatched on a branch. Against the unchanged gate exactly the 14 refusals fail; with the new terms the test passes, and production-readiness-report_test.sh still extracts the gate and accepts its freshly generated attested output. Signed-off-by: ErenAri <erenari27@gmail.com>
…e-run voiding Integration-only commit on the v0.4.0-rc.5 landing branch; not part of any source PR. Reconciles claude/release-provenance-binding (5794ee4) with #267 (2269647) and #268 (fdae550): wording each owner deferred to the union. - action.yml `prebuilt` description (#267 owns the input descriptions, the provenance branch the prebuilt step): the attestations are bound to the pinned release's tag and commit, and gh older than 2.68.0, a tag pin that cannot be resolved to its commit, or a resolution taking more than 60 s gives the warned source build (action.yml:223-230, :241, :251-259 at 5794ee4). Provenance review N3. - docs/verifying-releases.md (#267's Action section): from v0.4.0-rc.5 the Action passes --source-ref refs/tags/<tag> and --source-digest <commit>, with the same fallbacks; the v0.4.0-rc.2 to rc.4 Actions do not bind the tag. T3 remedy item 5. docs/compatibility-contract.md is unchanged: with the provenance branch its rc.5 sentence ("other bytes fail closed") is accurate as written. - docs/production-release-process.md (#268's Canaries bullet): GitHub reports a run's conclusion from its latest attempt, so a failed re-run of a successful canary voids it under the readiness run check (production-readiness-report.sh:482 at fdae550). Readiness review N1. make check-docs-drift, scripts/check-doc-pins.sh and scripts/check-doc-pins_test.sh pass. Signed-off-by: ErenAri <erenari27@gmail.com>
…ranch Integration-only commit on the v0.4.0-rc.5 landing branch; not part of any source PR. Reconciles #265's rc.5 section (e1cbd97; #265 owns CHANGELOG.md) with what the other sources bring into this branch: #266 (4cb4516), #267 (2269647), #268 (fdae550), #269 (69cebea), claude/release-provenance-binding (5794ee4) and this branch's stable-gate commit 4a1466d. The heading and its TBD date are unchanged. - Corrects CHANGELOG.md:110-112 at e1cbd97 ("asserted by the operator, not checked"): with attestation verification on, #268 reads the incident run and requires a completed, dispatched promote-release.yml run of this repository that did not succeed (production-readiness-report.sh:685-688 at fdae550). Decision-packet critique M3. - Security: the installer's gh path checks SHA256SUMS against the CLI's attested subjects (#269); the Action and verify-release-assets.sh bind attestations to the tag ref and commit, promotion checks the signature files and the exact signer identity, and the stable gate requires the rollback drill and the canary run lookups (provenance branch, 4a1466d). - Fixed: the installer's gh failure classes, probe retry and whole-value tag check (#269); the tag-bound manual verification commands (#267). - Changed: #268's Falco start and drill, incident and canary run lookups; #266's stable-window consistency checks and runbook order. - Migration notes: the Action's prebuilt path needs gh 2.68.0 or later (and git and timeout for a tag pin) or takes the warned source build; verify-release-assets.sh callers must set its two variables; a readiness record from an earlier report must be regenerated. The wording starts from the writers' proposed lines (installer round 2, provenance round 1, readiness R1) and was checked against the code on this branch. Signed-off-by: ErenAri <erenari27@gmail.com>
|
Important Draft PR not reviewedDraft PRs are not automatically reviewed by default.
To automatically review draft PRs, update your CodeRabbit configuration: reviews:
auto_review:
drafts: true
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
DRAFT landing candidate for v0.4.0-rc.5. Merge only on an exact-head operator decision. Nothing here approves a tag, a release, a promotion, a designation or qualification. rc.4 stays NO-GO; its evidence never counts for rc.5.
What this branch is
main402d7c90b6c0plus six--no-ffmerges of unchanged source heads, then three attributed integration-only commits.claude/installer-gh-capability69cebeae6791d3c8f016cdb0a10ed3b68bce0778scripts/install.shonmain). It names a missing gh capability. Missing or invalid gh authentication (class 2) and an unreachable GitHub API (class 3) are now reported apart from a refused verification (class 4); every class still refuses to install. The probe is retried, bounded, on a 5xx or no response. gh-path installs are bound to the release's attested asset names (R-B)claude/docs-tag-bound-verification2269647bdb10954a603ea62e2b8e41fa0be91815claude/readiness-falco-earliest-targetfdae550d4d24c920f19e7f6f3a26290bcdc184d1published_at, and the plan job's stated upper bound for timed canariesclaude/stable-canary-pin-bootstrap4cb4516bda42cf4877397518c5f780ded8dfe54aclaude/release-provenance-binding(no separate PR)5794ee48babe3dc1e41b6850605ec8c253b3485bpromote-release.ymlverifies the exact signer identityrelease-artifacts.yml@refs/tags/<tag>and cmp's the signature and SBOM files against the run artifact.verify-release-assets.shand its callers, including the Action, bind attestations to the exact tag ref and commit (ported from the v0.3.8 maintenance line). The stable gate requires a completed rollback drill of this candidateclaude/rc5-preflight-toolchaine1cbd9748e2878033c60bf2cc1e405890d1a4dcerelease.yaml0.4.0-rc.5, the rc.5 CHANGELOG section, the offline AST-based RC preflight4a1466d94f0333action.ymlprebuilt,docs/verifying-releases.md); a re-run of a successful canary can void it (docs/production-release-process.md)0826cdeTBDNot included, on purpose:
115a3745) plus six fix commits, so that commit is in this branch only as release: prepare v0.4.0-rc.5 metadata and an offline, AST-based RC preflight (replaces #264) #265's ancestor. release: prepare rc.5 metadata and verify active preflight pins #264 itself is untouched.distribution/), candidate pins, schedules and crons, and the validator C sources (validator/).Source-to-union proof (LOCAL)
Ancestry: every source head above is an ancestor of this head and equals its live GitHub head. Each merge commit's tree equals a clean re-merge with zero conflicts.
Shared files: three files have more than one owner:
.github/workflows/ci.yml: release: prepare v0.4.0-rc.5 metadata and an offline, AST-based RC preflight (replaces #264) #265, release: let the stable PR and tag build pass the stable canary-pin and doc-pin checks #266 and provenance;action.yml: docs: bind manual release verification to the exact tag and mark rc-only behaviour #267 and provenance;docs/production-release-process.md: release: let the stable PR and tag build pass the stable canary-pin and doc-pin checks #266 and readiness: date the Falco run from its first target; look up the drill, incident and canary runs #268.At the merge-stage head
dd4164a, each contains every source hunk and no outside line. Every single-owner file is byte-identical to its owner.Integration commits: the only content outside the sources is the three integration commits above. They touch exactly
release-artifacts.yml,scripts/release-workflow-gates_test.sh,action.yml,docs/verifying-releases.md,docs/production-release-process.mdandCHANGELOG.md.Protected surfaces: changed only by their owners.
release.yamlandCHANGELOG.mdare changed by release: prepare v0.4.0-rc.5 metadata and an offline, AST-based RC preflight (replaces #264) #265 (and I1)..github/workflows/release-artifacts.ymlandpromote-release.ymlare changed by provenance (and I3).scripts/install.shis changed by install.sh: tell gh capability, auth and API failures apart from a refused verification; bind gh-path installs to attested names #269, andproduction-readiness-report.shby readiness: date the Falco run from its first target; look up the drill, incident and canary runs #268. Unchanged: the canary and campaign workflows,.github/campaign-candidate.json, every scheduled workflow,distribution/,validator/,go.mod,go.sumandvendor/.Validation at this head: all LOCAL, none hosted
CI replay: a replay of every
run:step ofci.yml's test and build jobs ran 43 steps with 0 CI failures. Skipped:go mod download(vendored, offline). It covered actionlint, all shell regressions (installer 110 cases, release-workflow gates 66),go vet,go test -race, the coverage floor and the toolchain proof. The extra non-CI shellcheck reports SC2034 atscripts/check-canary-pins_test.sh:223;mainhas the same warning.rc.5 tag build's "Release quality gates": the commands, replayed locally, all pass. That includes govulncheck: "No vulnerabilities found".
Real gh 2.68.0 and 2.102.0 (official, checksum-verified binaries; real v0.3.7 and rc.4 release bytes from a checksum-verified local mirror):
refs/tags/v0.4.0-rc.4with its commit; refuses it forrefs/tags/v0.4.0-rc.5or the wrong commit.Cross-component checks:
release.yamlrc.5, CHANGELOG,make check-release-consistencyand the canary and doc pins pass. The candidate pin stays on rc.4 by the bootstrap rule until the rc.5 tag exists; a simulated local tag build passes.Independent exact-head review of
0826cde: GO, no BLOCKER, no SHOULD-FIX, 5 NITs:ci.ymlcomment is not updated;Every source head also had its own independent review, with no open BLOCKER or SHOULD-FIX.
Local equivalents of the 9 required contexts, run on this exact head (2026-10-19, local only, on the maintainer's host; hosted Actions are locked): all 9 PASS, and the non-required "Action diff mode (end-to-end)" job passes too. The jobs come from this commit's own workflow files, run step by step through a local job runner, against a local
refs/pull/270/mergeequivalent.main, the same harness matched the hosted jobs step for stepmain's full analysis;main's counts matched the hosted rune0e7fcf8. There is no apt step, and it ran on the host kernelPlanted failures (a data race, an unchecked error, a path-injection sink, a broken VM check, a deleted gate workflow) were each reported FAIL or NOT-RUN. This is local evidence, not hosted CI; GitHub's branch protection does not see it.
Never claimed here: hosted CI; tag-only signing or attestation; a VM or image qualification; any real-release consumer run at this head.
What merging changes immediately
mainswitch to this version.release.yamlreads0.4.0-rc.5. It can be reverted, and nothing is published.main. While the billing lock lasts, no workflow runs at all.Decisions this PR does not make
Before the
v0.4.0-rc.5tag (the tag's copies of these workflows execute, and tags are immutable):Before T0+20h: the timed-canary re-run question (tighten, keep as written, or relax).
The full ordered list is in the operator's decision packet.