arc: reading key (standalone page), top-to-bottom legend lighting, end-to-end flow blueprint

This commit is contained in:
2026-08-17 15:01:19 +10:00
parent e69d0854b3
commit 5a7b45f1b9
6 changed files with 177 additions and 14 deletions
+55 -9
View File
@@ -6,6 +6,15 @@
<p class="scrollcue" aria-hidden="true">scroll<span></span></p>
</header>
<aside class="key" aria-label="reading key">
<p class="kt">Reading key &middot; this page stands alone - anything it leans on is listed here</p>
<ul>
<li><b>C-I</b><span>Docker: an image is a stack of frozen diffs. Referenced by Course VII.</span><i>course lands here soon</i></li>
<li><b>C-II</b><span>Kubernetes: a cluster is a promise - the reconciliation loop. Referenced by Courses VI and VIII.</span><i>course lands here soon</i></li>
<li><b>BLOG</b><span>Consistency by construction beats consistency by discipline - Course VIII's contexts move, lived on a real fleet.</span><a href="https://www.bztmon.com/blog/one-value-many-enforcers/">read it on bztmon.com</a></li>
</ul>
</aside>
<!-- ============ COURSE VI ============ -->
<section class="course" id="c6" data-scene="gitops" aria-labelledby="c6h">
<header class="chead">
@@ -30,7 +39,7 @@
</div>
<div class="prose">
<h3>The loop you already know, one level up</h3>
<p>Course II taught the reconciliation loop: desired versus actual, fix the difference, repeat.
<p>C-II taught the reconciliation loop: desired versus actual, fix the difference, repeat.
ArgoCD is the same habit applied to <b>delivery</b>. An Application names a repo, a path and a
revision - watch this branch of this repository - and the controller renders what it finds
there, compares it against the live cluster, and syncs the difference. The deploy button is a
@@ -44,7 +53,7 @@
it back, and what leaves git leaves the cluster. Rollback is <span class="mono">git revert</span>,
which is why commit hygiene is an operational skill.</p>
<p class="fieldnote"><b>Field note.</b> Fighting the reconciler over SSH is arm-wrestling a
machine that does not get tired - Course II's warning, now with a face. The fix is always
machine that does not get tired - C-II's warning, now with a face. The fix is always
upstream, in git.</p>
<h3>The one thing git never holds</h3>
<p>Git holds everything except secrets - a secret in git is published, forever, to everyone who
@@ -57,7 +66,7 @@
<p class="closeline">Delivery stops being an event and becomes a property: the cluster is always
converging on what the repository says. "Who deployed this?" becomes "who merged this?" - and
that question always has an answer.</p>
<p class="reads"><b>Pre-reads:</b> Course II &middot;
<p class="reads"><b>Pre-reads:</b> C-II &middot;
<a href="https://kubernetes.io/docs/concepts/">Kubernetes concepts</a> &middot;
<a href="https://docs.github.com/en/get-started">git + pull requests</a>
&ensp;<b>Further:</b> <a href="https://argo-cd.readthedocs.io/">Argo CD</a> &middot;
@@ -117,7 +126,7 @@
<p class="closeline">Names that can move are convenient exactly until they move. Address content
by what it is, and the supply chain stops being a chain of trust and becomes a chain of
arithmetic.</p>
<p class="reads"><b>Pre-reads:</b> Course I &middot;
<p class="reads"><b>Pre-reads:</b> C-I &middot;
<a href="https://kubernetes.io/docs/concepts/containers/images/">Kubernetes images</a>
&ensp;<b>Further:</b> <a href="https://zotregistry.dev/">zot</a> &middot;
<a href="https://learn.microsoft.com/azure/container-registry/">Azure Container Registry</a> &middot;
@@ -164,7 +173,7 @@
one values file: names, addresses, sizes, flags. The context is deliberately values-only; the
moment it carries its own manifests there are two owners for one object, and they will
disagree. One value can feed many rendered artefacts - an address appearing in the app config,
the network attachment and two policies renders from one field, so the copies cannot diverge.</p>
the network attachment and two policies renders from one field, so the copies cannot diverge - the lived version is <a href="https://www.bztmon.com/blog/one-value-many-enforcers/">BLOG in the key above</a>.</p>
<p class="sayit">The chart owns the shape. The context owns the numbers.</p>
<h3>Make the template refuse</h3>
<p>A template that renders whatever it is given just moves the failure downstream. The grown-up
@@ -176,7 +185,7 @@
<p class="closeline">Fleet consistency is not a discipline problem, it is a construction
problem. Divergence stops being something you police and becomes something the tooling cannot
express.</p>
<p class="reads"><b>Pre-reads:</b> Course II &middot;
<p class="reads"><b>Pre-reads:</b> C-II &middot;
<a href="https://kubernetes.io/docs/concepts/overview/working-with-objects/">Kubernetes objects</a>
&ensp;<b>Further:</b> <a href="https://helm.sh/docs/">Helm docs</a> &middot;
<a href="https://helm.sh/docs/chart_template_guide/">chart template guide</a> &middot;
@@ -245,6 +254,27 @@
</div>
</section>
<section class="course" id="flow" aria-labelledby="flowh">
<header class="chead">
<p class="eyebrow">The whole arc &middot; end to end</p>
<h2 id="flowh" class="thesis">One flow, <em>no gaps.</em></h2>
<p class="lede">Every course above is one stretch of the same journey. Here is the full run,
drawn in the house blueprint style: the change lane, the shape lane, the artefact lane and
the secret lane, all converging on one running workload.</p>
</header>
<figure class="blueprint">
<img src="__DIAG_FLOW__"
alt="End-to-end delivery flow. Change lane: a commit lands in the GitHub repository, ArgoCD
renders and diffs it, and syncs the cluster - the cluster pulls, nothing pushes.
Shape lane: the Helm chart plus a per-cluster values context passes the schema gate
and renders the manifests ArgoCD applies. Artefact lane: CI builds once, pushes to
Azure Container Registry, the zot mirror caches it, and the node pulls by digest.
Secret lane: Azure Key Vault holds the values, the External Secrets operator syncs
them in - git only ever holds the reference. All four lanes converge on the running
workload.">
</figure>
</section>
<footer class="outro">
<p>The Exploded Cluster &middot; the delivery arc &middot; every scene is one generated hero,
sliced and scrubbed - no diagrams were harmed.</p>
@@ -275,6 +305,22 @@ b{color:var(--ink)} .mono{font-family:var(--mono);font-size:.9em;color:var(--cya
color:var(--dim);margin-top:4.5rem;display:flex;flex-direction:column;align-items:center;gap:.7rem}
.scrollcue span{display:block;width:1px;height:46px;background:linear-gradient(var(--cyan),transparent)}
.key{max-width:760px;margin:0 auto;padding:0 24px 4vh}
.key .kt{font-family:var(--mono);font-size:.68rem;letter-spacing:.16em;text-transform:uppercase;
color:var(--dim);text-align:center;margin:0 0 1rem}
.key ul{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:10px}
.key li{display:grid;grid-template-columns:52px 1fr auto;gap:14px;align-items:baseline;
border:1px solid var(--line);border-radius:8px;padding:10px 16px;background:#0a1020}
.key b{font-family:var(--mono);font-size:.72rem;letter-spacing:.1em;color:var(--cyan)}
.key span{font-size:.82rem;line-height:1.5;color:var(--dim)}
.key i{font-family:var(--mono);font-size:.62rem;letter-spacing:.1em;text-transform:uppercase;
color:var(--amber);font-style:normal;white-space:nowrap}
.key a{font-family:var(--mono);font-size:.66rem;letter-spacing:.06em;white-space:nowrap}
@media (max-width:700px){.key li{grid-template-columns:44px 1fr}.key i,.key a{grid-column:2}}
.blueprint{max-width:1220px;margin:2vh auto 4vh;padding:0 24px}
.blueprint img{width:100%;height:auto;display:block;border-radius:12px}
.course{position:relative;padding-top:10vh}
.chead{max-width:64ch;margin:0 auto;padding:4vh 24px 2vh;text-align:center}
.chead .thesis{font-size:clamp(1.6rem,4vw,2.6rem)}
@@ -370,16 +416,16 @@ body:not(.fx) .bench{display:none}
return d;
});
u.flat.style.visibility = "hidden";
const dists = u.man.parts.map(p => Math.hypot(p.dx, p.dy));
const maxD = Math.max(...dists, 1e-6);
const lis = [...u.legend.children];
const n = lis.length;
u.apply = t => {
u.man.parts.forEach((p, i) => {
u.slabs[i].style.transform =
"translate3d(" + (p.dx * (1 - t)).toFixed(3) + "%," + (p.dy * (1 - t)).toFixed(3) + "%,0)";
});
u.scene.style.transform = "scale(" + (0.95 + 0.05 * t).toFixed(4) + ")";
lis.forEach((li, i) => li.classList.toggle("lit", t >= 0.82 * (dists[i] / maxD)));
// the side list lights in reading order, top to bottom, as the scrub advances
lis.forEach((li, i) => li.classList.toggle("lit", t >= 0.9 * ((i + 0.5) / n)));
};
// bench: hand scrub + HUD (pilot instrument - strip for the site ship)
const bench = u.sec.querySelector(".bench");
+3 -1
View File
@@ -31,7 +31,8 @@ const heroes = {
__HERO_APPENDIX__: "appendix-dependency-ledger.webp",
};
for (const [ph, file] of Object.entries(heroes)) body = body.replace(ph, "/assets/" + file);
if (body.includes("__HERO_") || app.includes("__MAN_")) throw new Error("unfilled placeholder");
body = body.replace("__DIAG_FLOW__", "/assets/delivery-flow.svg");
if (body.includes("__HERO_") || body.includes("__DIAG_") || app.includes("__MAN_")) throw new Error("unfilled placeholder");
const favicon = "data:image/svg+xml," + encodeURIComponent(
`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><rect width="32" height="32" fill="#070b14"/><rect x="6" y="4" width="14" height="7" rx="1.5" fill="#3fbaf5"/><rect x="11" y="13" width="14" height="7" rx="1.5" fill="#7dd0f9"/><rect x="6" y="22" width="14" height="7" rx="1.5" fill="#e879f9"/></svg>`);
@@ -61,6 +62,7 @@ writeFileSync(join(DIST, "arc.js"), app);
copyFileSync(join(ROOT, "gsap.min.js"), join(DIST, "gsap.min.js"));
copyFileSync(join(ROOT, "st.min.js"), join(DIST, "st.min.js"));
for (const f of Object.values(heroes)) copyFileSync(join(ROOT, "assets/dist", f), join(DIST, "assets", f));
copyFileSync(join(ROOT, "assets/dist/delivery-flow.svg"), join(DIST, "assets/delivery-flow.svg"));
// CSP gate: no inline <script> may survive into the served HTML
const inline = (html.match(/<script(?![^>]*src=)[^>]*>/gi) || []).length;
+3 -2
View File
@@ -22,9 +22,10 @@ out = (ROOT / "pilot/arc.tpl.html").read_text() \
.replace("__HERO_GITOPS__", b64("course-VI-gitops.webp")) \
.replace("__HERO_SUPPLY__", b64("course-VII-supply-chain.webp")) \
.replace("__HERO_HELM__", b64("course-VIII-helm-press.webp")) \
.replace("__HERO_APPENDIX__", b64("appendix-dependency-ledger.webp"))
.replace("__HERO_APPENDIX__", b64("appendix-dependency-ledger.webp")) \
.replace("__DIAG_FLOW__", "data:image/svg+xml;base64," + base64.b64encode((ROOT / "assets/dist/delivery-flow.svg").read_bytes()).decode())
assert "__MAN_" not in out and "__HERO_" not in out, "unfilled placeholder"
assert "__MAN_" not in out and "__HERO_" not in out and "__DIAG_" not in out, "unfilled placeholder"
out = (f"<script>{(ROOT / 'gsap.min.js').read_text()}</script>\n"
f"<script>{(ROOT / 'st.min.js').read_text()}</script>\n" + out)
+1 -1
View File
@@ -5,7 +5,7 @@
{
"name": "image",
"title": "The image",
"blurb": "Course I's layers, shipped as one unit - the thing whose name we must say precisely.",
"blurb": "C-I's layers, shipped as one unit - the thing whose name we must say precisely.",
"z": 0,
"points": [
[