fix(release): annotate the index and mirror cosign's accessories to GHCR #41

Merged
ryangr0 merged 1 commit from fix/oci-annotations-and-signed-mirror into development 2026-08-26 09:06:08 +00:00
Owner

Blocked on webgrip/workflows#53 — needs v1.11.0 for the annotations and copy-accessories inputs. The pins here anticipate that version and must be confirmed after it is cut.

ADR-0020 got the decision right and the mechanism wrong

GHCR resolves a package's repository link from the manifest. A Dockerfile LABEL lands in each platform's image config — which docker inspect reads and a registry does not.

mechanism result
chart Helm writes a real OCI annotation linked ✅
image Dockerfile LABEL orphaned through rc.24–rc.27, label correct the whole time ❌

The Confirmation gate shared the mistake. It asserted the label, so it was green on all four of those releases. A gate that passes while the thing it guards is broken is worse than no gate. It now also asserts .annotations["org.opencontainers.image.source"] on the raw index — checked against an un-annotated index, where it fails, so it would have caught this.

Annotations are set at build time, and that was measured, not assumed: a plain imagetools create preserves index annotations and leaves the index digest byte-identical, so one annotation reaches all three registries while ADR-0020's identical-digest property stands. Annotating during the copy would have diverged GHCR from Harbor.

The LABEL stays — it is what docker inspect surfaces. Two readers, two mechanisms.

GHCR was an unsigned mirror of a signed image

imagetools create copies the manifest list, so BuildKit's SLSA provenance and SBOM ride along inside the index for free — both were already on GHCR. cosign's signature and CycloneDX attestation live as separate .sig/.att tags and were left behind.

copy-accessories: true mirrors them. Verified that a copied signature still verifies at the destination, because cosign checks the digest. Its payload keeps the Harbor reference, so Kyverno goes on verifying against Harbor — which it already does, so no policy change.

ADR amendment

Append-only, per the repo's ADR conventions: the body stands as decided, two dated entries record the corrected mechanism and the measurement behind it, frontmatter date moves, and the index row mirrors it. internal/ledger (the ADR consistency validator) passes.

Gates

go build / vet, gofmt clean, internal/ledger, helm lint and all three chart renderings.

Verifying after release

docker buildx imagetools inspect --raw ghcr.io/webgrip/ploegd:<v> | jq .annotations
crane ls ghcr.io/webgrip/ploegd | grep '\.sig$'      # or the tags API
**Blocked on webgrip/workflows#53** — needs v1.11.0 for the `annotations` and `copy-accessories` inputs. The pins here anticipate that version and must be confirmed after it is cut. ## ADR-0020 got the decision right and the mechanism wrong GHCR resolves a package's repository link from the **manifest**. A Dockerfile `LABEL` lands in each platform's image *config* — which `docker inspect` reads and a registry does not. | | mechanism | result | |---|---|---| | chart | Helm writes a real OCI annotation | linked ✅ | | image | Dockerfile `LABEL` | orphaned through rc.24–rc.27, label correct the whole time ❌ | **The Confirmation gate shared the mistake.** It asserted the label, so it was green on all four of those releases. A gate that passes while the thing it guards is broken is worse than no gate. It now also asserts `.annotations["org.opencontainers.image.source"]` on the raw index — checked against an un-annotated index, where it **fails**, so it would have caught this. Annotations are set at **build** time, and that was measured, not assumed: a plain `imagetools create` preserves index annotations and leaves the index digest byte-identical, so one annotation reaches all three registries while ADR-0020's identical-digest property stands. Annotating during the copy would have diverged GHCR from Harbor. The `LABEL` stays — it is what `docker inspect` surfaces. Two readers, two mechanisms. ## GHCR was an unsigned mirror of a signed image `imagetools create` copies the manifest list, so BuildKit's SLSA provenance and SBOM ride along **inside** the index for free — both were already on GHCR. cosign's signature and CycloneDX attestation live as separate `.sig`/`.att` tags and were left behind. `copy-accessories: true` mirrors them. Verified that a copied signature still verifies at the destination, because cosign checks the digest. Its payload keeps the **Harbor** reference, so Kyverno goes on verifying against Harbor — which it already does, so no policy change. ## ADR amendment Append-only, per the repo's ADR conventions: the body stands as decided, two dated entries record the corrected mechanism and the measurement behind it, frontmatter `date` moves, and the index row mirrors it. `internal/ledger` (the ADR consistency validator) passes. ## Gates `go build` / `vet`, `gofmt` clean, `internal/ledger`, `helm lint` and all three chart renderings. ## Verifying after release ```bash docker buildx imagetools inspect --raw ghcr.io/webgrip/ploegd:<v> | jq .annotations crane ls ghcr.io/webgrip/ploegd | grep '\.sig$' # or the tags API ```
fix(release): annotate the index and mirror cosign's accessories to GHCR
All checks were successful
On Pull Request / checks (pull_request) Successful in 50s
da548a145e
ADR-0020 got the decision right and the mechanism wrong. GHCR resolves a
package's repository link from the MANIFEST; a Dockerfile LABEL lands in each
platform's image config, which `docker inspect` reads and a registry does not.
The chart linked because Helm writes a real OCI annotation. The image did not,
and stayed orphaned through rc.24-rc.27 with the label set correctly the whole
time.

The Confirmation gate shared the mistake — it asserted the label, so it was
green on all four of those releases. A gate that passes while the thing it
guards is broken is worse than no gate, so it now also asserts
.annotations["org.opencontainers.image.source"] on the raw index. Checked
against an un-annotated index: it fails, meaning it would have caught this.

Annotations are set at BUILD time, not at copy time, and that was measured
rather than assumed: a plain `buildx imagetools create` preserves index
annotations and leaves the index digest byte-identical, so one annotation
reaches Harbor, Forgejo and GHCR while ADR-0020's identical-digest property
stands. Annotating during the copy would have diverged GHCR from Harbor.

The LABEL stays. It is what `docker inspect` surfaces; the annotation is what
registries read. Two readers, two mechanisms.

Separately, GHCR was carrying an unsigned mirror of a signed image.
`imagetools create` copies the manifest list, so BuildKit's SLSA provenance and
SBOM ride along inside the index for free — both were already there — but
cosign's signature and CycloneDX attestation live as separate .sig/.att tags and
were left behind. copy-accessories mirrors them with `cosign copy`. A copied
signature verifies at the mirror because cosign checks the digest; its payload
keeps the Harbor reference, so Kyverno goes on verifying against Harbor, which
it already does.

ADR-0020 is amended append-only: the body stands as decided, two dated entries
record the corrected mechanism and the measurement behind it, and the index row
mirrors the new date.

Requires webgrip/workflows v1.11.0 (PR #53) for the `annotations` and
`copy-accessories` inputs.

Gates: go build/vet, gofmt clean, internal/ledger (the ADR validator), helm lint
and all three chart renderings.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
ryangr0 merged commit 142f3f33bc into development 2026-08-26 09:06:08 +00:00
Commenting is not possible because the repository is archived.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
webgrip/ploeg!41
No description provided.