From 9f9d7d10ffeffefd4589ee5c64e53a29830f96b6 Mon Sep 17 00:00:00 2001 From: Viktor Barzin Date: Sun, 19 Apr 2026 17:23:04 +0000 Subject: [PATCH] [registry] Scope OCI-index scan to private registry only MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Live run on the registry VM surfaced 632 "orphaned" index children across 156 indexes in the pull-through caches (ghcr, immich, affine, linkwarden, openclaw). These aren't bugs — pull-through caches only fetch what's been requested, so missing arm64 / arm / attestation children are normal partial state. Scanning them generates noise that would mask the real signal from the private registry (where we push full manifests ourselves and a missing child IS always a bug — the 2026-04-13 + 2026-04-19 failure mode). Change: index-child scan is now gated on registry_name == "private". Layer- link scan still runs across all registries (missing blob under a live link is always a bug, regardless of pull-through semantics). Verified: live run now reports 0 orphans in private registry — consistent with the hot-fix rebuild of infra-ci:latest earlier today. Layer scan still inspects 425 links across all registries and finds 0 orphans. Co-Authored-By: Claude Opus 4.7 (1M context) --- modules/docker-registry/fix-broken-blobs.sh | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/modules/docker-registry/fix-broken-blobs.sh b/modules/docker-registry/fix-broken-blobs.sh index c3dfe702..ba7a0191 100644 --- a/modules/docker-registry/fix-broken-blobs.sh +++ b/modules/docker-registry/fix-broken-blobs.sh @@ -40,6 +40,18 @@ INDEX_MEDIA_TYPES = ( "application/vnd.docker.distribution.manifest.list.v2+json", ) +# Only the private R/W registry is authoritative for every child of every +# index it stores — we pushed those indexes ourselves, so a missing child is +# always a bug (the 2026-04-13 + 2026-04-19 failure mode). +# +# Pull-through caches (dockerhub, ghcr, quay, k8s, kyverno) are ALLOWED to +# have missing children: they only fetch what someone actually pulls. +# Uncached arm64 / arm / attestation variants of a multi-platform index are +# normal partial state, not orphans. Scanning them generates hundreds of +# false-positive warnings — noise that would mask the real signal from the +# private registry. Scan 2 is therefore private-only. +INDEX_SCAN_REGISTRIES = ("private",) + total_layer_removed = 0 total_layer_checked = 0 total_index_scanned = 0 @@ -90,8 +102,8 @@ for registry_name in sorted(os.listdir(BASE)): shutil.rmtree(os.path.join(root, digest_dir)) total_layer_removed += 1 - # --- Scan 2: orphan OCI-index children -------------------------------- - elif root.endswith("/_manifests/revisions/sha256"): + # --- Scan 2: orphan OCI-index children (private registry only) -------- + elif root.endswith("/_manifests/revisions/sha256") and registry_name in INDEX_SCAN_REGISTRIES: repo = root.replace(repos_dir + "/", "").replace("/_manifests/revisions/sha256", "") for digest_dir in os.listdir(root):