Repository navigation
Description
🤖 Generated by the Agentic Engineer
Evidence
Every check we had passed on a cosign matcher that verified zero artifacts, and it halted all
GitOps delivery on prod for ~5.5 hours (#3005). The config was schema-valid, docs-valid, correctly
targeted, enabled, and constrained a signer — it satisfied a purpose-built validator written for
exactly this control. The defect was only visible in status.conditions on the live cluster, after
the deploy.
#3001 fixes that instance and adds an arity guard so the specific shape cannot return. It also
demonstrates efficacy — the artifact currently on latest is confirmed via the public Sigstore
transparency log to be signed by a publisher the matcher accepts. That demonstration was manual and
one-off. Nothing asserts it on the next deploy, or the one after.
Affected audience and impact
Anyone operating this cluster. The remaining gap is the general form of the incident: a matcher can be
well-formed and still match nothing — a wrong org, a renamed workflow, a changed trigger ref, a
publisher moved behind a reusable workflow (which relocates the OIDC subject to the called
workflow's path, as the aws and wedding-app OCIRepositories already document). Each of those
produces a control that is present, green, and inert, and the failure lands on the root source where
it is self-locking (#3006).
Expected behaviour
A change to a cosign matcher should fail in CI when the matcher would not verify the artifact it
guards — before it reaches a cluster, not after.
Acceptance criteria
- CI verifies the configured matcher against the real artifact it guards, and fails the build when
verification does not succeed. In-repoGITHUB_TOKENcan read the org's packages, so this needs
no new credential for first-party artifacts. - The check reads issuer and subject from the manifest rather than restating them, so the
thing tested is the thing deployed. - A negative control proves non-vacuity: a deliberately wrong subject must fail the check. Without
this the check is indistinguishable from one that always passes — which is precisely the failure
class being closed. - The transparency-log route is considered as the primary or fallback mechanism. It needed no
registry credential at all and is what settled fix(security): enforce cosign verify on the live root OCIRepository #3001;cosign verifyagainst GHCR returned
UNAUTHORIZEDfrom an unprivileged context. - Covers the root
OCIRepositorymatcher in both halves (cluster config + FluxInstance patch),
and states explicitly whether the four app/tenant matchers are in scope.
Size
Medium. The mechanism is understood and the evidence path is proven manually; the work is wiring it
into CI, keeping it non-flaky, and proving it is not vacuous.
Related: #3005 (the incident), #3006 (why it could not self-recover), #3001 (the fix that motivated
this), #2818 (which signer revisions should be acceptable — a related but distinct question).
Metadata
Metadata
Assignees
Labels
Type
Fields
Projects
- StatusShow more project fields✅ Done
Activity
coderabbitai commentedon Aug 6, 2026
🔗 Related PRs
#2319 - feat(security): cosign-verify the infrastructure Flux OCI artifact [merged]
#2404 - fix: tenant artifact verification rejects the dual signing identities from
#2399[merged]#2816 - fix(security): reject floating workflow refs in cosign subject matchers [merged]
#2919 - fix(security): move Flux cosign verify to the path KSail reads [merged]
#2930 - feat(ci): verify published evidence before promoting the prod digest [merged]
📝 Issue Planner
Check the box below or use the
@coderabbitai plancommand to generate an implementation plan and prompts that you can use with your favorite coding assistant.🧪 Issue enrichment is currently in open beta.
You can configure auto-planning by selecting labels in the issue_enrichment configuration.
To disable automatic issue enrichment, add the following to your
.coderabbit.yaml:💬 Have feedback or questions? Drop into our discord!
chore: agent-claim #3007 nonce=4e0f6b3baa5a5850a5dff8094d30f96f
fix(ci): let cosign do the matching instead of parsing identities out…
test(ci): prove the negative control refused, rather than merely failed
fix(ci): hand the matcher over as JSON so a multiline value cannot be…