From c5b1044d7a7a675b10d56f0ddcf82bdf3f086a50 Mon Sep 17 00:00:00 2001 From: Jonathon Wright Date: Tue, 25 Aug 2026 09:18:11 +1000 Subject: [PATCH] projects: add The Mirror pull-through registry case study; wire cover art for both learn-site projects Adds mirror-registry as a featured project (order 26, beside The Exploded Cluster) covering the zot deployment, the bootstrap and auth circular dependencies, and the retention/revalidation behaviours that corrected the first published write-up. Adds the cover: field to exploded-cluster (it had none, so the card rendered imageless) and stages public/covers/image.md with the generation prompts and processing steps for both covers. --- public/covers/image.md | 98 ++++++++++++++++++++++ src/content/projects/exploded-cluster.md | 3 +- src/content/projects/mirror-registry.md | 102 +++++++++++++++++++++++ 3 files changed, 202 insertions(+), 1 deletion(-) create mode 100644 public/covers/image.md create mode 100644 src/content/projects/mirror-registry.md diff --git a/public/covers/image.md b/public/covers/image.md new file mode 100644 index 0000000..726aea1 --- /dev/null +++ b/public/covers/image.md @@ -0,0 +1,98 @@ +# Project cover prompts - bztmon.com + +Two covers to generate: **The Exploded Cluster** and **The Mirror (pull-through registry)**. +Paste the block below the `---` for each one into Gemini / Nano Banana 2, generate ~4 +variations, curate against the reject list, then process per "After generating". + +Aspect: **3:2 landscape** (existing covers are 1600 x 1074). + +--- + +## House style block (already inside each prompt - do not paste separately) + +The site's covers are dark isometric hero renders: matte dark hardware, cyan and magenta +edge light, a subtly circuit-etched ground plane, thin atmospheric haze, one small four-point +sparkle highlight, and generous empty space on one side so the card's title text has room. +Unlike the learn-site scene plates, covers MAY have a ground plane and atmosphere - they are +never sliced or animated. + +## Reject checklist (per candidate, before you keep it) + +- [ ] any text, letters, numbers, logos or UI chrome anywhere in the frame (reject hard) +- [ ] subject clipped by the frame edge, or centred so tightly there is no negative space +- [ ] colours drifting warm - the palette is cyan/magenta on near-black, no orange or gold +- [ ] photographic realism or stock-render gloss (it should read as an illustration) +- [ ] busy background competing with the subject +- [ ] the two covers looking like the same object (they must be distinguishable as a pair) + +## After generating + +1. Keep the best of each, name them `exploded-cluster.png` / `mirror-registry.png`. +2. Convert and place (from the repo root, on the box with the site checkout): + +``` +# full-size cover: 1600px wide +ffmpeg -y -i exploded-cluster.png -vf "scale=1600:-2" -q:v 80 public/covers/exploded-cluster.webp +ffmpeg -y -i mirror-registry.png -vf "scale=1600:-2" -q:v 80 public/covers/mirror-registry.webp + +# card thumbnail: 640px wide +ffmpeg -y -i exploded-cluster.png -vf "scale=640:-2" -q:v 80 public/covers/thumb/exploded-cluster.webp +ffmpeg -y -i mirror-registry.png -vf "scale=640:-2" -q:v 80 public/covers/thumb/mirror-registry.webp +``` + +3. Both project files already reference these names (`cover: "exploded-cluster"`, + `cover: "mirror-registry"`), so the cards pick them up on the next build. + +--- + +# PROMPT 1 - The Exploded Cluster + +Dark isometric technical illustration in a cinematic sci-fi engineering style, 3:2 landscape. +Near-black background, hex #070b14, with a faint dark circuit-etched ground plane beneath the +subject and a thin drift of atmospheric haze in the upper right. Matte dark metal and smoked +glass surfaces with glowing neon edge lighting: primary cyan #3fbaf5, secondary magenta +#e879f9, cool white rim highlights. Even studio lighting, no lens flare, no depth-of-field +blur. ABSOLUTELY NO text, letters, numbers, logos, labels, arrows or UI elements anywhere in +the image - surface markings must be abstract geometric patterns only. + +The subject: a single machine caught mid-explosion, its layers separated vertically like an +engineering teardown diagram, floating apart with clear gaps of void between them. From the +bottom up: a heavy dark chassis base with cyan-lit vents; above it a thick slab housing rows +of small identical cells; above that two thin translucent glass sheets etched with fine +circuit tracery, glowing faintly cyan; and at the top, held well clear of everything else, a +small bright magenta-lit cube - the smallest and most luminous part, clearly the payload the +whole machine exists to carry. + +The parts hover in stable formation rather than flying apart violently - a considered +teardown, paused for inspection. Composition sits left of centre, leaving open dark space on +the right third of the frame. One small four-point white sparkle highlight in the lower right, +far from the subject. + +--- + +# PROMPT 2 - The Mirror (pull-through registry) + +Dark isometric technical illustration in a cinematic sci-fi engineering style, 3:2 landscape. +Near-black background, hex #070b14, with a faint dark circuit-etched ground plane beneath the +subject and a thin drift of atmospheric haze in the upper left. Matte dark metal and smoked +glass surfaces with glowing neon edge lighting: primary cyan #3fbaf5, secondary magenta +#e879f9, cool white rim highlights. Even studio lighting, no lens flare, no depth-of-field +blur. ABSOLUTELY NO text, letters, numbers, logos, labels, arrows or UI elements anywhere in +the image - surface markings must be abstract geometric patterns only. + +The subject: a wide, low, heavily-armoured way-station machine sitting on the ground plane, +right of centre - a bunker-grade cache with a glass-fronted shelf running along its face, +holding a row of small identical luminous cyan bricks in cold storage. Its face has a single +broad output aperture ringed in cool white light, aimed left. + +Above and behind it, arranged in a loose arc receding into the haze, five small dark intake +pods at clearly diminishing scale - each a compact rounded capsule with one cyan-lit aperture, +each floating alone with generous void between them, none touching the main machine. They +read as five distant sources feeding one local shelf. + +Below and left of the way-station, close to the ground plane, a single luminous cyan brick +hovers in mid-transfer - small, bright, clearly in motion toward the viewer's side of the +frame, the one thing the whole arrangement is delivering. + +Composition sits right of centre, leaving open dark space on the left third for title text. +One small four-point white sparkle highlight in the upper right, far from the subject. diff --git a/src/content/projects/exploded-cluster.md b/src/content/projects/exploded-cluster.md index e2f6a56..60c7073 100644 --- a/src/content/projects/exploded-cluster.md +++ b/src/content/projects/exploded-cluster.md @@ -4,9 +4,10 @@ outcome: "A scroll-driven teaching site that takes container platforms apart one summary: "A single-page course on how Kubernetes and OpenShift delivery actually works - each subject drawn as one exploded illustration, sliced into its real components and wired to the scroll, then shipped through the same GitOps and CSP discipline as the rest of the estate." role: "Author / Engineer" period: "2026" -stack: ["Astro-free static build", "GSAP ScrollTrigger", "Strict CSP", "Cloudflare Tunnel", "Argo CD", "zot mirror"] +stack: ["Dependency-free static build", "GSAP ScrollTrigger", "Strict CSP", "Cloudflare Tunnel", "Argo CD", "zot mirror"] featured: true order: 25 +cover: "exploded-cluster" links: - label: "learn.bztmon.com" href: "https://learn.bztmon.com" diff --git a/src/content/projects/mirror-registry.md b/src/content/projects/mirror-registry.md new file mode 100644 index 0000000..309ae37 --- /dev/null +++ b/src/content/projects/mirror-registry.md @@ -0,0 +1,102 @@ +--- +title: "The Mirror: A Pull-Through Registry" +outcome: "Ended anonymous registry rate-limits across a six-node fleet, then published the failure modes the deployment revealed - including the one that contradicted my own first write-up." +summary: "A zot OCI registry fronting five upstreams for a Talos/containerd fleet: on-demand pull-through, digest preservation, a staged authentication rollout with a per-node proof gate, and a documented set of behaviours - garbage collection, cache revalidation, upstream routing - that only surfaced when the deployment was read against the source rather than the marketing." +role: "Author / Engineer" +period: "2026" +stack: ["zot v2.1.17", "Talos Linux", "containerd", "OCI distribution", "Argo CD", "External Secrets"] +featured: true +order: 26 +cover: "mirror-registry" +links: + - label: "learn.bztmon.com/mirror" + href: "https://learn.bztmon.com/mirror" +--- + +## Problem + +A CI build stalled with no visible error. The cause was an anonymous Docker Hub pull hitting +the rate limit and answering with HTTP 429 - which a retry wrapper swallowed, four outer +attempts around a builder doing up to four of its own, so one failing pull could generate up +to sixteen requests against the limit it was trying to outlast. Beneath that specific bug sat +the structural one: every node and every build asked the public internet for the same bytes, +repeatedly and forever. + +## Constraints + +- **The registry is also a dependency of the thing that runs it.** Its own image lives on a + registry it mirrors, and the node hosting it boots its workloads through it. +- **Digest-pinned estate.** Every deployment references images by digest, so anything that + rewrites a manifest in transit breaks the pins and any signature attached to them. +- **Authentication cannot flip in one step.** Node registry credentials only apply after a + reboot, and a runtime whose auth is wrong does not degrade gracefully - it stops pulling. + +## Design + +A single zot instance fronts five upstream registries in on-demand pull-through mode: a miss +fetches and caches, a hit serves locally. Digest preservation is enabled per upstream, paired +with the media-type compatibility flag the registry refuses to start without - conversion to +OCI would otherwise change every mirrored manifest digest. + +Consumers point at it through Talos machine config. The mirror is the only listed endpoint, +but it is not a hard dependency, because the platform falls back to the origin registry +implicitly unless that behaviour is disabled. That default is what breaks the bootstrap +circle: when the hosting node boots and the mirror is not yet running, the pull falls through +and the registry's own image arrives from upstream. The fleet's recovery tooling deliberately +does not use the mirror at all - its image is cached outside the cluster, so it stays +available when the platform it repairs is not. + +Authentication is a staged rollout rather than a flip. Anonymous read stays on while each node +receives credentials and reboots, and a node only counts as migrated once it passes two +separate checks: an authenticated pull of a canary repository that denies anonymous access +(so success cannot come from being served as an anonymous reader), and a separate correlation +between an original-upstream reference pulled on a cold node and the mirror's own request log +(because the first check proves identity, not routing). + +## What reading the source changed + +The first published version of this write-up was wrong in a way worth keeping on the record. +I had written that with no retention policy configured, the cache keeps everything. That holds +for tagged images. It does not hold for untagged manifests, which garbage collection removes +once they age past the delay - and digest-only pull-through entries are stored untagged. A +digest-pinned fleet pulling through this mirror therefore caches exactly the class of object +its garbage collector is entitled to delete. Selective, pull-aware retention for untagged +content exists upstream, but only from a release later than the one deployed here; until an +upgrade, the levers are a wider delay window or a blanket policy that trades eviction for +unbounded growth. + +Two related behaviours came from the same exercise. Pulls by tag contact the upstream registry +even when the content is already cached, so a warm cache is not equivalent to independence +from the origin. And five upstreams currently share one flat namespace, which works because +their repository paths happen to be largely disjoint - a probability rather than a design, with +per-origin prefixes as the fix. + +## Security and reliability decisions + +- **Fallback is a recorded policy choice.** Keeping it enabled buys bootstrap survivability + and availability during mirror outages, at the cost of pulls bypassing the mirror unnoticed + and reaching origin limits. A regulated or disconnected estate makes the opposite call + deliberately, along with preseeded content and a tested recovery path. +- **Credentials are separated by consumer.** Node pull credentials, the build host's push + credential, and the registry's own upstream credentials never share material, and the + automation identity that synchronises secrets into the cluster is read-only so it cannot + half-rotate a credential pair - the failure mode behind a twelve-day outage on this estate + when a stored hash and its plaintext drifted apart. +- **Health checks that test the path.** A process returning 200 while every pull hangs is a + state this deployment has actually been in. The checks that matter authenticate, request a + real manifest, and confirm the request arrived through the mirror. + +## Outcome + +Fleet-wide pulls resolve locally instead of hitting public rate limits, and the estate's +disconnected posture is now honestly bounded: the mirror is a rate-limit shield and latency +win today, not yet a shelf that survives a severed link. The full teardown - configuration, +diagrams, failure-mode matrix, and the tests still outstanding - is published as a public +case study. + +## Future improvements + +Upgrade to a release with selective untagged retention and write the policy before the +upgrade; per-origin namespace prefixes to make upstream collisions impossible rather than +improbable; enable the metrics extension so cache growth and eviction are visible; and run +the disconnected drill that would let the site claim WAN independence with evidence.