Status: approved 2026-05-04. MkDocs Material, parallel writing, no public domain yet (configure for one-line swap-in later).
This plan lives in-tree so anyone touching docs can see the shape we're building toward. The plan is intentionally opinionated: the system is large enough that an unstructured "write whatever's missing" approach burns months and produces drift. Every decision below has a "why" attached.
- 3am operator can succeed without reading docs first — but when they do, every command, error, and runbook is one click away.
- Tier-2 plugin authors can write a working plugin from documentation alone — no source-reading required.
- Compliance auditor finds every claim verifiable — schemas, controls mapping, attestation chain.
- Every published example actually runs in CI. No bit-rotted code blocks.
- 24-month back-compat on docs version-pinning per release.
- Customer case studies / marketing pages
- i18n (DE/FR/JA) — defer to v1.0+ doc release; plan tooling so it drops in
- Video tutorials
- Blog
- Cookbook of advanced patterns — defer to v1.5
| Audience | First question | First page they hit |
|---|---|---|
| 3am operator | "How do I restore right now?" | tutorials/restore-now, runbooks/ |
| Evaluator (DBA) | "Why this not pgBackRest?" | explanation/comparison, tutorials/ |
| Steady-state operator | "How do I add a deployment / KMS / repo?" | how-to/, reference/config |
| K8s operator team | "How does this fit my CNPG cluster?" | how-to/kubernetes/ |
| Tier-2 plugin author | "Storage plugin reference contract" | reference/plugins/, tutorials/build-a-plugin |
| Compliance auditor | "Where's the audit chain?" | explanation/security, reference/audit-events |
| CI / build engineer | "How do I package this?" | how-to/packaging/, reference/build-flavours |
Four-quadrant Diátaxis split:
- Tutorials — learn by doing, fixed paths
- How-to — task-oriented recipes
- Reference — exhaustive, machine-comparable
- Explanation — the "why"
Plus operations (handbook), compliance (control mappings), and support pages. Full page tree:
docs/
├── index.md
├── tutorials/ # ~8 pages
├── how-to/ # ~40 pages
│ ├── adding/
│ ├── operating/
│ ├── kubernetes/
│ ├── air-gapped/
│ ├── packaging/
│ ├── migration/
│ └── verify/
├── reference/ # ~50 pages, half auto-generated
│ ├── cli/ # AUTO from Cobra
│ ├── api/ # AUTO from openapi.yaml
│ ├── grpc/ # AUTO from .proto
│ ├── config/ # AUTO from internal/config schema
│ ├── plugins/
│ ├── manifest-schema.md # AUTO
│ ├── audit-event-schema.md # AUTO
│ ├── output-event-schema.md # AUTO
│ ├── metric-catalog.md # AUTO from /metrics scrape
│ ├── error-codes.md # AUTO from grep
│ ├── exit-codes.md # AUTO
│ ├── kekref-schemes.md # AUTO
│ ├── storage-url-schemes.md # AUTO
│ ├── skill-schema.md # AUTO
│ ├── runbooks/ # MOVED from current docs/runbooks/
│ ├── crd/ # AUTO from api/crd/
│ ├── build-flavours.md
│ ├── filesystem-layout.md
│ └── compatibility-matrix.md # AUTO from test/matrix.yaml
├── explanation/ # ~14 pages
├── operations/ # day-2 handbook
├── compliance/ # SOC2/ISO/HIPAA/PCI/FedRAMP/GDPR
├── faq.md
├── glossary.md
├── support/
├── changelog.md # symlinked to repo CHANGELOG.md
└── release-notes/
~150 pages total. Roughly half are auto-generated from single sources of truth; the other half are hand-written content distributed across parallel writing tracks.
| Reference page | Source of truth | Tooling |
|---|---|---|
reference/cli/*.md |
Cobra command tree | cobra/doc.GenMarkdownTree |
reference/api/openapi.html |
api/openapi.yaml |
redoc-cli build |
reference/api/_index.md |
api/openapi.yaml |
small Go tool |
reference/grpc/*.md |
proto/**/*.proto |
protoc-gen-doc |
reference/config/* |
internal/config.Schema() reflection |
new cmd/docsgen Go tool |
reference/manifest-schema.md |
internal/backup/manifest.go struct tags |
reflection |
reference/audit-event-schema.md |
internal/audit/events registry |
reflection |
reference/output-event-schema.md |
internal/output/event.go Op registry |
reflection |
reference/metric-catalog.md |
live /metrics scrape during make docs-regen |
scrape + format |
reference/error-codes.md |
grep output.NewError("…") over the tree |
one-shot script |
reference/exit-codes.md |
internal/output/Exit* constants |
reflection |
reference/kekref-schemes.md |
kms.DefaultRegistry.Schemes() |
runtime emitter |
reference/storage-url-schemes.md |
storage.Schemes() |
runtime emitter |
reference/skill-schema.md |
internal/llm/skills.SchemaV1 |
reflection |
reference/crd/*.md |
api/crd/*.yaml |
crd-ref-docs |
reference/compatibility-matrix.md |
test/matrix.yaml |
yq + jq → markdown |
man/man1/pg_hardstorage*.1 |
Cobra | cobra/doc.GenManTree |
changelog.md |
repo CHANGELOG.md |
symlink |
make docs-regen rebuilds everything; CI fails on
non-empty git diff after regen.
| Layer | Pick | Why |
|---|---|---|
| Static-site generator | MkDocs Material | best-in-class search, native Mermaid + admonitions, mkdocs-versioning + mkdocs-static-i18n drop-in, Python tooling already in CI |
| OpenAPI render | Redoc static HTML | single-file, no server, embeds cleanly |
| gRPC render | protoc-gen-doc Markdown | mature |
| API mock for examples | prism (stoplight) | validates docs' example bodies against spec; CI gate |
| Linkcheck | lychee | fast, parallel |
| Code-block testing | markdown-test-runner custom | # RUNNABLE blocks executed in containers |
| i18n (deferred) | mkdocs-static-i18n | drops into the same site |
| Hosting | GitHub Pages + Cloudflare later | versioned via gh-pages branch |
v1.0 ships without a public domain. The site builds
to a portable site/ directory and is publishable to
GitHub Pages (or any static host) with one config flip.
mkdocs.yml is configured with:
site_url: ""left blank by defaultrepo_urlpopulated (the GitHub repo)- All internal links relative (no absolute URLs)
- All extra-asset paths relative
Adding a domain later is one line in mkdocs.yml
(site_url: "https://docs.…") plus a DNS record + a
GitHub Pages CNAME. No content rewrite needed.
make docs-regenis clean on every PR — auto-gen can't drift.mkdocs build --strict— broken cross-link or missing nav entry fails the build.lychee docs/— every external link reachable.markdown-test-runner docs/— every# RUNNABLEcode block executes against a containerisedpg_hardstorage+ a fake repo.- OpenAPI gate — every published path has at least one example request + response. Vacuum + Spectral.
buf lint+buf breaking— proto changes can't break the v1.0 contract.- Schema parity — config keys in
reference/config/exist ininternal/config.Schema(), and vice versa. pg_hardstorage <cmd> --helpround-trip — text inreference/cli/pg_hardstorage_<cmd>.mdis byte-equal to live--helpoutput.- Spell-check —
cspellwith project allowlist.
Parallel-writer plan; effort numbers assume two writers.
Phase 1 — Foundations (1 week) — DONE-ish in this commit.
- mkdocs scaffold, IA migration, CONTRIBUTING-DOCS, Makefile targets, basic CI, CLI auto-gen wired.
Phase 2 — Tutorials (3-4 days, parallel)
- 8 tutorials, each with
# RUNNABLEcode blocks.
Phase 3 — How-to library (1 week, parallel)
- ~40 task-oriented pages. The "adding/" branch parallelises per plugin.
Phase 4 — Explanation deep-dives (4 days, parallel)
- ~14 conceptual pages. Adapt heavily from
SPEC.md.
Phase 5 — Compliance & operations (4 days)
- Control-mapping pages + operations handbook.
Phase 6 — Plugin author guide (3 days)
- Tier-1 contracts + Tier-2 walkthrough + reference
example plugin in
examples/.
Phase 7 — Polish (3 days)
- FAQ, glossary, search-tuning, screenshots, v1.0 release notes.
Total: 4-5 weeks with two parallel writers.
These are real gaps the doc audit surfaced; they are engineering work not doc work and will be tracked separately:
- OpenAPI completeness gap —
api/openapi.yamlcovers 15 paths; the SPEC promises ~25. Missing paths:/v1/deployments/{d}/backups/{id}/verify,/v1/wal/*,/v1/repos/{r}/gc,/v1/repos/{r}/usage,/v1/kms/*,/v1/audit,/v1/search,/v1/doctor. Either implement the missing routes or tighten the SPEC to match what's shipped. - gRPC service definitions —
proto/pg_hardstorage/v1/has 3 files; some services in the SPEC aren't in the protos yet. - CRD schema files —
api/crd/referenced in the plugin model section but the directory is sparse.
These don't block the doc project; we document what's there and link to issues for what's coming.
See docs/CONTRIBUTING-DOCS.md.
- Versioning policy — every minor (v1.0, v1.1) gets
its own preserved doc set, or latest-wins with archived
snapshots only at majors?
mikeplugin supports either. Default until decided: latest-wins. - Feedback widget — "this page wasn't helpful" / GitHub issue link? Default until decided: no widget, link to "How to file a bug" in footer.
- Screenshot policy — none for v1.0 (every screenshot bit-rots) is the default. TUI / status output rendered as text blocks instead.