You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
OCI cache is keyed only by tag — collisions, orphaned blobs, no pruning
Relates to #88.#88's "Open Design Questions → Cache pruning" already asks what
pruning UX to build (manual clean, TTL, size-cap/LRU). This issue is the concrete defect
that motivates it: the current tag-only cache key causes collisions and orphaned blobs,
so pruning is not just a nice-to-have — without it the cache is actively corrupted by
normal multi-source use. Scope here is the two gaps #88 does not spell out: the
collision-prone key, and the untracked blobs it leaves behind.
Where: the on-disk cache design as a whole
(internal/source/source.go:67,81, internal/registry/client.go:57-76ParseTag).
What happens: artifacts are stored in one shared OCI layout, referenced by the tag
that ParseTag extracts (v1, latest, a digest, …). Two different repositories that
share a tag map to the sameindex.json key:
ghcr.io/org/controls:latest -> index key "latest"
ghcr.io/org/policies:latest -> index key "latest" # overwrites the previous entry
Each load re-tags the key to its own manifest, so the two sources can never be cached at
the same time — every run overwrites and re-pulls the other. The overwritten manifest's
blobs stay on disk but become untracked, and there is no garbage collection, so the
cache grows unboundedly until cache clean wipes everything.
Important — this is NOT a wrong-result bug. Within a single run each source opens its
own store and oras.Copy re-resolves the root from the correct remote right before bundle.Unpack, so the correct manifest is always unpacked. The damage is limited to
cache efficiency: thrashing on tag collisions, and unbounded growth from dangling blobs.
Suggestions:
Key cache entries by full repository + reference, not just the bare tag. This removes
the collision at the source and is a prerequisite for any correct GC — a prune that
keys on the bare tag cannot tell two colliding sources apart.
Add a prune/GC path that removes manifests/blobs no longer referenced by any
configured source (or a TTL/LRU policy) — this is the concrete design input for Persistent OCI cache with XDG_CACHE_HOME support #88's
open pruning question. Today the only reclamation is a full cache clean wipe.
OCI cache is keyed only by tag — collisions, orphaned blobs, no pruning
Relates to #88. #88's "Open Design Questions → Cache pruning" already asks what
pruning UX to build (manual clean, TTL, size-cap/LRU). This issue is the concrete defect
that motivates it: the current tag-only cache key causes collisions and orphaned blobs,
so pruning is not just a nice-to-have — without it the cache is actively corrupted by
normal multi-source use. Scope here is the two gaps #88 does not spell out: the
collision-prone key, and the untracked blobs it leaves behind.
Where: the on-disk cache design as a whole
(
internal/source/source.go:67,81,internal/registry/client.go:57-76ParseTag).What happens: artifacts are stored in one shared OCI layout, referenced by the tag
that
ParseTagextracts (v1,latest, a digest, …). Two different repositories thatshare a tag map to the same
index.jsonkey:Each load re-tags the key to its own manifest, so the two sources can never be cached at
the same time — every run overwrites and re-pulls the other. The overwritten manifest's
blobs stay on disk but become untracked, and there is no garbage collection, so the
cache grows unboundedly until
cache cleanwipes everything.Important — this is NOT a wrong-result bug. Within a single run each source opens its
own store and
oras.Copyre-resolves the root from the correct remote right beforebundle.Unpack, so the correct manifest is always unpacked. The damage is limited tocache efficiency: thrashing on tag collisions, and unbounded growth from dangling blobs.
Suggestions:
the collision at the source and is a prerequisite for any correct GC — a prune that
keys on the bare tag cannot tell two colliding sources apart.
prune/GC path that removes manifests/blobs no longer referenced by anyconfigured source (or a TTL/LRU policy) — this is the concrete design input for Persistent OCI cache with XDG_CACHE_HOME support #88's
open pruning question. Today the only reclamation is a full
cache cleanwipe.Drafted with LLM assistance (Claude Opus 4.6).