Documentation platform¶
The platform is deliberately small. A static site generator, a pinned dependency set, one manifest, six generators and eight gates. There is no server, no database, no build-time network access and no dynamic content.
Toolchain¶
The site is built with Material for MkDocs.
Every dependency is pinned to an exact version in requirements.txt, inherited
from a dependency freeze that was captured and reproduced before this site was
written.
python3 -m venv .venv-docs
.venv-docs/bin/python -m pip install -r requirements.txt
.venv-docs/bin/python -m mkdocs build --strict
The build takes no input from the network, the clock or environment variables. That is not incidental — it is what makes the determinism check below meaningful.
The single source of truth¶
manifest/page-route-contract.json defines the published page set, the
navigation order, the canonical URL of every page, the redirect map, and the
disposition of all 61 pages in the documentation set this site replaces.
Seven things are generated, and none may be hand-edited:
| Generated file | Generator | Contains |
|---|---|---|
manifest/page-route-contract.json |
tools/gen_manifest.py |
The contract itself, derived from the vendored immutable disposition matrix. |
manifest/availability-overlay.json |
tools/gen_overlays.py |
Per page, the distribution of its claims across the availability lifecycle. |
manifest/disclosure-review.json |
tools/gen_overlays.py |
The publication decision for every claim in every lane. |
mkdocs.yml |
tools/gen_mkdocs.py |
Navigation tree and redirect map. |
docs/reference/capabilities.md |
tools/gen_reference.py |
The capability register, filtered by disclosure decision. |
docs/reference/api-navigation.md |
tools/gen_api_navigation.py |
Route and schema navigation, from the vendored backend API surface. |
docs/reference/venues.md |
tools/gen_venue_matrix.py |
The venue matrix, from the vendored venue surface. |
| Status strips and evidence blocks | tools/docs_hooks.py |
Per-page provenance, rendered from front matter at build time. |
Two inputs are vendored rather than fetched: manifest/api-surface.json and
manifest/venue-surface.json were extracted once from the application
repositories at the recorded pins by tools/extract/extract_surfaces.py, which
never runs during a build. This repository therefore builds and verifies with no
application source present — the same discipline the 61-row disposition matrix
already used.
Editing a generated file by hand is not a shortcut; the drift check will fail.
Regeneration is an authoring step:
python tools/regenerate.py # rewrites the seven files above
python tools/release.py # verifies; never regenerates
Structural isolation of quarantined material¶
docs/ is the build input. internal/ is a sibling directory and is never a
build input. Quarantined material lives in internal/ and therefore cannot reach
the public site by accident — not through a stray navigation entry, not through a
glob, and not through a copy-paste mistake.
This is a structural guarantee rather than a procedural one, which is the only kind worth relying on. What is quarantined, and why, is published as a register; see the documentation model.
Release gates¶
All eight must pass before anything is published, and the order is part of the design.
1. Immutable inputs¶
The vendored 61-row disposition matrix is hashed and compared with its recorded SHA-256 before anything else happens.
2. Drift — first, and side-effect-free¶
Each generator is re-run into a temporary directory and its output byte-compared with the committed file. The checked tree is never opened for writing, so a crash or an interrupt cannot leave unreviewed generated output behind, and a detected difference cannot be erased by the act of detecting it.
The order matters as much as the mechanism. A release driver that regenerates
first can never find drift, because it has already overwritten the evidence.
tools/release.py therefore runs this gate before anything else and does not
regenerate at all.
3. Drift negative test¶
A gate nobody has watched fail is a gate nobody knows works. This test applies a
temporary hand edit to a generated file, requires the drift check to fail,
requires the edit to still be there afterwards — proving the checker did not
quietly regenerate over the problem — then restores the file and requires the
check to pass again. Restoration runs from a finally, so an interrupted test
still leaves the tree exactly as it found it.
4 and 5. Two strict builds¶
Strict mode promotes every warning to an error: unresolved internal links, unresolved anchors, pages missing from navigation, navigation entries pointing at files that do not exist. The front-matter hook additionally fails the build for any page with incomplete or invalid provenance metadata. The build runs twice, into separate output directories.
6. Determinism, reported two ways¶
Both builds are hashed and compared raw and normalized, and both results are recorded.
SOURCE_DATE_EPOCH is fixed for both builds. Without it MkDocs stamps
sitemap.xml.gz with wall-clock time and the gzip container bytes differ across
days; with it, the raw bytes match too. The receipt states which guarantee
actually held rather than asserting the stronger one and normalizing the
inconvenient file away.
7. Route-contract closure and census¶
The census reconciles the built output against the contract in both directions,
and every membership check is an exact set comparison rather than a lower
bound. A page in the contract with no HTML output fails; an HTML output with no
contract entry fails; a search index or sitemap carrying an entry the contract
does not name fails just as loudly as one missing an entry. It also verifies that
every declared redirect emitted a redirect document resolving to a real published
file, that each page has exactly one <h1> and declares the canonical URL the
contract assigns it, that robots policy names the sitemap and does not disallow
the site, and that the 404 page renders real content.
8. Publication safety, disclosure and repository exposure¶
python tools/safety_scan.py # built site: brand names, secrets, topology
python tools/disclosure_gate.py # built site and sources: withheld subjects
python tools/repo_gate.py # tracked repository: allowlist and secrets
Three different scopes, because one scan cannot cover them.
The safety scan reads the built site for retired brand names, credential-shaped strings, private keys, developer filesystem paths, private IP ranges, private operational topology, and the specific claims this documentation has committed not to make.
The disclosure gate is semantic rather than lexical. A regex secret-scanner cannot tell the difference between naming a withheld subject and disclosing its mechanics, and the difference is the whole point: "session and authentication detail is withheld" is exactly what an honest site should say, while naming the guard, the header or the storage item is not. Each rule states which withheld subject it protects, and each allowlisted phrase states why it is safe. Without this gate, quarantine is enforced only by old URL, which is not a disclosure control at all — the same subject simply reappears under a different route.
The repository exposure gate reads the tracked tree rather than the built
site. Every tracked path must match an explicit allowlist, so a new file type is
refused until someone decides it belongs in a published repository. Build output,
virtual environments, caches and editor state must not be tracked. internal/
must contain nothing but its own README. A credential and private-path scan runs
over every tracked text file — with one documented exception: an evidence
packet's falsifiable_check is a command a reviewer runs locally, and stripping
its path would make the check unrunnable. The published claim text is separately
forbidden from carrying one, and the reference generator enforces that.
Independent verification¶
Beyond the mechanical gates, a separate reviewer with no write access to any
documentation page attempts to falsify representative claims from every lane
against the pinned sources, using the falsifiable_check command recorded with
each claim. Results are written to evidence/verification/ in this repository.
The separation matters: the reviewer cannot fix a page to make a check pass, so the only available outcome for a bad claim is to report it. See evidence and testing.
What the platform deliberately lacks¶
- No analytics or third-party scripts. Nothing on this site reports a reader's behaviour anywhere.
- No web fonts fetched at runtime. The theme's font loading is disabled and a system font stack is used, so rendering does not depend on a third party.
- No versioned documentation picker. One published version, with explicit source pins on every page, is more honest for a system changing this quickly than a version selector implying long-term support of older states.
- No comments, feedback widget or search analytics.
Evidence and source pins for this page
Verified. Current behaviour, confirmed in source at the pinned commit.
Verified on against the following immutable sources:
onix-docs:mkdocs.ymlonix-docs:requirements.txtonix-docs:tools/check_drift.pyonix-docs:tools/release.pyonix-docs:tools/drift_negative_test.py
Status tokens are defined on the documentation and status model page. Every pin on this site is listed under versions and source pins.