mkdocs.yml
Rama master · réplica de lectura del repositorio original.
---
# The docs site. `uv run mkdocs build --strict` renders docs/ into site/; in production
# scripts/docs/build_docs.py runs the reference generators first and builds into the hostPath the
# `docs` k3s role serves.
#
# docs_dir points at the EXISTING docs/ tree and the nav names those files in place. Moving
# them to fit a navigation shape would break every reference to docs/secret-rotation.md and
# its siblings across CLAUDE.md, the role docs and the skills.
site_name: Homelab Docs
site_description: Reference and runbooks for the daniel-box / daniel-server / daniel-pi homelab
docs_dir: docs
site_dir: site
use_directory_urls: true
theme:
name: material
features:
- navigation.top
- navigation.indexes
- search.highlight
- content.code.copy
palette:
- media: "(prefers-color-scheme: light)"
scheme: default
toggle:
icon: material/brightness-7
name: Switch to dark mode
- media: "(prefers-color-scheme: dark)"
scheme: slate
toggle:
icon: material/brightness-4
name: Switch to light mode
markdown_extensions:
- admonition
- attr_list
- md_in_html
- tables
- toc:
permalink: true
- pymdownx.details
- pymdownx.superfences
- pymdownx.highlight:
anchor_linenums: true
# `--8<-- "assets/generated/fragments/<name>.md"` splices a generated fact table into a
# hand-written page, so the prose stays prose and the tunables it sits beside are re-read
# from the tree by scripts/docs/gen_doc_fragments.py on every docs-refresh run. check_paths
# turns an include naming a fragment nobody generates into a strict-build failure; without
# it the include line renders as text and the page reads as if the table were missing.
- pymdownx.snippets:
base_path: [docs]
check_paths: true
plugins:
- search
# Resolves the `<span class="fqdn">` placeholders the reference generators emit into links,
# using the domain of the URL the page was served from, and resolves the nav's sentinel hosts
# the same way. The generators cannot bake an FQDN in: they parse the tree statically and
# `domain` is SOPS-sourced. See the file's own header.
# Table overflow on the wide generated reference tables. The file's own header carries the
# mechanism; the short version is that Material's stock table rules clip long cells instead of
# wrapping them, and the scrollbar that would reach the clipped text sits several screens down.
extra_css:
- assets/extra.css
extra_javascript:
- assets/fqdn-links.js
# A filter bar and sortable headers on the long tables. It runs in the browser because the
# filter is the reader's choice, not a fact the build knows. The file's own header says
# which tables get one; it sits after fqdn-links.js so a filter matches the resolved links.
- assets/table-filter.js
# Turns a repo path written as a code span -- `scripts/docs/service_catalog.py`,
# `docs/secret-rotation.md` -- into a link to where this site documents it. Unlike the FQDN
# links above this resolves at build time, because a path -> URL map is fully known then; the
# file's own header carries what it links, what it deliberately does not, and why it injects
# the Scripts page's row anchors itself rather than asking the generator for them.
# The leading underscore keeps it out of the Scripts reference, which enumerates scripts/**.
hooks:
- scripts/docs/_mkdocs_repo_links.py
# Appends to every hand-written page the date of its last commit and which of the repo
# files it names changed after that -- the freshness signal the generated pages get from
# their frontmatter. Computed from git at build time, so nothing is committed;
# reference/freshness.md is the same data as one ranked table.
- scripts/docs/_mkdocs_freshness.py
# Pages that are BUILT unless named here. mkdocs serves everything under docs_dir whether or
# not the nav links it, and it logs an unlinked page at INFO — so `--strict` does not catch
# one, and neither does reading the nav.
#
# `superpowers/` is 11 design plans kept on disk and deliberately gitignored (.gitignore:5-6).
# Untracked is not unpublished: the cron builds from the host's working tree, so they were
# being served from an internet-facing site. They are the only pages here with a real exposure
# delta — this repo is PUBLIC, so everything tracked is world-readable already and excluding it
# protects nothing. `adr/template.md` is a fill-in-the-blanks stub, not a decision.
exclude_docs: |
superpowers/
adr/template.md
# Snippet bodies, not pages: each is spliced into a page above by `--8<--`, and mkdocs
# would otherwise build every one as an orphan page of one table. Excluding a file from
# the build does not hide it from the snippets extension, which reads base_path from disk.
assets/generated/fragments/
# M6's per-batch status records, hand-written and spliced into m6-retire-kuma.md the same
# way. One file per batch so parallel PRs do not conflict over one status block (#6656).
target-architecture/monitoring/m6-status/
# Built and served on purpose, but deliberately absent from the nav. `archive/` is the
# historical record of executed work — other docs and CLAUDE.md link INTO it, so excluding it
# would 404 those links, while listing every superseded planning doc in the nav would bury the
# live pages. Its README is the one exception: the nav's History section names it, as the
# index a reader browses the archive from. Declaring it here is what separates "decided to
# leave unlinked" from "nobody noticed", which is the distinction the nav coverage test now
# enforces.
#
# DECIDED: docs/archive stays; size was never the concern, and a PR number is not greppable
# offline the way the linked record is. Deleting the archive in favour of PR numbers was
# re-examined on 2026-09-28 (#2808) and kept.
#
# `reference/backlog.md` is the same case for a different reason: `findings.py list` is the
# open register, this page is only its rendering, and `issue-claiming-and-fanout.md` and
# CLAUDE.md link INTO it directly — so it stays built and reachable, just off the nav.
not_in_nav: |
archive/
reference/backlog.md
# ENFORCED by scripts/tests/test_mkdocs_config.py, in both directions: every entry below must
# resolve to a file, and every docs/**.md must appear here OR be matched by exclude_docs above.
# A document absent from the nav is still built and served, but nothing links to it — which is
# indistinguishable from it not existing.
nav:
- Home: index.md
# A nav heading that is only a link, off the site to the artifacts browser. The host is a
# sentinel: `domain` is SOPS-sourced, so nothing static can name the real one, and
# assets/fqdn-links.js rewrites this href to the domain and tier the reader is already on
# — the same derivation it does for the reference pages' `<span class="fqdn">` spans.
#
# The degradation differs from those spans and is worse. A span that is never rewritten
# still reads as its placeholder text; a nav anchor is a live link or a dead one, so a
# JavaScript failure leaves this entry pointing at an unresolvable host. Accepted: the
# same failure already costs every service link on the reference pages.
- Artifacts: https://artifacts.local.invalid/
- Reference:
- Services: reference/services.md
- Hosts: reference/hosts.md
- Secrets: reference/secrets.md
- Scheduled jobs: reference/crons.md
- State of the lab: reference/state.md
- Networking: reference/networking.md
- Scripts: reference/scripts.md
- Topology: reference/topology.md
- Doc freshness: reference/freshness.md
- Decision markers: reference/decisions.md
# template.md is deliberately absent — it is a form, not a document. The nav test only
# checks docs/*.md, not subdirectories, so leaving it out costs nothing.
- Decisions:
- Index: adr/index.md
- "ADR-0001 MkDocs site with generated reference": adr/0001-mkdocs-site-with-generated-reference.md
- "ADR-0002 k3s over Docker Compose": adr/0002-k3s-over-docker-compose-for-the-cluster-nodes.md
- "ADR-0003 SOPS with age for secrets": adr/0003-sops-with-age-for-secrets-at-rest.md
- "ADR-0004 Authelia as the SSO layer": adr/0004-authelia-is-the-single-sign-on-layer.md
- "ADR-0005 Traefik as the edge": adr/0005-traefik-is-the-edge-with-ingressroute-crds.md
- "ADR-0006 Longhorn for cluster storage": adr/0006-longhorn-for-cluster-storage.md
- "ADR-0007 Backup tiering: R2 daily, B2 weekly": adr/0007-backup-tiering-r2-daily-b2-weekly.md
- "ADR-0008 16 MiB Longhorn blocks": adr/0008-16-mib-longhorn-blocks.md
- "ADR-0009 NetworkPolicy default-deny": adr/0009-networkpolicy-default-deny-ingress.md
- "ADR-0010 Pull-based GitOps over Argo and Flux": adr/0010-pull-based-gitops-over-argo-and-flux.md
- "ADR-0011 One lock serialises every deploy path": adr/0011-one-lock-serialises-every-deploy-path.md
- "ADR-0012 Zero-downtime rollout gates": adr/0012-zero-downtime-deploys-gate-on-rollout-and-restarts.md
- "ADR-0013 daniel-pi stays on Docker": adr/0013-daniel-pi-stays-on-docker.md
- "ADR-0014 Kopia retired, Longhorn owns B2": adr/0014-kopia-retired-longhorn-owns-the-b2-credentials.md
- "ADR-0015 D2 for hand-authored diagrams": adr/0015-d2-for-hand-authored-diagrams.md
- "ADR-0016 Code scanning stays on default setup": adr/0016-code-scanning-stays-on-default-setup.md
- "ADR-0017 The tree lock guards the tree": adr/0017-the-tree-lock-guards-the-tree-not-the-cluster.md
- "ADR-0018 The repository stays public, CI stays hosted": adr/0018-the-repository-stays-public-and-ci-stays-hosted.md
- "ADR-0019 Fact support stops at the repo store": adr/0019-fact-support-stops-at-the-repo-store.md
- "ADR-0020 Flux applies the cluster from a promoted artifact": adr/0020-flux-applies-the-cluster-from-a-promoted-artifact.md
- "ADR-0021 The host-package builder trusts master plus green CI": adr/0021-the-host-package-builder-trusts-master-plus-green-ci.md
- "ADR-0022 The cluster decrypts its own file with its own key": adr/0022-the-cluster-decrypts-its-own-file-with-its-own-key.md
- "ADR-0023 Fact support is a diff notice": adr/0023-fact-support-is-a-diff-notice.md
- "ADR-0024 Code scanning is off": adr/0024-code-scanning-is-off.md
- "ADR-0025 SUC upgrades k3s, and homelab-k3s-restart restarts it": adr/0025-suc-upgrades-k3s-and-homelab-k3s-restart-restarts-it.md
- "ADR-0026 One alert plane on Prometheus and Alertmanager; Uptime Kuma retired": adr/0026-one-alert-plane-on-prometheus-and-alertmanager.md
- "ADR-0027 Fan-out batches stay on host cgroups": adr/0027-fanout-batches-stay-on-host-cgroups.md
- "ADR-0028 The cluster render supports a closed Jinja subset": adr/0028-the-renderer-supports-a-closed-jinja-subset.md
# The long-form documents three of the records above compress. They sit under Decisions
# rather than in a section of their own: each one is the working-out behind a single
# ADR, and a reader who wants that depth arrives from the record, not from a
# top-level heading.
- Background:
- "Longhorn backup tiering (ADR-0007)": longhorn-backup-tiering.md
- "NetworkPolicy default-deny (ADR-0009)": networkpolicy-default-deny.md
# The per-slice record. It is
# the answers each deploy settled by observation, and slice 5's four decisions govern
# how the baseline selects pods — live content, not archive material.
- "NetworkPolicy — what each slice settled": networkpolicy-slice-answers.md
- "Argo and Flux evaluation (ADR-0010)": archive/gitops-argo-flux-evaluation.md
# The measurement #3734 needs before initial_setup.yml loops over a setup_roles list.
- "Looped include_role measurement (#3734)": setup-role-loop-measurement.md
# Six groups, ordered day-to-day first and recovery last. An upgrade is both a driving and
# a recovery procedure, so the upgrades sit with recovery, where a reader looking for "how do
# I get the cluster back" finds them. A group is a nav heading only: every page keeps its
# path, so moving one between groups breaks no link.
- Operations:
- Deploy and land:
- Deploying by hand: deploying.md
# The long form behind the `land-after-merge` skill. The skill carries the invocation
# and the verdict decoder; this page carries the measurement behind each rule, which
# keeps the skill short.
- Landing a merged PR: landing.md
- The GitOps pipeline: gitops-pipeline.md
- Host packages: host-packages.md
- The Flux deploy plane: flux-deploy-plane.md
- The homelab-render renderer: homelab-render.md
- Secret rotation: secret-rotation.md
- Monitoring and alerting:
- Healthchecks.io deadman: healthchecks-io-deadman.md
- Uptime Robot monitors: uptime-robot-monitors.md
# internals holds the module table, prerequisites and gate sets; the check record
# holds one entry per live check. check.py and cli.py cite the internals path.
- monitor-bridge internals: monitor-bridge-internals.md
- monitor-bridge check record: monitor-bridge-checks.md
- uptime-kuma and AutoKuma record: uptime-kuma-autokuma-record.md
- observability record: observability-dashboards.md
- autofix-bridge actuators: autofix-bridge-actuators.md
- Access and host security:
- WireGuard private access: wireguard-private-homelab-access.md
- Security tools: security-tools.md
- Claude tooling:
- Claude tooling reference: claude-tooling.md
- Claude shell permissions: claude-shell-permissions.md
- Claude as its own UNIX user: claude-agent-user.md
- claude-rc caps: claude-code-rc-caps.md
- Claude memory store copy: claude-memory-sync.md
- Alert-triggered diagnosis (no-go): alert-triggered-diagnosis.md
- Issue claiming and fan-out: issue-claiming-and-fanout.md
- Failure classes: failure-classes.md
- Python code organization: python-code-organization.md
# Each page below takes the working-out of one role CLAUDE.md: drill records, measured
# traps, board provenance and design records. The role doc keeps the operating rules,
# within the inject hook's character budget, and a session reads the page only when it
# edits that code.
- Role records:
- Edge:
- Traefik record: traefik-plugins-and-startup.md
- CrowdSec record: crowdsec-waf-record.md
- Authelia sessions record: authelia-sessions-and-crowdsec-init.md
- Pi-hole DNS continuity record: pihole-dns-continuity-record.md
- Hosts:
- Host baseline record: host-baseline-record.md
- Pi host tuning record: pi-host-tuning-record.md
- k3s node-plane crons and incidents: k3s-node-plane-crons-and-incidents.md
- hypervisor libvirt internals: hypervisor-libvirt-internals.md
- Docker engine pins and runtime: docker-engine-pins-and-runtime.md
- Storage:
- volume-snapshot drill record: volume-snapshot-drills.md
- volume-revert drill and sizing: volume-revert-drill-and-sizing.md
- Renovate:
- renovate_notify internals: renovate-notify-internals.md
- renovate_agent bounds and digest: renovate-agent-bounds-and-digest.md
- Services:
- headlamp plugins and OIDC record: headlamp-oidc-and-plugins.md
- homepage widgets and layout: homepage-widgets-and-layout.md
- Jellyfin plugins: jellyfin-plugins.md
- Jellyfin font attachment latency: jellyfin-attachment-latency.md
- qbittorrent VPN and prefs: qbittorrent-vpn-and-prefs.md
- n8n community node packages: n8n-community-packages.md
- game-stats internals: game-stats-internals.md
- Valheim modding: valheim-modding.md
- Email to RSS: email-to-rss.md
- artifacts star list: artifacts-star-list.md
- Recovery and upgrades:
- k3s control-plane upgrade: k3s-upgrade.md
- Longhorn upgrade: longhorn-upgrade.md
- Ubuntu 26.04 upgrade plan: ubuntu-26.04-upgrade-plan.md
- Longhorn disaster recovery: longhorn-disaster-recovery.md
- k3s etcd restore: k3s-etcd-restore.md
- Registry push store collection: registry-push-store-gc.md
# The last stop on the recovery ladder — total loss of daniel-box or the author's own
# access. It is the entry point that links to every runbook above rather than one more
# subsystem-specific procedure, so it sits after all of them.
- Break glass: break-glass.md
# The planned migration to Flux, one alert plane, per-host packages and generated facts.
# Every page is a plan with Status: PLANNED, not a description of what runs. A slice moves
# to archive/ once it ships, the way the k3s migration's slices did.
- Target architecture:
- Design: target-architecture/design.md
- Contracts: target-architecture/contracts.md
- Renderer:
- R1 Shadow render: target-architecture/render/r1-shadow-render.md
- R2 Secret placeholders: target-architecture/render/r2-secret-placeholders.md
- R3 Remove live reads: target-architecture/render/r3-remove-live-reads.md
- R4 Delete the k8s Ansible path: target-architecture/render/r4-delete-k8s-ansible-path.md
- Cluster deploy plane:
- D1 Flux installed: target-architecture/deploy/d1-flux-installed.md
- D2 First leaf: target-architecture/deploy/d2-first-leaf.md
- D3 Secrets: target-architecture/deploy/d3-secrets.md
- D4 Ordering: target-architecture/deploy/d4-ordering.md
- D5 Bulk move: target-architecture/deploy/d5-bulk-move.md
- D6 Delete the old path: target-architecture/deploy/d6-delete-old-path.md
- D7 Superseding promotions: target-architecture/deploy/d7-superseding-promotions.md
- Monitoring:
- M1 Alertmanager: target-architecture/monitoring/m1-alertmanager.md
- M2 First bridge family: target-architecture/monitoring/m2-first-bridge-family.md
- M3 Textfile timer: target-architecture/monitoring/m3-textfile-timer.md
- M4 Blackbox ingress probes: target-architecture/monitoring/m4-blackbox-ingress.md
- M5 Loki ruler: target-architecture/monitoring/m5-loki-ruler.md
- M6 Retire Kuma: target-architecture/monitoring/m6-retire-kuma.md
- Alertmanager UI and readers: target-architecture/monitoring/alertmanager-ui-and-readers.md
- Host plane:
- H1 Cluster config to Flux: target-architecture/host/h1-cluster-config-to-flux.md
- H2 Shrink the host plane: target-architecture/host/h2-shrink-host-plane.md
- H3 Pi pilot: target-architecture/host/h3-pi-pilot.md
- H4 Secrets unit: target-architecture/host/h4-secrets-unit.md
- H5 Pi Compose: target-architecture/host/h5-pi-compose.md
- H6 k3s upgrades: target-architecture/host/h6-k3s-suc.md
- H7 Delete Ansible: target-architecture/host/h7-delete-ansible.md
- Docs guard:
- G1 Diff notice: target-architecture/docs-guard/g1-diff-notice.md
- G2 Retire the lock: target-architecture/docs-guard/g2-retire-lock.md
- G3 Freshness reviewer: target-architecture/docs-guard/g3-freshness-reviewer.md
- G4 Drop the size test: target-architecture/docs-guard/g4-drop-size-test.md
- G5 Shrink root CLAUDE.md: target-architecture/docs-guard/g5-shrink-root-claude-md.md
# Work that shipped, a spike that concluded and an incident that closed: kept as the record,
# not as instructions. Operations holds only pages that describe the lab as it runs.
# The archive index lists every archived plan and names where its live documentation went,
# and the pages under it stay off the nav (see not_in_nav above). So this heading is the
# index alone.
- History:
- Archive index: archive/README.md