From 7f7035b98f616c0c3636f284c8dad38e95f3c8ba Mon Sep 17 00:00:00 2001 From: jwright Date: Tue, 25 Aug 2026 08:08:18 +1000 Subject: [PATCH] mirror rev 4: evidence-verified rewrite, six-diagram artefact set, front-end and build-gate hardening Content corrected against upstream docs, deployed-version source and the live config: retention/GC semantics (untagged manifests including digest-only pull-through entries are GC-eligible; keepUntagged is v2.1.19+, not deployed), warm-cache guarantees (tag pulls revalidate upstream per open issue; digest pulls serve locally), retry arithmetic (buildah --retry counts retries: 16 max, not 12), five-upstream flat-namespace routing stated as observed risk with a proposed prefix redesign, auth-canary design validated against longest-match policy precedence with the Docker-client caveat, coalescing reframed from deadlock to design with the restart as recovery-not-root-cause. Adds fact panel, contents, failure-mode and cache test matrices, decision table, open verification register and primary-source references. Front-end: grid full-bleed (no viewport units - no scrollbar overflow), shell-level skip link, real 1600x872 intrinsic dims injected from bytes, lazy below-fold plates, AA code-comment contrast, scene engine rewritten (IO-gated, rAF-coalesced, live reduced-motion, hidden-tab pause, zero guards). Assembler: per-page gates plus mirror-specific required/banned content assertions. README describes the multi-page site honestly. --- README.md | 65 ++- pilot/assemble-arc.mjs | 58 ++- pilot/index.tpl.html | 3 - pilot/mirror.js | 127 +++-- pilot/mirror.tpl.html | 1094 ++++++++++++++++++++++++++-------------- 5 files changed, 912 insertions(+), 435 deletions(-) diff --git a/README.md b/README.md index fe042c8..4d74547 100644 --- a/README.md +++ b/README.md @@ -1,27 +1,52 @@ -# The Exploded Cluster +# The Teaching Lab (learn.bztmon.com) -Interactive scroll-driven teardown of Kubernetes / OpenShift / Docker, in the estate's -Bat-Computer visual world (void `#070b14`, cyan `#3fbaf5` → magenta `#e879f9`). +A multi-page static technical learning site: scroll-driven teardowns of infrastructure the +author actually operates. Dark engineering-dossier aesthetic (void `#070b14`, cyan `#3fbaf5`, +magenta `#e879f9`), dependency-light by design, strict security posture. -- **v2** (tag `v2`) — CSS `preserve-3d` diagrams. Parked: eyeballed px positions, misaligned at some widths. -- **v3** (in progress) — ships PUBLIC at **learn.bztmon.com**. -- **v3 build** — Nano Banana 2 exploded hero shots, hand-sliced into `clip-path` parts and - scrubbed collapsed → hero with GSAP. Spec: `homelab/specs/SPEC-exploded-cluster-v3.md`. +## Pages +| Route | Page | Source | +|---|---|---| +| `/` | The Teaching Lab - chapter directory | `pilot/index.tpl.html` | +| `/cluster` | Chapter 01: The Exploded Cluster - eleven-course container-platform teardown | `pilot/arc.tpl.html` + `pilot/manifest-*.json` | +| `/mirror` | Chapter 02: The Mirror - a zot pull-through registry, its failure modes and operations | `pilot/mirror.tpl.html` + `pilot/mirror.js` | + +## Build +`bash build.sh` -> `dist/`. The assembler (`pilot/assemble-arc.mjs`, plain node, no +dependencies) wraps each page in a shared document shell (canonical/OG metadata, build +revision, skip link), content-fingerprints every file under `/assets/` (immutable-cached +by nginx), splits page scripts into external fingerprinted files, injects intrinsic image +dimensions (WebP and JPEG parsed from the actual bytes) with deliberate lazy/eager loading, +and runs per-page preflight gates: no inline scripts (CSP `script-src 'self'` with no +exceptions), exactly one `h1`, unique ids, no dangling anchors, no unresolved placeholders, +and page-specific content assertions. A failed gate fails the build. + +Ship: `podman build` (multi-stage: node builds `dist/`, `nginx-unprivileged` serves it, +both digest-pinned) -> push to the estate registry -> digest-pinned deploy via GitOps. +`nginx/` carries the runtime config: strict CSP, HSTS, no-cache HTML with immutable +fingerprinted assets, security headers on every location. + +## Accessibility and performance +Skip link on every page (hidden until keyboard focus), one `h1` per page, stable section +anchors, reduced-motion honoured live (effects also pause when the tab is hidden or the +scene is off screen), intrinsic image dimensions to prevent layout shift, below-fold images +lazy-loaded, system font stacks (CSP permits no third-party fonts). ## Layout | Path | What | |---|---| -| `exploded-cluster.html` | v2 assembled single file (174KB) | -| `ec2-part1.html` / `ec2-part2.html` | v2 source split — markup+CSS / app JS | -| `gsap.min.js` · `st.min.js` | GSAP 3.13 + ScrollTrigger, inlined at assemble time | -| `prompts/` | NB2 prompt contract — master style block + per-scene prompts | -| `tools/slicer.html` | slice editor: load a hero, tune cut lines, export the manifest | -| `assets/raw/` | curated NB2 heroes, straight from the generator | +| `pilot/` | page templates, page scripts, scene manifests, the assembler | +| `assets/dist/` | production heroes and diagrams for `/cluster` | +| `assets/mirror/` | plates for `/mirror` | +| `content/` | course source notes | +| `nginx/` | runtime config baked into the image | +| `tools/slicer.html` | slice editor: load a hero, tune cut lines, export a scene manifest | +| `prompts/` | image prompt contract for the house visual style | +| `exploded-cluster.html`, `ec2-part*.html` | v2 (parked, superseded by `/cluster`) | +| `gsap.min.js` / `st.min.js` | GSAP 3.13 + ScrollTrigger, fingerprinted at assemble time | -Rebuild v2: `cat ec2-part1.html <(echo "") ec2-part2.html > exploded-cluster.html` - -## The slicing rule (load-bearing — read before touching the engine) -Parts occlude each other in 2D, so every slice carries a hidden notch where the part in front -covers it. Animating **collapsed → hero** only ever increases occlusion, so notches stay buried. -**Never overshoot past the hero layout and never fan parts sideways** — either exposes the tears. -Region boundaries overlap upward by `EPS`; expanding downward would ghost. +## The slicing rule (load-bearing - read before touching the arc engine) +Parts occlude each other in 2D, so every slice carries a hidden notch where the part in +front covers it. Animating **collapsed -> hero** only ever increases occlusion, so notches +stay buried. **Never overshoot past the hero layout and never fan parts sideways** - either +exposes the tears. Region boundaries overlap upward by `EPS`; expanding downward would ghost. diff --git a/pilot/assemble-arc.mjs b/pilot/assemble-arc.mjs index 7504565..8d0e619 100644 --- a/pilot/assemble-arc.mjs +++ b/pilot/assemble-arc.mjs @@ -40,6 +40,30 @@ function webpSize(buf) { return null; } +// --- intrinsic JPEG dimensions (SOF0/SOF2 markers) for the mirror plates --- +function jpegSize(buf) { + if (buf[0] !== 0xff || buf[1] !== 0xd8) return null; + let o = 2; + while (o + 9 < buf.length) { + if (buf[o] !== 0xff) { o++; continue; } + const marker = buf[o + 1]; + if (marker >= 0xc0 && marker <= 0xcf && marker !== 0xc4 && marker !== 0xc8 && marker !== 0xcc) { + return { h: buf.readUInt16BE(o + 5), w: buf.readUInt16BE(o + 7) }; + } + o += 2 + buf.readUInt16BE(o + 2); + } + return null; +} + +// skip link: emitted by the shared shell, so its accessibility CSS lives in the shell +// too - visually hidden until keyboard focus, visible and prominent when focused. +const SKIP_CSS = ``; + let tpl = readFileSync(join(ROOT, "pilot/arc.tpl.html"), "utf8"); // split the app script (the last