Summary
The unsigned-commit-then-CI-sign evidence-publishing flow is internally contradictory with the on-disk pointer contract, and the signing script it relies on is effectively dead (and mis-fires on allowlist.yaml). This needs a maintainer design decision, not a one-line path fix.
Details
.github/scripts/evidence-sign-unsigned.sh (invoked by .github/workflows/evidence-publish.yaml:130) discovers "committed-but-unsigned" pointers and signs them in place. It globs the flat path:
for pointer in recipes/evidence/*.yaml; do
Three problems:
-
Wrong location. Every committed pointer in the tree is nested, e.g. recipes/evidence/h100-gke-cos-training/<source>/sha256-….yaml. The flat glob matches none of them — it matches only recipes/evidence/allowlist.yaml.
-
Mis-fires on the allowlist. allowlist.yaml is not a pointer and has no .attestations[0].signer, so the script's yq check returns "null", classifies it as "unsigned," and would attempt aicr evidence sign allowlist.yaml (which fails).
-
No relocation, and the flow is circular. The script signs in place and never moves the pointer to its derived nested path. But the nested <source> segment is SourceSlug(issuer, identity) — derived from the signer, which an unsigned (--no-sign) pointer does not have (pkg/evidence/attestation/pointer.go PointerCopyToHint returns guidance, not a path, when there is no signer). So an unsigned pointer cannot be committed at its nested path to begin with. The only coherent unsigned-commit design is commit-flat → CI-sign → CI-relocate-to-nested, and the relocate step does not exist.
-
Blocking gate rejects the intermediate. .github/workflows/evidence-pointer-contract.yaml runs on any recipes/evidence/** PR change and requires every committed pointer to be a signed, nested single-attestation V1 pointer under <recipe>/<source>/. A flat unsigned pointer is rejected on the PR before the signing leg can help.
Net: the commit-unsigned-then-CI-sign path cannot pass the contract gate as designed; the script that supports it scans the wrong location and trips on the allowlist.
Options (maintainer decision)
- Deprecate the unsigned-commit flow. Require contributors to sign locally and commit the nested signed pointer directly (or use the ingest-by-ref GP2 path), then delete
evidence-sign-unsigned.sh and its evidence-publish.yaml step.
- Make the flow coherent. Have the script scan nested pointer paths, exclude
allowlist.yaml, sign, and then relocate the now-signed pointer to its derived nested path — and reconcile that ordering with the blocking contract gate.
Related
Summary
The unsigned-commit-then-CI-sign evidence-publishing flow is internally contradictory with the on-disk pointer contract, and the signing script it relies on is effectively dead (and mis-fires on
allowlist.yaml). This needs a maintainer design decision, not a one-line path fix.Details
.github/scripts/evidence-sign-unsigned.sh(invoked by.github/workflows/evidence-publish.yaml:130) discovers "committed-but-unsigned" pointers and signs them in place. It globs the flat path:Three problems:
Wrong location. Every committed pointer in the tree is nested, e.g.
recipes/evidence/h100-gke-cos-training/<source>/sha256-….yaml. The flat glob matches none of them — it matches onlyrecipes/evidence/allowlist.yaml.Mis-fires on the allowlist.
allowlist.yamlis not a pointer and has no.attestations[0].signer, so the script'syqcheck returns"null", classifies it as "unsigned," and would attemptaicr evidence sign allowlist.yaml(which fails).No relocation, and the flow is circular. The script signs in place and never moves the pointer to its derived nested path. But the nested
<source>segment isSourceSlug(issuer, identity)— derived from the signer, which an unsigned (--no-sign) pointer does not have (pkg/evidence/attestation/pointer.goPointerCopyToHintreturns guidance, not a path, when there is no signer). So an unsigned pointer cannot be committed at its nested path to begin with. The only coherent unsigned-commit design is commit-flat → CI-sign → CI-relocate-to-nested, and the relocate step does not exist.Blocking gate rejects the intermediate.
.github/workflows/evidence-pointer-contract.yamlruns on anyrecipes/evidence/**PR change and requires every committed pointer to be a signed, nested single-attestation V1 pointer under<recipe>/<source>/. A flat unsigned pointer is rejected on the PR before the signing leg can help.Net: the commit-unsigned-then-CI-sign path cannot pass the contract gate as designed; the script that supports it scans the wrong location and trips on the allowlist.
Options (maintainer decision)
evidence-sign-unsigned.shand itsevidence-publish.yamlstep.allowlist.yaml, sign, and then relocate the now-signed pointer to its derived nested path — and reconcile that ordering with the blocking contract gate.Related