Admin & engineering
How this site is built, for anyone who wants to check the claim rather than take it on trust — which is the same standard the site holds every threat model on it to.
The pipeline
Every push to dev runs .github/workflows/deploy-pages.yml: validate → auto-tag → deploy. Pull requests run validation only, so branch work is gated before it reaches the release branch. A validation failure stops the release: no tag, no publish.
| Stage | What it does |
|---|---|
| validate | Runs every generator's --check mode (papers, documents, sitemap, llms-full), then node admin/build/validate.js — the release gate, in full below |
| tag-release | Every push to dev is a minor release, tagged v{release}.{major}.{minor}. The version is owned by admin/build/version.txt and must agree with the release commit's subject ("site vX.Y.Z: ..."); CI verifies the two agree and that the bump is the next minor or a deliberate major |
| deploy | Publishes the tagged commit to GitHub Pages. Runs on manual dispatch even without a tag, never when validation failed |
Same three-stage pattern as SGit-AI/SGit-AI__Website__PKI and SGit-AI/SGit-AI__Website__Influences — copied deliberately rather than reinvented, per this site's own commissioning brief: "Base House pattern (copy pki.sgit.ai)."
The release gate — admin/build/validate.js
Plain Node, no dependencies. Seven checks; any failure exits 1, so no tag and no publish. Five are the house checks shared with every sibling site (version agreement, internal links and fragments, canonical host, no script loaded twice, the credential-leak tripwire). Two are this site's own editorial rules, made executable rather than stated:
- The framework-reproduction gate. STRIDE, MITRE ATT&CK, CAPEC, CWE/CVE, OWASP Top 10 and ASVS are referenced by name and linked; this site's tables are its own, never a copied framework text. Every
<blockquote>must declare whose words it carries viadata-quote; anything notdata-quote="site"is capped at 40 words. - The disclosure-location tripwire. Nothing in the tree may look like a source-file-and-line pointer to an unfixed finding, outside
/briefs/— the verbatim source pack, which is a separate, already-made, dated disclosure decision rather than a live pointer into a running codebase. This is the disclosure rule made into a build failure rather than left as a paragraph nobody re-checks.
Build tooling
| File | Owns |
|---|---|
admin/build/version.txt | The version — single source of truth |
admin/build/chrome.py | The nav, the footer and the version-badge wiring, rewritten across every page |
admin/build/pagelib.py | The shared page shell and the write-or-check writer every generator uses |
admin/build/gen_papers.py | /papers/ — the index and one page per white paper |
admin/build/gen_documents.py | /documents/ — the reader page for each source document, from /briefs/ |
admin/build/gen_sitemap.py | /sitemap.xml, generated from the tree |
admin/build/gen_llms_full.py | /llms-full.txt, generated from llms.txt plus every file under /briefs/ |
admin/build/validate.js | The release gate |
Python 3 and Node, both stdlib-only (plus marked and, for a mermaid fence, mermaid, both loaded from a CDN client-side for the in-page document reader). Nothing to install for the build itself.
The version badge is read at load time
chrome.py writes the version into assets/version.js once per release; assets/nav.js fills the badge and the footer's release-history link from it at load time. validate.js gates that version.js agrees with version.txt. This is the arrangement pki.sgit.ai adopted at v0.1.66 after learning the alternative the hard way — stamping the version literally into every page meant a one-line release rewrote roughly two hundred files. This site starts with the load-time version from its first release.
Release process
- Bump
admin/build/version.txt(vX.Y.Z, exactly once per release) and add a row toadmin/versions.html; updateadmin/comms.htmlif anything changed - Regenerate the pages that come from data:
python3 admin/build/gen_papers.py && python3 admin/build/gen_documents.py python3 admin/build/chrome.py— after the generators, so it stamps the version and nav/footer into pages they have just produced- Regenerate the files that read the tree and the stamped twins — after chrome, or they assemble a stale version line:
python3 admin/build/gen_sitemap.py && python3 admin/build/gen_llms_full.py - Validate exactly what CI runs: the four generators in
--checkmode, thennode admin/build/validate.js git commit -am "site vX.Y.Z: ..." && git push -u origin dev— thesite vX.Y.Z:prefix is load-bearing:tag-releasefinds the release commit by scanning subjects for it