Skip to content

Documentation platform

VerifiedOwner Documentation integratorLast verified onix-docs@a2-publication

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.

python tools/release.py

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

python tools/check_drift.py

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

python tools/drift_negative_test.py

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

python -m mkdocs build --strict

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

python tools/census.py

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.yml
  • onix-docs:requirements.txt
  • onix-docs:tools/check_drift.py
  • onix-docs:tools/release.py
  • onix-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.