Skip to content

fix: stop edge-caching apt metadata (no-store) + purge on publish — #3218 follow-up#6

Merged
bpamiri merged 1 commit into
mainfrom
peter/cdn-cache-no-store-metadata
Jun 19, 2026
Merged

fix: stop edge-caching apt metadata (no-store) + purge on publish — #3218 follow-up#6
bpamiri merged 1 commit into
mainfrom
peter/cdn-cache-no-store-metadata

Conversation

@bpamiri

@bpamiri bpamiri commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

The other half of #3218

apt-wheels#5 fixed the cross-channel clobber (origin was writing an empty Packages). But users still can't apt install wheels, because of a second, independent cause: CDN caching.

Cloudflare auto-caches by file extension. A published Packages.gz gets edge-cached and keeps being served after the next publish rewrites it — its hash no longer matches the freshly-regenerated Release, so apt-get update rejects it:

Err: https://apt.wheels.dev stable/main amd64 Packages
  File has unexpected size (29 != 1404). Mirror sync in progress?
E: Failed to fetch .../Packages.gz  File has unexpected size (29 != 1404)

The uncompressed Packages/Release/InRelease are extension-less → Cloudflare serves them DYNAMIC (never cached), which is why only the .gz broke, and why a ?cb= cache-buster masked it (different cache key → origin). Verified live: plain Packages.gz = cf-cache-status: HIT, 29 bytes (empty, from a prior clobber); ?cb= = MISS, 1404 bytes (correct).

Fix

  • Metadata (Release, Release.gpg, InRelease, Packages, Packages.gz) uploaded with Cache-Control: no-store — the edge never caches a stale index again. This prevents recurrence on its own, regardless of purge permissions.
  • .deb pool files uploaded with public, max-age=31536000, immutable — version-stamped and immutable, so long edge caching is correct and fast.
  • Best-effort purge step evicts the just-published URLs to unstick copies cached before no-store existed (the current legacy stale entry). Resolves the wheels.dev zone at runtime; warns and continues (never fails the publish) if the token lacks Zone.Cache-Purge scope.

After merge

Dispatch channel=stable, version=4.0.5 to re-upload with the new headers + purge, then apt install wheels works on a clean box (will verify). The purge step's log says whether the token has zone scope; if not, no-store + the legacy TTL still resolve it, and a one-time dashboard purge is the fallback.

Refs: wheels-dev/wheels#3218, #5

…218 follow-up)

The cross-channel clobber (apt-wheels#5) was only half the #3218 story. The
other half is CDN caching: Cloudflare auto-caches by file extension, so a
published Packages.gz got edge-cached and kept being served after the next
publish rewrote it. Its hash no longer matched the freshly-regenerated Release,
so `apt-get update` rejected it with "File has unexpected size (29 != 1404)" —
exactly the symptom users keep reporting. The uncompressed Packages/Release are
extension-less (served DYNAMIC, never cached), which is why only the .gz broke
and why a cache-buster query masked it (different cache key -> origin).

Fix:
- Upload apt metadata (Release, Release.gpg, InRelease, Packages, Packages.gz)
  with `Cache-Control: no-store` so the edge never caches a stale index again.
  This alone prevents recurrence regardless of purge permissions.
- Upload .deb pool files with `public, max-age=31536000, immutable` — they're
  version-stamped and never change, so long edge caching is correct and fast.
- Add a best-effort Cloudflare purge step that evicts the just-published URLs,
  to unstick copies cached BEFORE no-store existed (the current legacy entry).
  Resolves the wheels.dev zone at runtime; warns and continues (does not fail
  the publish) if the token lacks Zone.Cache-Purge scope.

Refs: wheels-dev/wheels#3218, #5

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Peter Amiri <peter@alurium.com>
bpamiri pushed a commit to wheels-dev/wheels that referenced this pull request Jun 19, 2026
…ctly

Two corrections to the index-integrity job after finding the real #3218
failure mode is CDN edge-cache staleness, not just the origin clobber:

1. Drop the `?cb=` cache-buster. apt/dnf fetch the plain URLs that hit
   Cloudflare's edge; a cache-buster hit R2 origin instead, so the probe went
   green while real `apt install` failed on a stale edge-cached Packages.gz.
   Test the SAME plain URLs clients use.

2. Verify hash-consistency, exactly what the package managers verify: parse the
   SHA256 the (plain) Release records for each binary-<arch>/Packages.gz, fetch
   the (plain) Packages.gz, and compare — this is precisely the "File has
   unexpected size" check that failed in #3218. yum: verify the served
   primary.xml.gz matches the hash repomd references. Then gunzip and confirm
   the content lists the GA (catches an empty-but-internally-consistent index).

Binary correctness: gzip blobs are fetched to a temp file and hashed/gunzipped
from the file — never captured into a shell variable, since `$(...)` is
text-only and silently corrupts binary (strips trailing newlines, drops NULs),
which would compute a bogus hash.

Paired with apt-wheels#6 / yum-wheels#6 (set `no-store` on metadata so the edge
stops caching it). Verified against the live indexes: correctly RED on the
currently-stale apt stable amd64 edge, GREEN on arm64 / bleeding-edge / yum.

Refs: #3218, wheels-dev/apt-wheels#6, wheels-dev/yum-wheels#6

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Peter Amiri <petera@pai.com>
@bpamiri bpamiri merged commit 145bec6 into main Jun 19, 2026
1 check passed
@bpamiri bpamiri deleted the peter/cdn-cache-no-store-metadata branch June 19, 2026 20:47
bpamiri added a commit to wheels-dev/yum-wheels that referenced this pull request Jun 19, 2026
…218 parallel) (#6)

Mirror of apt-wheels#6 for the rpm side. Cloudflare auto-caches by extension,
so a published repomd.xml / *-primary.xml.gz can be edge-cached and served stale
after the next publish rewrites it, leaving dnf with a repomd that references
checksums the served metadata no longer matches. repodata changes every publish
-> upload it with `Cache-Control: no-store` so the edge never serves a stale
index. .rpm packages are version-stamped/immutable -> `public, max-age=1y,
immutable`. Plus a best-effort Cloudflare purge step (resolves the wheels.dev
zone at runtime; warns + continues if the token lacks Zone.Cache-Purge scope)
to evict copies cached before no-store existed.

yum-wheels was not the repo that triggered #3218 (its upload was already
channel-scoped, so it never had the apt clobber), but it shared the same
extension-based edge-cache exposure on metadata — hardening it now so the rpm
channel can't develop the same stale-metadata failure.

Refs: wheels-dev/wheels#3218, wheels-dev/apt-wheels#6

Signed-off-by: Peter Amiri <peter@alurium.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
bpamiri added a commit to wheels-dev/wheels that referenced this pull request Jun 19, 2026
…gression guard) (#3234)

* ci: add index-integrity probe to distribution install smoke (#3218 regression guard)

The daily install-smoke installs the CLI per channel and asserts the version,
but that only samples once at 14:00 UTC. The #3218 cross-channel clobber empties
the stable apt index for most of the day (a bleeding-edge snapshot wipes it
minutes after each stable publish; it's only briefly populated), so a clobber
can land outside the install legs' sample window and slip past them — and when
they do fail, "apt install failed" doesn't name the cause.

Add a fast, container-free `index-integrity` job that probes the PUBLISHED apt
and yum dist indexes directly: stable must be non-empty and name the current GA
(per arch for apt), and bleeding-edge must stay non-empty (the clobber was
bidirectional — apt-wheels#5 scopes regen per-channel to prevent both
directions). An empty/missing/stale index fails with a message that names the
regression.

Reliability: fetches use `curl --retry ... --retry-all-errors` so a transient
blip can't false-red the guardian, and all grep checks feed from a here-string
(`grep -q PAT <<<"$body"`) rather than `printf | grep -q` — under `set -o
pipefail` the latter false-fails when grep -q early-exits and SIGPIPEs the
upstream printf of a 100KB index. Verified: 3/3 green against the live indexes,
and a negative test (bogus version) correctly fails.

Refs: #3218, wheels-dev/apt-wheels#5

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Peter Amiri <petera@pai.com>

* ci: probe plain (edge) URLs + hash-consistency, mirroring apt/dnf exactly

Two corrections to the index-integrity job after finding the real #3218
failure mode is CDN edge-cache staleness, not just the origin clobber:

1. Drop the `?cb=` cache-buster. apt/dnf fetch the plain URLs that hit
   Cloudflare's edge; a cache-buster hit R2 origin instead, so the probe went
   green while real `apt install` failed on a stale edge-cached Packages.gz.
   Test the SAME plain URLs clients use.

2. Verify hash-consistency, exactly what the package managers verify: parse the
   SHA256 the (plain) Release records for each binary-<arch>/Packages.gz, fetch
   the (plain) Packages.gz, and compare — this is precisely the "File has
   unexpected size" check that failed in #3218. yum: verify the served
   primary.xml.gz matches the hash repomd references. Then gunzip and confirm
   the content lists the GA (catches an empty-but-internally-consistent index).

Binary correctness: gzip blobs are fetched to a temp file and hashed/gunzipped
from the file — never captured into a shell variable, since `$(...)` is
text-only and silently corrupts binary (strips trailing newlines, drops NULs),
which would compute a bogus hash.

Paired with apt-wheels#6 / yum-wheels#6 (set `no-store` on metadata so the edge
stops caching it). Verified against the live indexes: correctly RED on the
currently-stale apt stable amd64 edge, GREEN on arm64 / bleeding-edge / yum.

Refs: #3218, wheels-dev/apt-wheels#6, wheels-dev/yum-wheels#6

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Peter Amiri <petera@pai.com>

---------

Signed-off-by: Peter Amiri <petera@pai.com>
Co-authored-by: Peter Amiri <petera@pai.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant