zot version
main @ b689b06 (also present in v2.1.20; the rule is unchanged since #1359, May 2023)
Describe the bug
An ordinary image whose tag is its own digest — repo:sha256-<64 hex> — is
silently excluded from metadb, and therefore from the search extension and the
UI, while remaining fully served over the distribution API.
This is the shape mirroring tools write when the source reference was
digest-pinned: oc-mirror renders repo@sha256:x as repo:sha256-x. On a
disconnected mirror seeded that way, whole repositories can be invisible in the
UI despite being present and pullable.
The cause is IsReferrersTag:
func IsReferrersTag(tag string) bool {
referrersTagRule := regexp.MustCompile(`sha256\-[A-Za-z0-9]*$`)
return referrersTagRule.MatchString(tag)
}
Two separate problems:
-
The rule is unanchored and over-permissive. It matches any tag merely
ending in something digest-shaped, and [A-Za-z0-9]* accepts suffixes no
digest can produce. notsha256-<hex>, v1-sha256-<hex> and sha256-zzz are
all treated as referrers tags today.
-
Anchoring alone does not fix the reported case. sha256-<64 hex> is a
genuine referrers shape, so a strict ^sha256-[a-f0-9]{64}$ still excludes
it. Shape cannot distinguish a referrers fallback tag from an image tagged by
digest, because both have exactly the same shape.
The two are distinguishable, just not by shape. Per the referrers tag
schema, the fallback tag encodes the subject's digest, so the index
it tags is by construction a different manifest from the one named. A tag that
names its own target therefore cannot be a referrers entry — it is an ordinary
image tagged by digest.
Call sites affected: pkg/meta/hooks.go (OnUpdateManifest,
OnDeleteManifest, OnGetManifest) and pkg/meta/parse.go (parseRepo,
both loops). pkg/extensions/sync also calls it, in
service.go and destination.go.
To reproduce
-
Configuration — any storage backend, with the search extension on:
{ "extensions": { "search": { "enable": true } } }
-
Client tool used — anything that can push a manifest under a chosen tag
(regctl, skopeo, oras, or oc-mirror against a digest-pinned source).
Push a manifest to repo, note its digest sha256:X, then tag that same
manifest sha256-X — i.e. the tag names the digest of the manifest it
resolves to. This is what oc-mirror produces from repo@sha256:X.
-
Seen error — no error is logged. The tag is served normally:
GET /v2/repo/tags/list → returns sha256-X
GET /v2/repo/manifests/sha256-X → 200, the manifest
but it is absent from metadb:
ImageList(repo) → no result
GlobalSearch → does not return the repository
- the UI does not list the repository at all when it has no other tag
The same happens on a restart against existing storage, because parseRepo
applies the same filter while re-indexing.
Expected behavior
An image tagged with its own digest is an ordinary image and should be indexed
in metadb like any other tag, appearing in ImageList, GlobalSearch and the
UI.
Genuine referrers fallback tags should stay excluded — the filter should
discriminate, not be disabled.
Screenshots
Not attached — the visible symptom is simply the absence of the repository from
the UI's repository list. Happy to add before/after captures if useful.
Additional context
Proposed fix — for review before I open a PR.
I have a working change and would like a read on whether the approach is the one
you want before raising it:
main...zaldre:zot:fix/referrers-tag-digest-named
(one commit, +79/−11 across 5 files; branch is based on b689b06 and would be
rebased onto current main before a PR)
It does three things:
- Anchors
IsReferrersTag to the exact schema, ^sha256-[a-fA-F0-9]{64}$.
- Adds
IsDigestNamedTag(tag, digest) — does this tag name its own target?
- Has
pkg/meta skip a reference only when it is referrers-shaped and not
self-naming, through one shared isReferrersEntry helper used by all four
call sites.
Both metadb entry points already have the digest in hand (digest in the hooks,
manifest.Digest in parseRepo), so no new plumbing is required.
Tests: new table tests in pkg/common/common_test.go cover the tag shapes above
and the self-naming discriminator. One existing fixture changed —
TestUpdateErrors asserted the early return using the tag sha256-123, which is
three hex characters and is not a referrers tag under the corrected rule; it now
uses a full-length referrers tag, which is what the test appears to have
intended. make check reports 0 issues across all build-tag combinations, and
pkg/common, pkg/meta, pkg/extensions/search,
pkg/extensions/search/convert, pkg/storage/... and pkg/extensions/sync
pass.
Verified manually with two manifests in one repository — one tagged
sha256-<its own digest>, one tagged sha256-<a different digest>:
|
tags/list |
ImageList |
GlobalSearch |
| before — self-named |
yes |
— |
— |
| before — referrers |
yes |
— |
— |
| after — self-named |
yes |
indexed |
repo returned |
| after — referrers |
yes |
— |
— |
Also verified that starting a patched binary against an existing storage
directory re-indexes previously hidden images with no re-push, so an upgrade
picks up content already on disk.
Open questions for maintainers
-
pkg/extensions/sync call sites. I left them calling IsReferrersTag
unchanged. Narrowing that rule does change sync behaviour for the malformed
shapes — notsha256-<hex>, v1-sha256-<hex> and sha256-zzz were skipped as
referrers tags and would now be replicated as ordinary tags. The
sha256-<64 hex> case is classified the same as before, so sync is unaffected
there. The consequence is that zot-to-zot sync still declines to replicate a
digest-named tag that metadb would now index. Would you prefer the sync sites
moved onto the self-naming discriminator in the same PR?
-
OnGetManifest has no digest in hand, only the body, so the check there
needs godigest.FromBytes(body). Happy to arrange it so the hash is only
computed once the tag is known to be referrers-shaped, if you would rather not
add work to the manifest GET path.
-
Algorithm coverage. The current rule is sha256-only, and I kept it that
way; the spec's fallback schema is <alg>-<encoded> for any algorithm. Worth
widening separately, or leave as is?
If the approach looks right I will open the PR with a Fixes #<this issue>.
Happy to take it in a different direction if you would rather solve it elsewhere
— e.g. by inspecting the tagged manifest for referrers structure rather than
comparing digests.
zot version
main@ b689b06 (also present in v2.1.20; the rule is unchanged since #1359, May 2023)Describe the bug
An ordinary image whose tag is its own digest —
repo:sha256-<64 hex>— issilently excluded from metadb, and therefore from the search extension and the
UI, while remaining fully served over the distribution API.
This is the shape mirroring tools write when the source reference was
digest-pinned:
oc-mirrorrendersrepo@sha256:xasrepo:sha256-x. On adisconnected mirror seeded that way, whole repositories can be invisible in the
UI despite being present and pullable.
The cause is
IsReferrersTag:Two separate problems:
The rule is unanchored and over-permissive. It matches any tag merely
ending in something digest-shaped, and
[A-Za-z0-9]*accepts suffixes nodigest can produce.
notsha256-<hex>,v1-sha256-<hex>andsha256-zzzareall treated as referrers tags today.
Anchoring alone does not fix the reported case.
sha256-<64 hex>is agenuine referrers shape, so a strict
^sha256-[a-f0-9]{64}$still excludesit. Shape cannot distinguish a referrers fallback tag from an image tagged by
digest, because both have exactly the same shape.
The two are distinguishable, just not by shape. Per the referrers tag
schema, the fallback tag encodes the subject's digest, so the index
it tags is by construction a different manifest from the one named. A tag that
names its own target therefore cannot be a referrers entry — it is an ordinary
image tagged by digest.
Call sites affected:
pkg/meta/hooks.go(OnUpdateManifest,OnDeleteManifest,OnGetManifest) andpkg/meta/parse.go(parseRepo,both loops).
pkg/extensions/syncalso calls it, inservice.goanddestination.go.To reproduce
Configuration — any storage backend, with the search extension on:
{ "extensions": { "search": { "enable": true } } }Client tool used — anything that can push a manifest under a chosen tag
(
regctl,skopeo,oras, oroc-mirroragainst a digest-pinned source).Push a manifest to
repo, note its digestsha256:X, then tag that samemanifest
sha256-X— i.e. the tag names the digest of the manifest itresolves to. This is what
oc-mirrorproduces fromrepo@sha256:X.Seen error — no error is logged. The tag is served normally:
GET /v2/repo/tags/list→ returnssha256-XGET /v2/repo/manifests/sha256-X→ 200, the manifestbut it is absent from metadb:
ImageList(repo)→ no resultGlobalSearch→ does not return the repositoryThe same happens on a restart against existing storage, because
parseRepoapplies the same filter while re-indexing.
Expected behavior
An image tagged with its own digest is an ordinary image and should be indexed
in metadb like any other tag, appearing in
ImageList,GlobalSearchand theUI.
Genuine referrers fallback tags should stay excluded — the filter should
discriminate, not be disabled.
Screenshots
Not attached — the visible symptom is simply the absence of the repository from
the UI's repository list. Happy to add before/after captures if useful.
Additional context
Proposed fix — for review before I open a PR.
I have a working change and would like a read on whether the approach is the one
you want before raising it:
main...zaldre:zot:fix/referrers-tag-digest-named
(one commit, +79/−11 across 5 files; branch is based on b689b06 and would be
rebased onto current
mainbefore a PR)It does three things:
IsReferrersTagto the exact schema,^sha256-[a-fA-F0-9]{64}$.IsDigestNamedTag(tag, digest)— does this tag name its own target?pkg/metaskip a reference only when it is referrers-shaped and notself-naming, through one shared
isReferrersEntryhelper used by all fourcall sites.
Both metadb entry points already have the digest in hand (
digestin the hooks,manifest.DigestinparseRepo), so no new plumbing is required.Tests: new table tests in
pkg/common/common_test.gocover the tag shapes aboveand the self-naming discriminator. One existing fixture changed —
TestUpdateErrorsasserted the early return using the tagsha256-123, which isthree hex characters and is not a referrers tag under the corrected rule; it now
uses a full-length referrers tag, which is what the test appears to have
intended.
make checkreports 0 issues across all build-tag combinations, andpkg/common,pkg/meta,pkg/extensions/search,pkg/extensions/search/convert,pkg/storage/...andpkg/extensions/syncpass.
Verified manually with two manifests in one repository — one tagged
sha256-<its own digest>, one taggedsha256-<a different digest>:tags/listImageListGlobalSearchAlso verified that starting a patched binary against an existing storage
directory re-indexes previously hidden images with no re-push, so an upgrade
picks up content already on disk.
Open questions for maintainers
pkg/extensions/synccall sites. I left them callingIsReferrersTagunchanged. Narrowing that rule does change sync behaviour for the malformed
shapes —
notsha256-<hex>,v1-sha256-<hex>andsha256-zzzwere skipped asreferrers tags and would now be replicated as ordinary tags. The
sha256-<64 hex>case is classified the same as before, so sync is unaffectedthere. The consequence is that zot-to-zot sync still declines to replicate a
digest-named tag that metadb would now index. Would you prefer the sync sites
moved onto the self-naming discriminator in the same PR?
OnGetManifesthas no digest in hand, only the body, so the check thereneeds
godigest.FromBytes(body). Happy to arrange it so the hash is onlycomputed once the tag is known to be referrers-shaped, if you would rather not
add work to the manifest GET path.
Algorithm coverage. The current rule is sha256-only, and I kept it that
way; the spec's fallback schema is
<alg>-<encoded>for any algorithm. Worthwidening separately, or leave as is?
If the approach looks right I will open the PR with a
Fixes #<this issue>.Happy to take it in a different direction if you would rather solve it elsewhere
— e.g. by inspecting the tagged manifest for referrers structure rather than
comparing digests.