Provenance
The audit map
For every published artefact on axilog.io: where the source of truth lives, which build step produces it, and what verification check gates it. The Axioma framework's claim is verifiability; this page is the evidence map that backs that claim.
Pipeline description verified against build.sh as of 15 May 2026.
Source chain
Every published artefact derives from a versioned source file in the public repository at github.com/SpeyTech/axilog.io (the site infrastructure) and github.com/SpeyTech/axioma-spec (the canonical specification source the Vault imports from). The repository is the audit-stable source of truth; every visible page corresponds to one or more files in version-controlled history.
For specifications specifically, three audit-trail surfaces align:
- Frontmatter
date— the spec's effective date. - Frontmatter
changelog— the revision history declared by the author. Surfaced at/specs/<slug>/changelog/. - Git commit history — every change to the spec file, accessible via the repository's blame and log views.
The build emits a warning when these surfaces disagree (a spec file's last git mtime exceeds its newest frontmatter changelog entry by more than one day). The check is informational; the discipline is editorial.
Build pipeline
The deploy is orchestrated by a single shell script, build.sh, which executes the steps below in order. Each step is gated:
- abort-deploy — failure prevents the atomic swap; the live site is unchanged.
- rollback — failure swaps the previous build back into place after the deploy has already happened.
- informational — failure is logged but the deploy continues. Used for non-correctness paths (e.g. IndexNow crawl-acceleration).
- 1
Compliance frontmatter lint
abort-deployscripts/compliance_lint.jsProduces: Verified resolution of every spec's `compliance` frontmatter array against the canonical taxonomy at src/lib/compliance-taxonomy.ts.
Walks every spec markdown file, extracts the compliance array, and verifies each value resolves to a canonical entry (matching id, displayName, or any alias). Runs before any rendering so unresolved values surface as data errors rather than as silent 404s on /compliance/<slug>/ pages. The taxonomy module is the single source of truth; adding a new standard requires adding a canonical entry there.
- 2
AEO frontmatter lint
abort-deployscripts/aeo_lint.jsProduces: Verified `defines` and `answers` fields on every insight; Definition-block linter check for core concepts.
Scoped to src/content/insights/ — specs are SHALL-bound formal prose and would fire false positives on every check. The linter enforces the AEO contract: every insight that defines a CORE_CONCEPT must include that concept verbatim in body text, and every insight must provide a 40–60-word `answers` field for llms-full.txt extraction.
- 3
Changelog drift check
informationalscripts/changelog_drift_check.jsProduces: Warning report comparing each spec's git mtime against its frontmatter changelog's newest entry date.
Per-spec, runs `git log -1 --format=%aI` against the file and compares against the maximum date in the frontmatter `changelog` array. Drift beyond one day surfaces as a build-log warning. Skipped (with explicit warning) on shallow clones — `git rev-parse --is-shallow-repository` returning `true` means the file-mtime data would be the shallow ceiling, producing false positives across the corpus.
- 4
Astro static build
abort-deploynpx astro build --outDir ./dist.newProduces: The full static site under dist.new/: HTML for every route, the JSON-LD blocks, sitemap.xml, dynamic llms.txt and llms-full.txt, the compressed CSS and JS bundle.
Output is written to dist.new/ rather than dist/ — the live build is untouched until the atomic swap step succeeds. Build into a staging directory means an Astro failure (broken schema, missing import, malformed markdown) leaves the production deploy intact.
- 5
Per-spec OG card generation
abort-deployscripts/generate-og-cards.mjsProduces: 15 PNG images at dist.new/og/specs/<slug>.png, each ~1200×630 with the spec id, title, layer pill, and dclass pill rendered from the brand woff2 fonts via the woff2→wawoff2→TTF→resvg pipeline.
Pre-decompresses Inter Variable and JetBrains Mono Variable from woff2 to TTF (resvg-js does not natively decompress woff2; see operational lesson #23). Composes SVG with inline presentation attributes per text element (resvg <style> blocks are unreliable; see lesson #24). Failure on zero cards produced — would silently downgrade every spec's social-share preview to the splash default.
- 6
Per-spec PDF generation
abort-deployscripts/generate-pdfs.mjsProduces: 15 PDFs at dist.new/specs/<slug>.pdf, each with populated outline tree, tagged-PDF mode for accessibility, universal TOC with per-entry-correct page numbers, and pdf-lib metadata (Title, Author, Subject, Keywords, CreationDate, ModificationDate).
Starts a transient HTTP server on 127.0.0.1:<ephemeral> over dist.new/, launches Playwright Chromium, navigates each spec page through the proxy origin to avoid file:// resolution failures on fonts and stylesheets. Cross-reference hyperlinks are rewritten from the navigation origin back to https://axilog.io before page.pdf() — without rewriting, every cross-ref in the PDF would embed the build-time loopback URL (operational lesson #34). TOC convergence is per-entry-equal via converge-loop algorithm; non-convergence within MAX_TOC_ITER=3 is a hard failure (shipping a wrong-page-number TOC is worse than no TOC).
- 7
Pagefind static search index
abort-deploynpx pagefind --site ./dist.newProduces: WASM-indexed search payload under dist.new/pagefind/, including the index files, pagefind-ui.js, pagefind-ui.css, and the per-page chunks the browser fetches lazily.
Runs after the PDF step so the corpus is final. Pagefind walks dist.new/, indexes the rendered HTML (post-Astro, post-compress), and writes the search payload alongside the rest of the site. The /search/ route consumes pagefind-ui.js at runtime; the index itself is fetched on first query. Failure aborts the build because shipping /search/ without a Pagefind index would mean a working search box returning zero results — worse than no search box.
- 8
Permission set
abort-deploysudo chown -R www-data:www-data dist.newProduces: dist.new/ owned by www-data:www-data, the nginx user. Required for the production server to read the swapped directory after the atomic swap.
Runs after every output-producing step so no late-arriving file slips past the chown. The atomic swap step that follows performs a filesystem-level rename, preserving ownership.
- 9
Atomic swap
rollbacksudo mv dist dist.old && sudo mv dist.new distProduces: Production dist/ now contains the new build; the previous build is preserved at dist.old/ for rollback.
Filesystem-level rename. Single inode operation per `mv` call; no partial state visible to nginx. The rollback step on validation failure reverses the second `mv` first (preserving the new build at dist.failed/ for inspection), then restores dist.old/ as the live dist/.
- 10
Live-site SEO validator
rollbackscripts/seo_validator.py --domain axilog.io --nginx-config /etc/nginx/sites-available/axilog.ioProduces: Verified live HTTP responses for every sitemap-listed URL, redirect-rule conformance, image-asset path validation, and CSP header presence. The validator runs against the newly-swapped dist/ — a post-deploy verification step, not a pre-build check.
Both --domain and --nginx-config flags are required: the validator is shared across multiple vhosts and would mis-scope sections 20 and 21 (image redirects, redirect set verification) without explicit per-site arguments. Failure rolls back to dist.old/ via the script's explicit rollback branch, keeping the failed build at dist.failed/ for inspection. (Operational lesson #7.)
- 11
IndexNow change notification
informationalscripts/notify_indexnow.pyProduces: Per-changed-URL notifications submitted to the IndexNow protocol endpoints (Bing, Yandex, others) so the new content is crawled promptly.
Compares the new build's sitemap lastmod fields against dist.old/'s sitemap (preserved through the swap). URLs with newer lastmod are submitted as a single batch request. Failures are non-fatal — IndexNow is an acceleration, not a correctness path; search engines will discover the new content via normal crawl regardless.
- 12
Old-build cleanup
informationalsudo rm -rf dist.oldProduces: dist.old/ removed. The rollback window for this deploy closes at this point.
Runs in the background (`&` suffix). The previous build's footprint is freed; if a defect is detected after this step, recovery is by re-deploy of the previous commit from git, not by directory restoration.
- 13
Git push to GitHub
informationalgit pushProduces: Public commit on the SpeyTech/axilog.io repository recording the build state and any content changes from this deploy.
Records the deploy in the public history. A failed push (network, auth, divergent remote) does not undo the deploy — the live site is already updated. Recovery is to investigate and push manually.
Artefact lineage
The orthogonal cut: for every output the site publishes, the source it derives from, the pipeline step that produces it, and the audit-stable property that holds for the artefact.
| Artefact | Source of truth | Produced by | Audit property |
|---|---|---|---|
HTML spec page (/specs/<slug>/) | src/content/specs/<slug>.md (frontmatter + body) | Astro static build | Reproducible from source + dependency set. Identical input → identical output. |
Per-spec PDF (/specs/<slug>.pdf) | Rendered /specs/<slug>/ HTML, navigated via build-time proxy | Playwright Chromium + pdf-lib | Same content as the HTML route, archival print form. /CreationDate from spec frontmatter, /ModificationDate from build time. |
Per-spec OG card (/og/specs/<slug>.png) | Spec frontmatter (id, title, layer, dclass) | resvg-js + brand woff2 fonts via wawoff2 → TTF cache | Status pill omitted — status churns over a spec's lifetime, omission keeps the cached card valid as long as id and title are stable. |
Per-spec changelog (/specs/<slug>/changelog/) | src/content/specs/<slug>.md (frontmatter `changelog` array) | Astro static build (dynamic route) | Frontmatter is canonical. Build emits a warning when git mtime exceeds the newest changelog entry by more than one day (drift check). |
Compliance index (/compliance/, /compliance/<slug>/) | src/lib/compliance-taxonomy.ts + frontmatter `compliance` fields | Astro static build (curated dynamic routes) | Per-standard pages emit only for standards declared by at least one spec. Build-time linter refuses unresolved values (operational lesson #26). |
References graph (/references/) | frontmatter `dependencies` arrays across the spec corpus | Astro static build | Both directions of the graph are derivable from the same frontmatter data; the table transposes the M3-polish dependency declarations. |
llms.txt + llms-full.txt | specs, manifesto, insights, roadmap collections | Astro static endpoints (src/pages/llms*.txt.ts) | Regenerated every build from the live collection state. Static placeholders are forbidden in /public/ — would shadow the dynamic route silently (operational lesson #17). |
sitemap (/sitemap-index.xml, /sitemap.txt) | spec, insight, compliance collections + curated static-route list | Astro sitemap integration + src/pages/sitemap.txt.ts | Per-page lastmod from frontmatter (`lastUpdated` > `datePublished` > `date`). PDFs and OG cards excluded — surface artefacts, not navigation destinations. |
JSON-LD blocks (Organization, BreadcrumbList, TechArticle, ScholarlyArticle, AboutPage, ItemList, SearchAction) | frontmatter + route data | src/lib/schemas.ts + per-route inline construction | Emitted in <head> or inline at the top of <body> on every relevant route. Dates use full ISO 8601 with timezone (operational lesson #15); `about` typed as Thing not SoftwareApplication (lesson #14). |
Pagefind search index (/pagefind/*) | dist.new/ rendered HTML (post-build, pre-swap) | Pagefind CLI | WASM-indexed at build time, fetched lazily by the browser. The /search/ route consumes the index; the route itself is excluded from indexing. |
Compliance chain
Every compliance claim in any spec's frontmatter resolves to a canonical entry in src/lib/compliance-taxonomy.ts. The taxonomy is the audit-stable identifier; the per-spec declaration is the audit evidence. The chain that takes a frontmatter declaration to a published claim:
- 1. Spec author declares compliance in frontmatter: e.g. `compliance: [DO-178C, IEC 62304]`.
- 2. Build-time linter (scripts/compliance_lint.js) verifies every value resolves to a canonical entry in src/lib/compliance-taxonomy.ts — by id, displayName, or alias. Build aborts on any unresolved value.
- 3. SpecHeader renders the resolved displayName as a link to /compliance/<slug>/, using the canonical taxonomy's slug.
- 4. TechArticle JSON-LD emits each as a DefinedTerm with inDefinedTermSet pointing at the on-site /compliance/<slug>/ page.
- 5. /compliance/ index emits a card per declared standard; /compliance/<slug>/ lists every declaring spec, layer-grouped.
- 6. llms.txt emits a Compliance section enumerating each standard with the count and IDs of declaring specs; llms-full.txt emits a `Compliance:` line per spec.
The shipped surface: see /compliance/ for the standards declared across the corpus today, and any specific spec's meta-list for the per-spec declarations.
Cryptographic chain
This section is honest about what is and is not anchored cryptographically in the published form. Aspirational claims here would damage the project's reputation more than their absence does.
- Git commit history shipped
Every source change to the spec corpus is committed to the SpeyTech/axilog.io repository. Commits are signed where the maintainer has signing keys configured for the relevant remote.
- PDF /CreationDate and /ModificationDate metadata shipped
/CreationDate is taken from the spec's frontmatter date (the document's effective date); /ModificationDate is the build time at which the PDF was rendered. Both written by pdf-lib at PDF assembly time. Verifiable with any PDF reader's metadata inspector.
- PDF cryptographic signing (PAdES/PKCS#7) planned
Tracked as roadmap item pdf-archival-hardening. The signing infrastructure (signing key generation, key custody, certificate chain) is not yet established; published PDFs are reproducible from source but not cryptographically anchored in the published form. The honest claim today is "PDF integrity is verifiable by re-rendering against the corresponding commit".
- Substrate audit chain (libaxilog ledger) shipped
The Axioma substrate itself maintains a SHA-256-rooted audit ledger across L6 (SRS-001). The site documents this; the verification of any specific run's ledger is via the substrate, not via the site.
Reproducibility
The site build is deterministic in the engineering sense: the same source, the same dependency set, and the same build.sh invocation produce byte-identical output. The only inputs that vary between builds at the same commit are:
- The build time. Surfaced in PDF
/ModificationDate, the per-PDF attribution footer, and the sitemap's build-time-fallback<lastmod>for aggregated views. - Per-page
<lastmod>in the sitemap — derived from each artefact's source frontmatter, so the value at any given commit is also deterministic.
To reproduce a published spec's HTML, clone the repository at the commit identified by the spec page's blame, run npm ci against the lock-file at that commit, and run build.sh in an equivalent environment. The resulting dist/specs/<slug>/index.html should match the live page modulo the build-time fields named above.