test(registry): a declared repository nobody can publish to is a failure #63

Merged
binjovi-bot merged 1 commit from sean/zot-publisher-writable-check into trunk 2026-09-10 12:14:56 +00:00 AGit
Owner

The ARM tools publish failed because build/node-tools-arm64 had no write policy: publish-item-node-tools-arm64 died in 1.8 s with 'POST /v2/build/node-tools-arm64/blobs/uploads/: DENIED'. The entry and the expected count are already fixed on trunk; this adds the check that catches the next one, plus the retention entry that was missed. The guard's hand-kept list of 30 names proves each listed name has a policy, but cannot prove the reverse -- a repository declared with the publisher left out of its policies passes every assertion, because its name is not in the list yet, and fails at publish time with the same DENIED. So this derives both facts from the rendered config: every declared repository except the deliberate catch-all must grant create or update to a non-admin identity, and the builder-images publisher must write exactly 30 build repositories. Two controls confirmed red. node-tools-arm64 also joins the ARM node chain in retention rather than falling to the catch-all; retention is still dryRun with gc false, so that changes nothing today. https://claude.ai/code/session_01KZoQin34jeyt6nDGqvJA76

The ARM tools publish failed because build/node-tools-arm64 had no write policy: publish-item-node-tools-arm64 died in 1.8 s with 'POST /v2/build/node-tools-arm64/blobs/uploads/: DENIED'. The entry and the expected count are already fixed on trunk; this adds the check that catches the next one, plus the retention entry that was missed. The guard's hand-kept list of 30 names proves each listed name has a policy, but cannot prove the reverse -- a repository declared with the publisher left out of its policies passes every assertion, because its name is not in the list yet, and fails at publish time with the same DENIED. So this derives both facts from the rendered config: every declared repository except the deliberate catch-all must grant create or update to a non-admin identity, and the builder-images publisher must write exactly 30 build repositories. Two controls confirmed red. node-tools-arm64 also joins the ARM node chain in retention rather than falling to the catch-all; retention is still dryRun with gc false, so that changes nothing today. https://claude.ai/code/session_01KZoQin34jeyt6nDGqvJA76
test(registry): a declared repository nobody can publish to is a failure
All checks were successful
binjovi/ci Binjovi completed the frozen plan
0ac0ccea75
THE ARM TOOLS PUBLISH FAILED BECAUSE A REPOSITORY HAD NO WRITE POLICY.
builder-images added node-tools-arm64, the ARM64 worker setup tools. The image
built and exported cleanly, and then publish-item-node-tools-arm64 died in
1.8 seconds:

  POST https://registry.sean.farm/v2/build/node-tools-arm64/blobs/uploads/:
  DENIED: requested access to the resource is denied

The entry and the expected count are already fixed on trunk. This adds the
check that catches the NEXT one, and the retention entry that was missed.

WHAT THE LIST CANNOT PROVE. The guard holds a hand-kept list of 30 image
names and asserts each has a policy. It cannot prove the reverse: a repository
declared in the manifest with the publisher left out of its policies passes
every assertion, because its name is not in the list yet, and it fails at
publish time with the same DENIED as no entry at all. A repository only
registry-admin can write is a repository no pipeline can publish to.

So this reads the rendered config rather than a hand-kept list: every declared
repository except the deliberate `**` catch-all must grant create or update to
some identity that is not registry-admin, and the builder-images publisher
must write exactly 30 build repositories. It cannot drift, because it derives
both facts from the config it checks.

Two controls confirmed red: the publisher dropped from an entry whose name is
listed, and a new repository that only registry-admin can write.

RETENTION. node-tools-arm64 now sits with the rest of the ARM node chain --
node-bootc-arm64, node-base-arm64, zfs-userspace-arm64 -- rather than falling
to the catch-all. Retention is still dryRun with gc false, so this changes
nothing today and is correct when either flips.

The coupling is recorded at the list, with the DENIED signature, because the
list is coupled to another repository and nothing enforces that coupling: an
image added to builder-images and to neither place here is not visible from
this tree.

Claude-Session: https://claude.ai/code/session_01KZoQin34jeyt6nDGqvJA76
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
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
sean/zot!63
No description provided.