live: heartbeat sync 2026-10-21T02:32:51-07:00 (Refs #11)

This commit is contained in:
Frank committed 2026-10-21 02:32:51 -07:00
1 parent 22d2f3bcd8
commit 336201e814
22 files changed
+451 -322

No files matched your search

@@ -1,49 +1,44 @@
---
name: docker-pin-latest-census
description: "Prove a Docker Hub / lscr.io image pin is the latest version before bumping it: registry-API tag census plus digest-equals-latest verification, no docker CLI needed."
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."
---
# Is my pinned image actually the latest?
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.
Use when bumping a pinned container image tag (e.g. `lscr.io/linuxserver/*` quadlet `Image=` lines) and "latest" must be PROVEN, not assumed. Works from any box with curl — no podman/docker daemon required. `lscr.io` fronts Docker Hub; its tags are NOT on quay.io (querying quay returns nothing and reads like "no newer tags exist" — false).
## 1. Full tag census via the registry API
## The API
```bash
tok=$(curl -s "https://auth.docker.io/token?service=registry.docker.io&scope=repository:<owner>/<repo>:pull" \
| python3 -c "import json,sys;print(json.load(sys.stdin)['token'])")
curl -s "https://registry-1.docker.io/v2/<owner>/<repo>/tags/list?n=2000" \
-H "Authorization: Bearer $tok" | python3 - <<'PY'
import json,sys,re
tags=json.load(sys.stdin).get('tags') or []
# linuxserver form: <version>-<sha>-ls<N>; group by version, keep newest ls per version
v={}
for t in tags:
m=re.match(r'^(\d+(?:\.\d+)+)-([0-9a-f]+)-ls(\d+)$',t)
if m: v.setdefault(tuple(map(int,m.group(1).split('.'))),[]).append((int(m.group(3)),t))
for k in sorted(v)[-5:]:
print('.'.join(map(str,k)),'-> newest:',max(v[k])[1]) # max on (ls#, tag) tuple
PY
# 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
```
- Sort versions as **tuples of ints**, never strings (`1.4.10 < 1.4.9` lexicographically).
- Keep the max `ls` build per version, then the newest version — both can lag independently.
- Trap already eaten: joining the tuple key to a string mid-loop then re-looking-it-up → `KeyError`. Keep keys as tuples; format only when printing.
- Some channels have NO `latest` tag in `/tags/list` (or the endpoint truncates) — absence there is not evidence.
## 2. Digest-proof the candidate against `latest`
```bash
for t in latest <candidate-tag>; do
curl -sI "https://registry-1.docker.io/v2/<owner>/<repo>/manifests/$t" \
-H "Authorization: Bearer $tok" \
-H "Accept: application/vnd.oci.image.index.v1+json,application/vnd.docker.distribution.manifest.list.v2+json,application/vnd.docker.distribution.manifest.v2+json" \
| grep -i '^docker-content-digest'
done
# 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'
```
Equal digests ⇒ the pinned tag IS what `latest` serves right now — the strongest "ensure latest" evidence, and recordable in the endpoint page's pin note (the dank-fleet law: *pinned; verified == the registry's `latest` manifest digest at pin time*). A multi-arch index digest matches only if both requests send the same Accept set — send all three types above.
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.
## 3. Bump through git, not by hand
## The `latest` trap
Edit the quadlet/unit `Image=` line + the vault page's pin row (one owner each), run the repo suite, PR, deploy `--service <svc>` only. Rollback stays "revert one line".
- `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.