# Rack Documentation Design **Date:** 2026-06-24 **Status:** Approved ## Goal Document the 48U server rack as hand-edited Markdown that a CI pass turns into a clear visual presentation, mirroring the existing `docs/hardware/*.md` → `gen_overview.py` → generated-index pattern. The rendered output covers three views: a physical rack **elevation** (SVG), a **power** distribution graph (mermaid), and a **network cabling** graph (mermaid). Authors (operator or AI) edit frontmatter; a push regenerates the artifacts and CI fails on drift, so the published page at `docs.makerfloss.eu` is always in sync with the source. ## Context - Per-item frontmatter is already the norm: `docs/hardware/*.md` and `docs/services/*.md` carry YAML frontmatter validated by `scripts/gen_overview.py` against schemas in `scripts/overview_config.yml`. - The generator writes a grouped/sorted `index.md`; CI (`.forgejo/workflows/docs.yml`) regenerates it and runs `git diff --exit-code` to **fail on drift**. - Mermaid already renders in the Marp slide pipeline; it is **not yet enabled in MkDocs**. Enabling it is a small `mkdocs.yml` change (superfences custom fence; Material ships mermaid.js). - The rack contains devices that already have host pages (`mf01`..`mf04`). - **The physical rack is labeled U1 at the top**, descending to U48 at the bottom (non-standard; standard racks number U1 at the bottom). The elevation must match the physical labels. ## Data model Rack data is added to **host frontmatter** (decision: extend existing files rather than introduce a separate layout file). Rack-mounted items that are not hosts (PDUs, patch panels, shelves, blank panels, UPS, KVM) each get their own lightweight file in `docs/hardware/` using new `kind` values. ### Frontmatter fields ```yaml # placement (Phase 1) rack: rack01 # rack identifier; one rack today, field enables future racks rack_u: 12 # lowest U occupied (1–48) u_height: 2 # number of U occupied rack_face: front # front | rear | both | left | right (0U PDUs use left/right rails) # power (Phase 2) — on each powered device power: - { pdu: pdu01, outlet: 3 } - { pdu: pdu02, outlet: 3 } # a second entry expresses a redundant PSU feed # network (Phase 3) — on each device, one entry per cable end originating here links: - { local: eth0, peer: sw01, peer_port: 12, speed_gbps: 1 } ``` ### New `kind` enum values Extend the `hardware` enum in `overview_config.yml` with: `pdu`, `patch-panel`, `shelf`, `blank`, `ups`, `kvm` (joining the existing `server`, `laptop`, `sbc`, `switch`, `ap`, `desktop`). Non-host item files declare their own capacity where relevant: ```yaml # docs/hardware/pdu01.md hostname: pdu01 kind: pdu status: in-use rack: rack01 rack_face: left outlets: 8 ``` ```yaml # docs/hardware/pp01.md hostname: pp01 kind: patch-panel status: in-use rack: rack01 rack_u: 24 u_height: 1 rack_face: front ports: 24 ``` ## Generator: `scripts/gen_rack.py` A sibling to `gen_overview.py`, sharing its style (stdlib + PyYAML, deterministic, offline, `SchemaError` → non-zero exit). It reads every `docs/hardware/*.md` with a `rack:` field, validates the rack schema, groups by `rack`, and writes generated artifacts per rack. ### Outputs (do-not-edit, generated) - `docs/infrastructure/racks/rack01-elevation.svg` — the elevation picture. - `docs/infrastructure/racks/rack01.md` — generated page embedding, in order: 1. the elevation SVG (`![Rack rack01 elevation](rack01-elevation.svg)`), 2. a mermaid **power** graph (Phase 2), 3. a mermaid **network** graph (Phase 3), 4. an occupancy table (U-range, hostname/link, kind, face, status). Each generated file carries the same "Auto-generated … do not edit by hand" banner the existing indices use. ### Rendering - **Elevation (SVG):** two side-by-side columns, **front** and **rear**. 48 U rows numbered **U1 at the top → U48 at the bottom** to match the physical rack. Each device is a rectangle spanning its true `u_height`, filled by a per-`kind` color, labeled with hostname and U-range. Empty U slots drawn faintly. 0U side-rail items (`rack_face: left|right`) drawn as thin vertical bars beside the columns. Plain hand-written SVG strings — no external drawing library. - **Power (mermaid):** `flowchart` of `pdu → (outlet) → device`; redundant feeds appear as multiple edges into one device. - **Network (mermaid):** `flowchart` of `device[:local] -- speed --> peer[:port]` edges built from `links`. ### Validation rules (CI-enforced, fail with a clear message) 1. `rack_u` in 1–48; `rack_u + u_height − 1 ≤ 48`. 2. No two items overlap the same U range on the same `rack_face` within a rack. 3. Every `power[].pdu` resolves to a file whose `kind: pdu`; `outlet` within that PDU's `outlets`. 4. Every `links[].peer` resolves to a real file; `peer_port` within the peer's declared `ports`/port count where the peer declares one. 5. Items with `rack_face: left|right` (0U) must omit `rack_u`/`u_height`; all other rack items must include them. ## Integration | File | Change | |------|--------| | `scripts/gen_rack.py` | New generator (SVG + mermaid + table + validation) | | `scripts/overview_config.yml` | Extend `hardware` `kind` enum with new values; optional `rack` config block if reusing config-driven validation | | `docs/hardware/*.md` | Add placement (then power, then links) fields to rack occupants; add new non-host item files | | `docs/infrastructure/racks/` | New dir holding generated `rack01.md` + `rack01-elevation.svg` | | `Makefile` | `docs-index`/`docs-check` gain a `gen_rack.py` step | | `.forgejo/workflows/docs.yml` | Run `gen_rack.py`; extend the drift `git diff` guard to the generated rack artifacts | | `mkdocs.yml` | Enable mermaid (superfences custom fence) [Phase 3]; add the rack page to `nav` | ## Phasing Each phase is independently shippable as its own PR. 1. **Phase 1 — Elevation.** Placement schema + new kinds + `gen_rack.py` producing the SVG elevation and occupancy table + CI drift check + nav entry. Populate `rack01` with the current devices. 2. **Phase 2 — Power.** `power` fields + PDU files + generated mermaid power graph + validation rules 3. 3. **Phase 3 — Network.** `links` fields + patch-panel files + generated mermaid network graph + validation rule 4 + enable mermaid in `mkdocs.yml`. ## Test plan - **Unit (per phase):** run `python3 scripts/gen_rack.py`; assert it writes the expected artifacts and exits 0 on a valid fixture set. - **Validation:** craft fixtures that violate each rule (U overflow, overlap, dangling `pdu`/`peer`, bad outlet/port, 0U with U fields) and assert non-zero exit with the right message. - **Drift:** run the generator, confirm `git diff --exit-code` is clean; mutate a source file without regenerating and confirm CI's guard fails. - **Visual:** `make docs-build` (or `docs-serve`), open the rack page, confirm the SVG shows U1 at the top, devices at correct positions/faces, and (Phase 2/3) the mermaid graphs render rather than appearing as code blocks.