# Part 0 — why the tab bar rendered unstyled through the public address **Measured 2026-10-15 ~19:55 CEST from DooPlex (Hungary, so the geo rule passes). Nothing was changed.** ## Verdict: (a) a cache — and the mechanism is named Not (b): **exactly one box runs 0.315.0 and it runs the registry's current image.** | Box | pinned tag | status | |---|---|---| | demo-hp guest 9201 | `felhom-controller:0.315.0` | Up, healthy | | demo-hp guest 9202 | `felhom-controller:0.314.0` | Up, healthy | | felhom-pve (N100) guest 9201 | `felhom-controller:0.314.0` | Up, healthy | | c11-appliance (Tester 1) | unreachable from DooPlex right now | — | - demo-hp 9201's running image: `gitea.dooplex.hu/admin/felhom-controller@sha256:8887343011d64a2da9c65674a777a099708315063e3abc7c9463c03151dff041` - The registry's current `0.315.0`: `sha256:8887343011d64a2da9c65674a777a099708315063e3abc7c9463c03151dff041` — **identical**. - The **global floor is `0.314.0`** (read from the hub's `/configuration`), so no box could have taken 0.315.0 by itself; the one that has it was deployed by hand. Tester 1 therefore cannot be on a stale 0.315.0 even though it is off right now. ## The four facts that make a cache the cause 1. **The stylesheet URL does not change between builds.** `layout.html` L12 asks for `/static/style.css?v={{.Version}}` → `?v=0.315.0` for all **three different** 0.315.0 builds (`e66d466`, `fe0228c`, `db52f5a`). The first of those had no `.app-tabs` rules at all. 2. **The origin offers no validator.** `serveCSSHandler` (`server.go:1130`) sets only `Cache-Control: public, max-age=3600`. Measured on the box: **ETag present = 0, Last-Modified present = 0.** With neither, no client and no edge can revalidate a copy — it can only expire. 3. **Cloudflare caches this URL and extends the browser's TTL.** Three requests in a row: `cf-cache-status: HIT`, `age: 99 → 100`, and `cache-control: public, max-age=14400` — Cloudflare rewrites the origin's 1 hour to **4 hours for the browser**. 4. **Cloudflare held a stale entry for that exact URL.** The very first request of the session returned `cf-cache-status: EXPIRED` — which only happens when the edge *has* an entry that has aged past its TTL. Having no validator, it had to refetch the whole body. ## What is true right now, and the honest caveat Origin, edge and box agree byte-for-byte (`sha256 c1774f63daf65c56` both sides) and all contain the `.app-tabs` rules. The page itself is never cached (`cf-cache-status: DYNAMIC`); only the stylesheet is. **The caveat, stated rather than glossed:** the specific stale object is no longer observable — the first probe of this session forced its revalidation, which is exactly what `EXPIRED` records. So I cannot produce a `HIT` whose body lacks `.app-tabs`. What is shown is every precondition for it, the proof that an aged entry existed, and the elimination of the only other candidate (b). ## Why this authorises Part 1.2 The fix is not "purge the cache" — that hides the cause and returns at the next build. The fix is that **the stylesheet's address changes with every build**, so no cache at any layer (browser, Cloudflare, or anything between) can serve a previous build's CSS under an address the new build still asks for. `Cache-Control` stays as it is: the new URL is the fix, not a shorter cache. Part 1.1 removes the precondition entirely — a version number can only be pushed once, so two different images can never share a number again.