The system, drawn

Architecture

The system end to end: what it is made of, how one topic becomes a page, how a change gets in, and what stops a bad build reaching readers.

31 stages 880 pages 7 diagrams no framework
Overview

A content pipeline with a gate at every seam

21 issues, 685 topics across 45 categories and 362 commands, rendered into 880 static pages by 31 build stages and published to one origin. No framework, no client-side rendering, no runtime dependencies.

SystemWhat is this made of?
SOURCE OF TRUTHtaxonomy.pyissue registercommand corpusreader checklistAUTHOREDcontent/ 25 pagesstatic/ assets + fontsindex.base.htmlBUILDbuild.sh 31 stagespage generatorswhole-site passesGATESverify.pytest_render.pyorigin auditempty-build gateDELIVERYGitHub Actionswrangler deploy dist/EDGECloudflare Worker_headers CSP HSTS
Source of truth at the bottom, reader at the top. Everything above the first band is derived — none of it is edited by hand, and a stage that would let two derived values disagree fails the build.

The shape matters more than the stack. Four data files describe the subject; everything a reader sees is computed from them at build time. That is why the count on the homepage, the count on a category hub and the count in the repository's README cannot drift apart — they are the same expression evaluated three times, not three numbers maintained in three places.

Data flow

One topic, from declaration to reader

Follow a single topic through the system. Every hop is a real file boundary, and the label on each edge is the operation that crosses it.

Data flowHow does one topic become a page?
taxonomy.pygeneratorimport PILLARSdist/*.htmlwrite pagewhole-sitepassinject nav, search, JSON-LD, fontsverify.pyread back and assertsitemap.xmlif not noindexwranglerupload changed assetsreader:443 · cached 1 week
The read-back at verify.py is the important hop. The build does not trust what it just wrote — it reads the rendered HTML and asserts against it, which is how a page that claims 35 topics while rendering 34 gets caught.
Why the sitemap is a fork in the path

784 of the pages are honest placeholders for checklist items not yet written, marked noindex,follow. They are real pages with real internal links, so they belong in the site — but a sitemap is a request to crawl, and asking a crawler to fetch 784 URLs that then decline to be indexed wastes the crawl budget of a site that has very little. So the sitemap carries the 95 indexable ones and the rest stay linked but unsubmitted.

The build

Opened up

31 stages in a fixed order. The order is not stylistic: a stage that counts or classifies against the filesystem must run after the stages that write what it counts.

BuildWhat runs, in what order, reading what?
TOOLS/BUILD.SHtaxonomy.pycontent/static/dist/ — 879 files_headerssitemap.xmlSEEDrm -rf dist/rebuilt from empty every runcp content/ static/sources copied, never mutatedCONTENT GENERATORSbuild_hubs45 category hubs — runs twicebuild_k8s / openshift /foundationthe deep-divesbuild_commands362 commandsbuild_topicmapthe 942-item checklistbuild_topic_pagesone honest page per unwritten itemAGGREGATION — COUNTS THE FINISHED SITEbuild_colophonstage list, page countbuild_aboutderived from taxonomy, not the READMEbuild_architecturethis pageWHOLE-SITE PASSESbuild_navmega-menu into every pagebuild_searchindex + wiringbuild_seoJSON-LD, icons, font…build_canonicalone origin, everywhereGATESbuild_headersCSP derived from what pages fetchverify.pylinks, nesting, page agreement, sitemap
build_hubs appears once but runs twice. It emits the stylesheet a later stage needs, so it must come early; it also prints counts that are only right once the pages it classifies exist. One pass cannot satisfy both.

Before the output directory existed, this ordering problem was invisible. The build wrote into the repository, so every counting stage read the previous build's output and was quietly one build behind. Wiping dist/ on every run exposed it immediately: 33 checklist items dropped from Live to Planned because the pages proving they were covered had not been generated yet.

Contribution

Pull request flow

What happens to a change between a fork and the live site — and, deliberately, what does not happen.

PR flowHow does a change get in?
forkbranchgit checkout -bedit tools/source only — dist/ is untrackedbash tools/build.shmust print: all checks passedopen PRagainst mainpr-checks.ymlbuild + verify + render, NO secretsreviewmaintainer approvesmerge to mainsquashdeploy.ymlbuild → gate → render → wrangler
Only source is edited. dist/ is untracked, so a pull request is never an 800-file diff — it is the change itself.
A fork's pull request never sees the deploy secrets

The deploy workflow triggers on push to main and workflow_dispatch only. There is no pull_request trigger and — the one that matters — no pull_request_target. That is the difference between a safe public CI pipeline and the attack that has drained plenty of real projects: a stranger's code never executes in a job holding the Cloudflare token.

PR validation therefore runs as its own workflow with no secrets at all. It can build, verify and render — it cannot deploy, and it has nothing to leak.

Runtime

The request path

There is no application server. A request resolves to a file, and the only computation at request time is header attachment.

RequestWhat happens between a click and a page?
readerDNSplatformops.srivantechnologies.com → Cloudflareedge PoP:443 TLS 1.3Worker static assetspath → file in dist/_headersCSP · HSTS · nosniff appliedHTML + assetsassets/* cached 604800s
The headers are generated at build time by walking the built pages for the origins they actually fetch from — so the policy cannot permit something the site stopped using, or block something it started.
Delivery

Deploy, and the way back

Push is the only action. Everything between it and production is automated, and every gate can stop it.

DeployWhat can stop a bad build reaching readers?
push tomaincheckoutactions/checkout @ SHAbuildbash tools/build.shempty-buildgatefind dist -name '*.html' | wc -l ≥ 500rendertests11 pages x 13 widths, real browserwranglerdeploynpx --yes wrangler@4.134.0livenew version idwranglerrollbackany prior version, on failure
The empty-build gate exists because a deploy from an empty output directory once published a site where every page 404'd. wrangler read zero files and reported success.
GateCatchesWhat it looked like when it did not exist
all checks passedbroken links, unbalanced tags, wrong canonicals, pages that disagreeAll 878 canonicals named a host being retired
empty-build gatea build that produced nothingA deploy published 0 files; the whole site 404'd
render testshorizontal overflow, duplicate fixed headersEvery page family scrolled sideways between 761px and 950px
origin audita page fetching from an unaccounted-for hostTwo LinkedIn iframes loaded on every homepage visit, unnoticed
wrangler rollbackanything the gates missedUsed once, in anger, and it worked
Security

Trust boundaries

The repository is public and the site is public. What is not public is exactly two values, and neither has ever been in the repository — a full-history scan confirms it.

TrustWhat crosses the boundary, and who can push it?
WHAT IS PUBLIC, WHAT IS NOTcontributorreadercrawlerCloudflareGitHubPUBLIC BY DESIGNthe repositorysources readable, not writablethe rendered site879 pages, crawlableSearch Console tokensserved in a meta tag anywaySECRET — NEVER IN THE REPOCLOUDFLARE_API_TOKENGitHub Actions secretCLOUDFLARE_ACCOUNT_IDGitHub Actions secretWHAT CAN EXECUTE WITH THOSE SECRETSpush to mainmaintainer onlyworkflow_dispatchmaintainer onlya fork's pull requestNEVER — no pull_request_targetENFORCED AT THE EDGEframe-ancestors 'none'clickjackingbase-uri 'none'an injected base tagobject-src 'none'plugin contentHSTS 2y preloaddowngrade
Sources being readable and sources being writable are different things. A public repo is read-only to everyone but the maintainer; visibility was never about who can change it.

One gap is left open deliberately rather than filled badly. The policy declares no script-src, because allowing the site's inline handlers would require 'unsafe-inline' — a directive that scores well on a header scanner and stops approximately no XSS. There is no live vector to close: the search box escapes every interpolated value, nothing reads a query parameter, and there is no backend. A policy that overstates its own strength is worse than an honest gap.