milestone 13: restructure docs to diataxis, tutorial, every command executed

This commit is contained in:
2026-08-02 18:02:58 +02:00
parent 35f23240e7
commit 16c9de2414
26 changed files with 3627 additions and 901 deletions
+183
View File
@@ -0,0 +1,183 @@
# Milestone 13: restructure the documentation to Diátaxis
Goal: replace the four mixed-mode documents with the four Diátaxis modes, add the
missing tutorial, and prove every instruction by running it.
## Rulings (binding)
1. **Four directories plus an index.** `docs/tutorial/`, `docs/how-to/`,
`docs/reference/`, `docs/explanation/`, and `docs/README.md` — the index names
the four modes, says who each is for, and links every page. The old
`docs/{operator,architecture,config-reference,api,performance}.md` are deleted
by the orchestrator once the new pages exist; no redirect stubs (greenfield
repo, AGENTS.md forbids compatibility leftovers).
2. **A document serves one mode.** The defect being fixed is `operator.md`, which
interleaves install procedure, CLI reference, exit-code tables and
troubleshooting in 418 lines. Reference pages state what is; how-to pages state
what to do; explanation states why; the tutorial teaches. When porting text,
move a paragraph to the mode it belongs to rather than keeping it where it was.
3. **Every command block in `tutorial/` and `how-to/` is executed verbatim by the
session that writes it**, on this host, before the session reports. A command
that cannot run here (needs a Raspberry Pi, root, a domain, hardware TLS) is
marked in the page itself as not verified on this host, with the reason. This
ruling exists because milestone 11's docs were written from source-reading
alone: the first real blocklist download aborted the process (commit 35f2324)
and a stale SPA bundle crashed the settings page — both would have surfaced if
the documented paths had been run. No session may report a page complete on the
strength of having read the code.
4. **Content is verified against `src/`, not copied from the old pages.** Defaults,
flags, exit codes, paths and route names come from the code at HEAD. Report any
discrepancy found; do not fix source in this milestone (docs-only, ruling 9).
5. **The tutorial has one guaranteed outcome.** `docs/tutorial/first-run.md` takes
a reader from a clean checkout to a running nxdns that answers a query and
blocks a domain from a real blocklist, on unprivileged ports in a scratch
directory, then stops it cleanly. It teaches by doing and states what the reader
will have at the end. No branching, no options menu, no "you may also".
6. **File map.**
- `docs/tutorial/first-run.md` — new.
- `docs/how-to/`: `install-with-systemd.md` (incl. the Raspberry Pi 5 aarch64
binary), `install-with-docker.md`, `upgrade.md`, `troubleshoot.md`,
`enable-doh-and-dot.md`, `set-up-admin-authentication.md`,
`back-up-and-restore.md`, `measure-performance.md`.
- `docs/reference/`: `configuration.md` (every section, field, default, range),
`api.md` (all 56 operations), `cli.md` (six subcommands, every flag, exit
codes), `files-and-directories.md` (data dir layout, file modes),
`performance.md` (targets + measured numbers).
- `docs/explanation/`: `architecture.md`, `configuration-model.md` (the file
seeds the database once, the database is truth, export/import round trip),
`performance-and-testing.md` (why the targets exist, why CI does not gate on
them, what hermetic tests do and do not prove).
7. **Drift guards repointed and kept honest.** `docs/docs.zig` embeds
`reference/api.md`, `reference/configuration.md`, `reference/cli.md`.
`src/docs_drift_test.zig` keeps its three guards against those paths: every
served route as a full `| METHOD | \`pattern\` |` row; every `toSettings` key
verbatim; every subcommand as its own heading. `reference/cli.md` gives each
subcommand a `## \`name\`` heading so the guard anchors on structure, not prose.
8. **Style.** Plain sentences. No banned vocabulary (leverage, seamless, robust as
filler, rule-of-three padding, "it's important to note"). No badges, no
marketing. Code blocks are copy-pasteable and use the scratch paths the
tutorial establishes, not `/var/lib/nxdns`, unless the page is about a real
install.
9. **Docs-only.** The only non-docs edits are the orchestrator's: `docs/docs.zig`,
`src/docs_drift_test.zig`, `README.md` links, and PLAN.md's repo-layout line.
No behavior changes.
## Sessions
X1, X2, X3, X4, X5 run in parallel — every session owns distinct files.
## Session X1: index + tutorial + README links
Owns `docs/README.md`, `docs/tutorial/first-run.md`, `README.md` (links section
only). Rulings 1, 5, 3 (the tutorial is executed end to end).
## Session X2: how-to, install and operations
Owns `docs/how-to/{install-with-systemd,install-with-docker,upgrade,troubleshoot}.md`.
Rulings 2, 3, 4.
## Session X3: how-to, security and measurement
Owns `docs/how-to/{enable-doh-and-dot,set-up-admin-authentication,back-up-and-restore,measure-performance}.md`.
Rulings 2, 3, 4.
## Session X4: reference
Owns `docs/reference/{configuration,api,cli,files-and-directories,performance}.md`.
Rulings 2, 4, 7 (heading convention).
## Session X5: explanation
Owns `docs/explanation/{architecture,configuration-model,performance-and-testing}.md`.
Rulings 2, 4.
## File ownership
X1 docs/README.md + docs/tutorial/* + README.md; X2 and X3 disjoint files under
docs/how-to/; X4 docs/reference/*; X5 docs/explanation/*. Orchestrator:
docs/docs.zig, src/docs_drift_test.zig, PLAN.md, deletion of the old pages, spec.
## Delivered
Seventeen pages: `docs/README.md`, one tutorial, eight how-to guides, five
reference pages, three explanation pages. The five old documents are deleted.
The orchestrator repointed `docs/docs.zig` at `reference/{api,configuration,cli}.md`
and `src/docs_drift_test.zig` at the same three, moved the subcommand anchor from
`### \`name` to `## \`name`, fixed the README Quickstart link that pointed at the
deleted `operator.md`, and corrected PLAN.md's repo-layout line and its §13.2
OpenAPI paragraph (which still promised a renderer and contract tests that were
never built).
All three guards were proven able to fail: deleting one API table row, one
settings key row and one `## \`export` heading each produced a named build
failure. The settings guard was strengthened during review — it searched for a
bare key, which prose and the annotated example could satisfy after a field's row
was deleted; it now anchors on `| \`key\` |`.
Ruling 3 held. The tutorial ran end to end twice, the second time after the review
fixes. DoH and DoT answered real queries, `POST /api/certs/reload` returned both
its success and its failure payload, the export/import round trip was
byte-identical, a Docker image was built and its container served DNS, and the
offline password change was proven (old password 401, new 200). What could not run
here — root-only steps, a second LAN host, a Raspberry Pi, a registry push, the
full-scale bench, `dnsperf` — is marked in the page that documents it, with the
reason.
Five review rounds: 15 findings, then 8, then 1, then 1, then clean. One round-1
finding was rejected on evidence (`troubleshoot.md` already stated the verified
exit codes; the contradiction was in `reference/cli.md`).
## Discrepancies found, not fixed
Docs-only per ruling 9. These are source-side and belong to a later milestone:
1. `nxdns run` exits 1 for seed-file errors (`ParseZon`, `MissingDefaultGroup`,
`NoUpstreams`) while `check` and `import` exit 2 for the same file, because
`app.isConfigFault` lists only `NoUsableUpstreams`, `BadBindAddress`,
`BadRateLimit`, `BadCertificate`.
2. `nxdns check` prints `OK: no problems found` and exits 0 after emitting WARN
lines.
3. `nxdns check` never parses a certificate or tests that the key matches it — it
checks path readability and key mode only. A green `check` is followed by
`run` exiting 2 with `BadCertificate` on a mismatched pair.
4. `check --config <missing file>` exits 1; the implicit path prints `nothing to
check` and exits 2.
5. A blocklist source attached to no group is silently inert, and `validate.zig`
has no diagnostic for it.
6. `nxdns check` says it validates without writing, but the `config.db` branch
chmods, enables WAL and runs migrations.
7. `pruneOrphans` matches only `.list`/`.wild`, so an orphaned `.raw.tmp` is never
swept.
8. `isEmpty` counts auto-materialised client rows, so a server that has answered
one query ignores a seed file placed afterwards.
9. PLAN §18 and the docs say 10 MB / 15 MB; CI asserts 10 MiB / 15 MiB.
## Acceptance (milestone complete)
- [x] The five old `docs/*.md` files are gone; the four directories and the index
exist with every page in ruling 6.
- [x] Every command in `tutorial/` and `how-to/` was executed on this host, or is
marked in-page as unverified with a reason.
- [x] `zig build test` passes with the three drift guards pointing at the new
reference pages: 16/16 steps, 1175 pass, 113 skip (integration-gated), 0 failed.
- [x] `zig build test -Dintegration` passes: 1284 pass, 4 skip (the live-network
TLS tests excluded by milestone-1 design), 0 failed. No src/ behavior change.
- [x] No page mixes modes: no procedure in `reference/`, no field table in any
`how-to/` or `tutorial/` page.
## Anti-requirements
- No new documentation subjects (no metrics-families reference, no security-model
page) — this milestone moves and splits existing content plus the tutorial.
- No redirect stubs or `docs/legacy/`.
- No source fixes; report discrepancies instead.
- No doc generator, no site builder, no MkDocs.