threat-modeling.sgit.ai / admin

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.

StageWhat it does
validateRuns every generator's --check mode (papers, documents, sitemap, llms-full), then node admin/build/validate.js — the release gate, in full below
tag-releaseEvery 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
deployPublishes 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:

Build tooling

FileOwns
admin/build/version.txtThe version — single source of truth
admin/build/chrome.pyThe nav, the footer and the version-badge wiring, rewritten across every page
admin/build/pagelib.pyThe 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.jsThe 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

  1. Bump admin/build/version.txt (vX.Y.Z, exactly once per release) and add a row to admin/versions.html; update admin/comms.html if anything changed
  2. Regenerate the pages that come from data: python3 admin/build/gen_papers.py && python3 admin/build/gen_documents.py
  3. python3 admin/build/chrome.pyafter the generators, so it stamps the version and nav/footer into pages they have just produced
  4. 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
  5. Validate exactly what CI runs: the four generators in --check mode, then node admin/build/validate.js
  6. git commit -am "site vX.Y.Z: ..." && git push -u origin dev — the site vX.Y.Z: prefix is load-bearing: tag-release finds the release commit by scanning subjects for it