Files
command-center/agent/managed-skills/docker-pin-latest-census/SKILL.md
T

45 lines
2.7 KiB
Markdown

---
name: docker-pin-latest-census
description: "Prove a Docker Hub / lscr.io image pin is the latest version before bumping it: registry-API tag enumeration, the rolling-latest trap, digest pinning, and rollback proof."
---
Use when asked whether a Docker Hub / lshr.io image pin is the latest version before bumping it, or when a manifest claims an image is stale. Registry API is the answer; the `latest` tag is not.
## The API
```bash
# hub: token then paginate (the list is 100 per page)
tok=$(curl -s "https://auth.docker.io/token?service=registry.docker.io&scope=repository:${repo}:pull" | jq -r .token)
for off in 0 100 200 300 400 500; do
curl -s -H "Authorization: Bearer $tok" \
"https://hub.docker.com/v2/repositories/${repo}/tags?page_size=100&ordering=last_updated&page=${page}" \
| jq -r '.results[] | "\(.name)\t\(.last_updated)"'
done | sort
```
```bash
# lscr.io (linuxserver): tags are semver, `latest` is a rolling alias
curl -s https://images-api.github.com/v2/linuxserver/${repo}/tags/list # wrong host for lscr
curl -s "https://hub.docker.com/v2/repositories/linuxserver/${repo}/tags?page_size=100" | jq -r '.results[].name'
```
Docker Hub's own tag list (`hub.docker.com/v2/repositories/<repo>/tags`, ordered by `last_updated`) is the practical source. The registry API (`/v2/<repo>/tags/list`) needs the bearer token flow above; both return tags, neither sorts by semver.
## The `latest` trap
- `latest` is a **rolling alias**, not a version. "Pin is older than `latest`" says nothing about whether a *versioned* tag exists.
- `latest` can lag or be rebuilt without a version bump; comparing a digest pin against it proves nothing.
- For linuxserver images: `stable` is the rolling branch and version tags (`2026-09-30`) are what you pin; multi-arch manifest lists mean the same tag has per-arch digests — the pin must be the manifest-list digest (`RepoDigests[0]`) you actually pull, or a re-pull on another arch silently changes the image.
## Verification recipe (before bumping a pin)
1. Read the pinned tag and digest from the manifest/quadlet.
2. Enumerate tags from the registry API (paginate; `last_updated` is not semver order).
3. Compare **versions**, not recency: does a version greater than the pinned one exist?
4. If bumping: pull the new tag and record `docker/podman inspect --format '{{index .RepoDigests 0}}'` as the new digest — pin the digest, not the tag.
5. Prove the service: it starts, version endpoint answers the new version, no crash-loop, and the rollback is the previous digest.
## Report
"pinned: X (digest …), latest available: Y, upstream changelog: …" — or "pin is current: no version tag newer than X exists". Never claim staleness from the `latest` tag alone.