The VIRDX docs site (mono/apps/docs, Astro + @cloudflare/nimbus-docs) has
two tabs — Documentation (product docs, live under /documentation/*) and
Knowledge — both behind Cloudflare Access. It is served from two surfaces:
docs.virdx.dev— Cloudflare Pages, including*.pages.devpreviews.docs.fra.virdx.dev— the in-cluster image, built the same way as any other job/service image (see Executing jobs / images / runs on the cluster).
Introduced in mono#305
(feat/docs-knowledge-section).
This repo is mirrored in, not copied by hand
Content stays in this repo (virdx/knowledge), not mono, because
agents can commit here freely while mono is review-gated. At build time,
apps/docs/bin/sync-knowledge.ts pulls a checkout of this repo and normalizes
it into Nimbus’s strict docs content collection:
- frontmatter is reduced to
title/description(+sidebar.order); - the leading H1 is stripped (Nimbus renders the title from frontmatter —
which is also why every concept doc here should carry a real H1: the nine
sources/monthly-reports/*.mdfiles that opened at##with no H1 used to render with filename-derived titles like01 2026_diffsimbefore this was fixed); - relative
*.mdlinks are rewritten to extensionless site URLs.
Only wiki/ and inbox/ are published. sources/ is deliberately not
published — it’s legacy, superseded by wiki/ (see top-level index.md).
Links from wiki/ into sources/ resolve to GitHub blob URLs instead of dead
site routes.
The build is the interesting part: two repos, one image
docs is the first image built from two repos, and this knowledge repo
changes far more often than mono does, so nothing in mono’s history moves
just because knowledge content changed.
apps/docs/Containerfiledoes an unconditionalCOPY .knowledge/; the sync script reads it viaKNOWLEDGE_PATH. No git credentials go inside the image build — whoever builds supplies the checkout.- GitHub Actions (Cloudflare Pages deploy) supplies it via
actions/create-github-app-token@v2against the existing org App — no new secret needed. - For the cluster image,
apps/docs/deployments/build.shsubmits an inlinepipelineSpec(no storedPipelineneeded) that clones both repos, referencing the existinggit-clone,check-image, andbuildkit-buildTasks unchanged.git-clonehardcodes its destination to/workspace/source/repo, so the second checkout has to be an inline step — see the cluster SOP’s gotchas. check-image’s idempotency tag is derived from exactly one repo’s commit. A two-input image needs a compositecommit(<mono-sha>-<knowledge-sha>) fed into it, or a knowledge-only change produces no new tag and a naive nightly rebuild silently does nothing — see the cluster SOP’scheck-imagegotcha for the exact formula.- A prior attempt put this recipe in
infra_k8sas adocs-imagePipeline plus a nightly CronJob (infra_k8s#174, closed). Inlining it inbuild.shneeds zero cluster-side changes and keeps the recipe next to theCOPY .knowledge/that is its only reason to exist. Nightly rebuilds were dropped — the Cloudflare Pages deploy already refreshes nightly.
Gotchas
- Only
.mdxis link-linted.bun run lint:docscovers.mdxonly;bun run builddoes not catch broken internal links at all. Knowledge pages are plain.md(MDX would choke on the<placeholder>/{brace}syntax these notes are full of), so their links are never automatically checked — moving the product docs to/documentation/*broke 17 links that the build reported as green. Check a.mdpage’s links againstdist/by hand after any path-affecting change. - Every doc under
wiki/should have a real H1 (not just OKF frontmattertitle) — Nimbus strips it for rendering, but the sync tooling and any local reading both benefit from an authored title rather than a slug-derived one.
Related
- Executing jobs / images / runs on the cluster — the build systems and gotchas this SOP’s cluster-image build reuses.
- Top-level
index.md—sources/legacy status.