How this site is built: one taxonomy, a Python build, and a set of assertions that stop the build rather than let a number go stale.
Everything else in this repo is output.
There is no framework, no bundler, no runtime and no server. The site is a tree of self-contained HTML files — but almost none of them are written by hand. They are printed by Python from a handful of data files, and ./tools/build.sh prints all of them in one pass.
That exists to solve one problem. A site with 685 topics, 45 categories and 21 published issues has the same number in a dozen places — the hero counter, the pillar bars, the coverage terminal, the mega-menu, the search index, the category hubs, the feed, the sitemap, the README. Maintained by hand, those drift apart within two issues. Derived from one list, they cannot.
The next build overwrites both. Edit tools/index.base.html or tools/README.base.md instead.
Every page on this site comes out of the boxes on the left.
Rendered once at build time from the same Mermaid source the README uses — the page itself loads no diagramming library.
The first thing build.sh does is throw away the two generated files and copy their bases back over them, so every build starts clean and a half-applied edit can never accumulate.
| # | Stage | Reads | Writes |
|---|---|---|---|
| 0 | reset | index.base.html, README.base.md | index.html, README.md |
| 1 | build_kmap.py | taxonomy + issue register | knowledge map, coverage terminal, hero counter, newest-issue links |
| 2 | build_features.py | — | topic-request queue, analytics loader, RSS discovery |
| 3 | build_wire.py | taxonomy | category-hub links, nav, licence line |
| 4 | build_readme.py | taxonomy + register + commands | the README map, badge, inventory, feature table, repo tree |
| 5 | build_library.py | taxonomy | the reference-library listing |
| 6 | build_sticky.py | — | pins the Latest Issues panel beside the map |
| 7 | build_author.py | linkedin_posts.py | author profile + LinkedIn column |
| 8 | build_hubs.py | taxonomy + hubs_spec.py | the category hubs, diagrams and cross-links |
| 9 | build_foundation.py | fnd_a.py, fnd_b.py | the Foundation deep-dives |
| 10 | build_openshift.py | ocp_a.py – ocp_c.py | the OpenShift deep-dives |
| 11 | build_k8s.py | k8s_a.py, k8s_b.py, mesh_a.py | the Kubernetes and Service Mesh deep-dives |
| 12 | build_commands.py | cmd_data.py | the command references |
| 13 | build_legacy_dg.py | hub.css + the Kubernetes hub | injects a diagram into the hand-written pages that have none |
| 14 | build_legacy_chrome.py | chrome.py + the issue register | one nav, one footer and a theme toggle on the 25 hand-written pages, which had twenty-three between them |
| 15 | build_colophon.py | everything above | this page |
| 16 | build_terminal.py | terminal_fs.py | the practice terminal: a shell simulated over an in-memory tree |
| 17 | build_feed.py | the issue register | feed.xml |
| 18 | build_nav.py | taxonomy | mega-menu assets, wired into every page |
| 19 | build_search.py | everything on disk | the search index, wired into every page |
| 20 | build_seo.py | each page + register | JSON-LD, article dates, per-issue og:image |
| 21 | build_canonical.py | siteconf.BASE | rewrites every origin to the canonical one |
| 22 | verify.py | the built site | sitemap.xml, and a non-zero exit if anything is wrong |
This table is generated from build.sh itself, so a stage cannot be added without appearing here.
A deep-dive is never typed as HTML. It is a Python list of section dictionaries handed to content_page.render(), which returns one finished file.
| Consequence | Why |
|---|---|
| The CSS lives in one place | content_page.CSS is written once per output directory as topic.css; each page links it with the right number of ../ for its depth. |
| The pager is built from the reading order | ORDER in each builder is the sequence. A topic with no module yet drops out of the chain rather than producing a dead link. |
| Relative links, always | Every href counts ../ from the page's own depth, so the site works opened from disk, from a local server, from GitHub Pages and from the Worker — with no base URL configured anywhere. |
Every interactive part of this site is plain DOM.
| Behaviour | How |
|---|---|
| Theme | Read from localStorage and applied to <html> before first paint, so there is no flash of the wrong theme |
| Knowledge map | A two-level accordion over static markup; search filters chips by textContent, the status pills by data-s |
| Global search | One JSON array inlined in assets/search.js, filtered in memory — no request, no index server |
| Mega-menu | One markup block injected into every page by build_nav.py, with depth-correct links baked in |
| Command tables | Group filter and substring match over the 362 commands already in the document |
| This diagram | An inline SVG. Mermaid rendered it once, at build time |
| Analytics | GoatCounter, loaded only if a site code is set — empty by default, so nothing is sent |
The only external requests a page makes are Google Fonts and, if enabled, GoatCounter. No CDN, no third-party JavaScript.
Both origins serve the same static files. Nothing is compiled at deploy time.
tools/siteconf.py holds the canonical origin as a single constant, and build_canonical.py rewrites every known origin to it on each build, then asserts none survived — which is what keeps the Pages mirror from competing with the Worker in search results.
Every derivation asserts that it matched what it expected to match. This is the part worth copying.
| Check | What it refuses to let through |
|---|---|
re.subn(..., count=1) + assert n == 1 | A reworded sentence in the README. It fails the build rather than silently going unmaintained. |
build_canonical.py | Any foreign origin surviving the canonical rewrite. |
make_og_card.py | A missing webfont. It falls back silently and bakes the wrong typography into a PNG, so the render is asserted instead. |
build_colophon.py | A diagram edited in the README but not re-rendered — this page checks the sha256 of the Mermaid source against the committed SVG. |
verify.py | A broken link, unbalanced tags, or a page missing its chrome, across all 94 pages. It also compares the counts the homepage states against the chips it actually renders. |
A non-greedy regex that marked 49 topics live against the wrong page, and a CSS class defined twice — .tt as both a 32px theme-toggle button and the terminal text — which clipped every command line on 53 pages. Neither was visible in a diff.