mkdocs.yml
Rama master · réplica de lectura del repositorio original.
# SPDX-License-Identifier: FSL-1.1-Apache-2.0
site_name: AppRafter
site_description: Opinionated, vertically-integrated PaaS on Kubernetes
# ADR 0057 decision 8: the docs are their own deployable on their own
# hostname. A path on the landing origin (`apprafter.dev/docs`) was
# considered and REJECTED there — it couples the docs release cadence
# to the landing's and shares one cache and one rollback unit between
# two artefacts with unrelated change rates. This value is not
# cosmetic: `docs/hooks/llm_export.py` bakes it into every absolute URL
# in llms.txt, llms-full.txt and the per-page markdown twins, and
# mkdocs bakes it into sitemap.xml, so the wrong value here publishes
# the rejected design in four artefacts.
site_url: https://docs.apprafter.dev/
copyright: >
© 2026 AppRafter Authors. Documentation licensed CC-BY-4.0;
code samples Apache-2.0. Platform core FSL-1.1-Apache-2.0.
docs_dir: docs
repo_url: https://github.com/apprafter/apprafter
edit_uri: edit/master/docs/
# Internal artefacts that live under docs/ but are NOT part of the
# published site: the release-note working file (also parsed by
# release-cli.yml — excluded from the SITE, never moved), the plan
# ledger, RSS measurements, and the gitignored superpowers tree.
#
# A markdown link INTO one of these paths resolves locally but 404s on
# the published site — mkdocs logs only INFO for that case, so --strict
# will not catch it. Reference these files by path in backticks, never
# as a link.
exclude_docs: |
superpowers/
changelog/history.md
measurements/
# The literate-nav fragment is nav data, not a page. literate-nav
# already keeps it out of the nav and out of search, but it still
# rendered at /reference/cli/SUMMARY/ and appeared in sitemap.xml —
# a crawlable page that is just a link list under a "do not edit"
# banner. Excluding it from the SITE does not hide it from
# literate-nav, which reads the docs tree, not the build output.
reference/cli/SUMMARY.md
# Build hooks (see `hooks:` below). They live under docs/ to sit
# beside what they act on, but they are build machinery, not
# content: without this, mkdocs copies them to the site as static
# files — verified, it published both hooks/cue_lexer.py and the
# hooks/__pycache__/ directory CPython left there. Excluding them
# from the SITE does not stop `hooks:` loading them, which reads the
# path directly, not the docs tree.
hooks/
# Pages deliberately reachable by URL but absent from nav. Anything
# NOT on this list that is missing from nav fails the build — an
# orphaned guide (node-prep.md was invisible for months) is a bug.
# Only the ADR bodies are on it: `adr/README.md` is the nav entry and
# indexes every record, so listing each one beside it would be noise.
not_in_nav: |
adr/*
# NOTE: mkdocs 1.6.1's validation levels are warn/info/ignore only —
# there is no literal "error" level. `--strict` (see the Justfile) is
# what promotes "warn" to a build failure, so "warn" here is the
# strictest real setting and is what actually enforces these checks.
# `just docs-build` passes --strict; `just docs-serve` (local preview)
# does not, so there a warn is only a printed line — the enforcement
# point is the build, not the live-reload server.
validation:
nav:
omitted_files: warn
not_found: warn
absolute_links: ignore
links:
not_found: warn
anchors: warn
unrecognized_links: warn
absolute_links: warn
theme:
name: material
# The AppRafter mark, not material's stock `material/library` glyph.
# Two files rather than one: `partials/logo.html` renders theme.logo
# through an <img>, so it cannot inherit the page's colour, and the
# header bar is the primary colour in both schemes — hence a
# fixed-light variant. The favicon is the landing's file byte for
# byte, so a browser tab cannot show two different marks for one
# product. Both sit at the docs root rather than under `assets/`:
# docs-site/Caddyfile pins `/assets/images/*` for 24h and gives
# everything outside `/assets/` a five-minute window, and a file under
# `docs/assets/` would match neither rule.
logo: apprafter-logo-header.svg
favicon: favicon.svg
# Adds one <link rel="alternate" type="text/markdown"> per page (see
# overrides/main.html), so an agent on a rendered page can reach the
# markdown twin without guessing /llms.txt exists.
custom_dir: overrides
features:
- navigation.instant
- navigation.tracking
- navigation.tabs
- navigation.sections
- navigation.indexes
- navigation.top
- search.suggest
- search.highlight
- content.code.copy
- content.tabs.link
palette:
- media: "(prefers-color-scheme: light)"
scheme: default
primary: indigo
accent: indigo
toggle:
icon: material/weather-night
name: Switch to dark mode
- media: "(prefers-color-scheme: dark)"
scheme: slate
primary: indigo
accent: indigo
toggle:
icon: material/weather-sunny
name: Switch to light mode
# Plausible, through mkdocs-material's own analytics integration point:
# partials/integrations/analytics.html includes
# partials/integrations/analytics/<provider>.html, which `overrides/`
# supplies. `provider` is not a fixed enum, so no plugin is involved.
#
# Which property it reports to is decided in that partial and nowhere
# else — the site is baked into the `pa-*.js` bundle it requests. This
# one is `docs.apprafter.dev`, separate from the landing's.
#
# Cookieless and with no cross-site identifier, so there is no consent
# banner to gate it behind, and docs-site/Caddyfile sets no CSP to widen.
# `docs/hooks/llm_export.py` also writes into `extra`, which is why this
# key is spelled out here rather than assumed absent.
extra:
analytics:
provider: plausible
plugins:
- search
# The CLI reference is generated (`just docsgen-generate`), so its
# nav is generated with it: `docs/reference/cli/SUMMARY.md` is a nav
# fragment docsgen emits alongside the pages. Listing 26 command
# pages by hand here would be a second source of truth that drifts
# the first time a command is added.
- literate-nav:
nav_file: SUMMARY.md
# Provisioned in flake.nix for exactly this and never wired up, so
# until now a rename silently 404'd the published URL — nothing in
# `just docs-check` can see a URL that used to exist, because the
# build only knows the tree it was given.
#
# An entry goes in the SAME commit that renames or deletes a page,
# keyed on the OLD source path. This map is the only record that a URL
# existed; there is nowhere else to infer it from.
#
# What it cannot do: it maps page → page, never anchor → anchor. A
# section that moves to another page leaves its `#fragment` behind.
# In-repo referrers are caught by `--strict` (`links.anchors: warn`
# above); an external deep link lands at the top of the page it named,
# and that is the accepted cost of moving a section.
- redirects:
redirect_maps: {}
# Build hooks. mkdocs loads these by PATH (not as importable modules)
# and calls the same event API a plugin gets, so a hook can do what no
# packaged plugin here can — pygments ships no CUE lexer, and nixpkgs
# has no llms.txt plugin (ADR 0057 §"Keep MkDocs-Material" records both
# as accepted costs).
#
# Hooks live under docs/ so they sit beside what they act on; that also
# puts them inside docs_dir, where mkdocs would otherwise copy them to
# the site as static files — hence the exclude_docs entry above.
hooks:
# Registers a CUE lexer and FAILS THE BUILD if the registration did
# not take. pymdownx.highlight catches a lexer-lookup failure and
# falls back to `text`, so without that self-check a broken hook is a
# green build with every ```cue fence silently unhighlighted.
- docs/hooks/cue_lexer.py
# Writes llms.txt, llms-full.txt and a markdown twin per page into
# the built site. Nothing it writes is committed: all three derive
# from committed content at build time, so they cannot drift from it.
# Listed after the lexer because it captures each page's markdown and
# a hook that edited markdown would have to run first to be seen.
- docs/hooks/llm_export.py
markdown_extensions:
- attr_list # explicit {#stable-anchor} ids — the docs cross-reference gate resolves against these
- admonition
- pymdownx.details
- pymdownx.highlight # explicit: a build hook registers a CUE pygments lexer and needs this extension present to attach to
- pymdownx.superfences:
# Structural concepts (the tier model, the bootstrap → Argo CD
# handover, the need → claim → secret → env chain, the migration
# gate) are carried as `mermaid` diagrams. Material renders these to
# `<pre class="mermaid">` and loads mermaid.js itself; the source
# fence reaches the twin/bundle unchanged (it IS the diagram).
custom_fences:
- name: mermaid
class: mermaid
format: !!python/name:pymdownx.superfences.fence_code_format
- pymdownx.snippets: # guides include REAL files instead of carrying copies
# Snippet paths are written repo-root-relative (e.g.
# "examples/applications/redis-app.cue"). `!relative $config_dir`
# anchors that root to this file's directory rather than to the
# caller's CWD — a bare `base_path: ["."]` silently depends on
# every caller cd-ing to the repo root first.
base_path: !relative $config_dir
# Traversal above the base is blocked (this is the default; pinned
# here because a repo-root base is worth being explicit about).
restrict_base_path: true
# A missing include is a hard build failure, not a literal
# "--8<--" line nobody notices.
check_paths: true
- pymdownx.tabbed:
alternate_style: true
- pymdownx.tasklist:
custom_checkbox: true
- tables
- toc:
permalink: true
nav:
- Home: index.md
- Operator Guide:
- Overview: operator-guide/index.md
# Ordered by the sequence the work happens in (plan.md 2.23e), not
# by artefact family. Each comment below says which step of that
# sequence a page is, and a page that cannot answer that question
# is in the wrong place.
#
# 1-4: stand a cluster up.
- Quickstart: operator-guide/quickstart.md
- Managing targets: operator-guide/target-store.md
# 5: firewall and domains. Both are about cluster-owned resources
# (the zones on the platform Gateway and the certificate that
# terminates TLS for them), which is operator work.
- Connect a domain: operator-guide/connect-a-domain.md
- Cloudflare Origin CA cert: operator-guide/cloudflare-origin-cert.md
# 6: backup. Moved up nine places. The page tells the reader to set
# it up before they need it, and position 15 of 19 under a heading
# called "Day 2, and getting out of trouble" is not before.
- Back up a cluster: operator-guide/backup-restore.md
- Restore from a backup: operator-guide/restore.md
- Backup retention & integrity: operator-guide/backup-maintenance.md
# 7: dependencies. Secrets comes FIRST of them, up eight places:
# `backup-restore.md` already sends the reader here, and seals a
# SealedSecret on their behalf, before they have met sealing —
# and both `postgres` and `redis` assume sealed credentials exist.
- Secrets: operator-guide/secrets.md
- Postgres: operator-guide/postgres.md
- Redis: operator-guide/redis.md
- JetStream: operator-guide/jetstream.md
- Persistent disk: operator-guide/persistent-disk.md
- Shared volumes: operator-guide/shared-volumes.md
- Shared databases: operator-guide/shared-databases.md
- Egress policy: operator-guide/egress-policy.md
# 8-9: reading a failure, then the platform's own lifecycle.
- Troubleshooting: operator-guide/troubleshooting.md
# Not a setup step: bootstrap already applies the reservations and
# the swap (ADR 0055 decision 7), so this is the retrofit for an
# older node, a node whose swap step failed, or reading the posture.
- Node preparation: operator-guide/node-prep.md
- Platform management: operator-guide/platform-management.md
- Migration plans: operator-guide/migration-plans.md
# Both machine pages sit last on purpose. The `target add` wizard
# already makes the choice, so `Choosing the machine` is the
# reference for the exceptions — a scripted target, a rejected
# SKU, a pre-default cluster — none of which a first-time operator
# is doing; and moving a machine is a rare rebuild off the
# critical path that used to interrupt the backup block.
- Choosing the machine: operator-guide/choosing-the-machine.md
- Moving to a bigger machine: operator-guide/moving-to-a-bigger-machine.md
- Recovery: operator-guide/recovery.md
- How it works:
# Mechanism, split out of the guides by ADR 0058: a guide is a
# recipe, and an explanation of what the platform does behind a
# step is a different job for a different reader. Sits after the
# two guide sections because that is the order the reader meets
# them in — run the recipe first, come here when it surprises you.
- Overview: how-it-works/index.md
- Declared Postgres dependencies: how-it-works/needs-pg.md
- Declared Redis dependencies: how-it-works/needs-redis.md
- Declared JetStream dependencies: how-it-works/needs-jetstream.md
- Declared disk dependencies: how-it-works/needs-disk.md
- Egress derived from declared dependencies: how-it-works/egress-policy.md
- The target store on disk: how-it-works/the-target-store.md
- GitOps and the CUE plugin: how-it-works/gitops-and-the-cue-cmp.md
- Node reservations and swap: how-it-works/node-reservations-and-swap.md
- How the approval gate works: how-it-works/the-approval-gate.md
- What cluster-bootstrap installs: how-it-works/what-cluster-bootstrap-installs.md
- How the platform upgrades itself: how-it-works/platform-upgrades.md
- Cross-application shared volumes: how-it-works/cross-application-shared-volumes.md
- Shared databases: how-it-works/shared-databases.md
- How a restore replays a backup: how-it-works/how-a-restore-works.md
- How retention and the integrity check work: how-it-works/backup-retention-and-checks.md
- Per-environment deploy: how-it-works/per-environment-deploy.md
- The image digest: how-it-works/the-image-digest.md
- The public route: how-it-works/the-public-route.md
- Source credentials: how-it-works/source-credentials.md
- Sealing a secret: how-it-works/sealing-a-secret.md
# Worked examples: a whole assembly rather than one mechanism.
# Both are real deployments in this repository, included from the
# manifests they describe rather than copied.
- "Worked example: the documentation site": how-it-works/deploying-the-docs-site.md
- "Worked example: the landing site and its CMS": how-it-works/deploying-the-landing-and-cms.md
- Right-sizing an application's requests: how-it-works/resources-and-autoscaling.md
- Health checks on a running application: how-it-works/health-checks.md
- Developer Guide:
- Overview: dev-guide/index.md
# Ordered as the work happens (plan.md 2.23e), which for a
# developer starts before the manifest exists.
- Quickstart: dev-guide/quickstart.md
# Steps 3-4: the image is the input the platform takes (ADR 0010),
# and the moving tag is how a new build reaches it. One page,
# because it is one story.
- From source to a running image: dev-guide/build-and-push.md
# What the platform then does with that tag — read before the
# manifest names it, which is why it is above `application-cue`.
- Image iteration: dev-guide/image-iteration.md
# Step 5, and the three things you reach for while filling it in.
- Writing Application.cue: dev-guide/application-cue.md
- Deploying more than one environment: dev-guide/environments.md
- Resources and autoscaling: dev-guide/resources-and-autoscaling.md
- Health checks: dev-guide/health-checks.md
- Secrets: dev-guide/secrets.md
# Last of the "describe your application" run: the reader has now
# met every field that can hold a change for approval, and has not
# yet been told that some of them do.
- When a change needs approval: dev-guide/when-a-change-needs-approval.md
# Steps 6-7: connecting the manifest to the cluster, and the
# credentials a private source needs to do it.
- Private repos & registries: dev-guide/private-repos-and-registries.md
# Step 10. Split out of `image-iteration` (2.23e): a reader looking
# for "undo" does not know to look under "iteration".
- Rolling back a bad deploy: dev-guide/rollback.md
- Reference:
- Overview: reference/index.md
# Expanded by literate-nav from reference/cli/SUMMARY.md, which
# docsgen writes — a new top-level command needs no edit here.
- CLI: reference/cli/
# Hand-written on purpose: only four of these variables are
# declared through clap (`#[arg(env = …)]`), so the generated
# tree cannot carry the rest — they are read by `std::env::var`
# in command code, or by a dependency (miette, owo-colors,
# tracing-subscriber).
- Environment variables: reference/environment.md
- Contributing:
- Overview: contributing/README.md
- Setup: contributing/setup.md
- Writing documentation: contributing/documentation.md
- Documentation gate: contributing/documentation-gate.md
# The file stays under operator-guide/ so its eight inbound
# references keep resolving, but it is listed here: it is the
# runbook for publishing THIS project's documentation, and two of
# its five steps edit this repository — the GHCR package visibility
# of `apprafter/docs`, and `landing/`. Neither is an operator
# action. `llm_export` derives `audience` from the nav group first,
# so the machine-readable audience follows this move for free.
- Publishing this site: operator-guide/publish-the-docs-site.md
- License headers: contributing/license-headers.md
# Which license covers which directory and how the FSL conversion
# works. It was reachable by URL but absent from the nav, which
# made it findable only by search — this is a page visitors look
# for by name.
- Licensing: license.md
- ADRs: adr/README.md
- Status: status.md