From 9f6d0dd19595c00f190394933defb579df3e3b38 Mon Sep 17 00:00:00 2001 From: "fkrebs (via Claude)" Date: Thu, 21 May 2026 00:35:24 +0200 Subject: [PATCH] homelab-architecture: add Mermaid topology / auth / MCP / data plane diagrams --- services/homelab-architecture.md | 132 +++++++++++++++++++++++++++++++ 1 file changed, 132 insertions(+) diff --git a/services/homelab-architecture.md b/services/homelab-architecture.md index fe1e5a6..ea08932 100644 --- a/services/homelab-architecture.md +++ b/services/homelab-architecture.md @@ -378,3 +378,135 @@ must target *exactly these silent-failure classes*. `/mnt/pve/unas` (catches a regression of the tier-1 rule). 4. Container unhealthy/restart-looping > 5 m (the gluetun pattern). 5. local-zfs `rpool` or NFS pool > 85 %; 6. TLS cert < 14 d. + +## Topology — at a glance + +### Physical / CT layout + +```mermaid +flowchart TB + subgraph Host["Proxmox host · 192.168.1.20 · Intel Core Ultra 7 155H · 64 GiB"] + direction TB + haos["VM 100 · haos
(.60)
Home Assistant"] + shepard["CT 101 · shepard
(.49)
Shepard product stack"] + dns["CT 102 · dns
(.2)
AdGuard Home"] + backrest["CT 103 · backrest
(.3)
Backrest / restic"] + docker104["CT 104 · docker
(.40) · 16c/48G/200G
~65 containers · Intel Arc passthrough"] + nc["CT 105 · nextcloud
(.41)
Nextcloud AIO (CIFS to UNAS)"] + zoraxy["CT 108 · zoraxy
(.4)
reverse proxy + ACME"] + obs(["CT 109 · observe
(.6) · planned
Prom / Graf / Arcane / Homarr / Tinyauth"]) + id["CT 110 · id
(.5)
Pocket-ID (moved here 2026-05-20)"] + dev["CT 111 · dev
(.42) · 12c/32G/60G
Coder + Gitea + workspaces · Intel Arc"] + db(["CT 113 · db
(.?) · planned
shared Postgres + pgbouncer"]) + end + UNAS[("UNAS
192.168.1.31
NFSv3 + CIFS")] + UDM[["UDM-SE · 192.168.1.1
UniFi gateway · DNS → AdGuard"]] + Inet([Internet · ACME challenges · jottacloud · LiteLLM upstreams]) + + UDM <--> Host + UDM <--> Inet + UNAS <--> shepard + UNAS <--> backrest + UNAS <--> docker104 + UNAS <--> nc + UNAS <--> dev + + classDef planned stroke-dasharray:5 5,fill:#222,stroke:#aaa,color:#aaa + class obs,db planned +``` + +### Auth plane — Pocket-ID is the universal IdP + +Every web service that supports OIDC federates against Pocket-ID. Coder/Gitea/Vaultwarden access through Zoraxy; Zoraxy + Tinyauth fronts the non-OIDC-native ones. + +```mermaid +flowchart LR + user(["fkrebs · browser / VS Code / Claude"]) --> zx[Zoraxy
CT 108] + zx --> coder[Coder · CT 111] + zx --> gitea[Gitea · CT 111] + zx --> nc2[Nextcloud · CT 105] + zx --> immich[Immich · CT 104] + zx --> lobe[LobeChat · CT 104] + zx --> n8n[n8n · CT 104] + zx --> mcp[MCP gateway · CT 104] + zx --> vw[Vaultwarden · CT 104] + coder --> pid[(Pocket-ID
CT 110)] + gitea --> pid + nc2 --> pid + immich --> pid + lobe --> pid + n8n --> pid + mcp --> pid + vw -.->|via Tinyauth
when CT 109 lands| pid + classDef pending stroke-dasharray:4 4,color:#888 + class vw pending +``` + +### MCP plane — gateway control, child workers + +The MCP gateway on CT 104 is the OIDC-gated front; ~23 child MCP servers run on the same docker network (`ai-internal`). When CT 109 "observe" is built, the gateway moves to CT 109 and reaches CT 104's docker daemon via a socket-proxy; the children stay put. + +```mermaid +flowchart LR + client[["Claude Code / Cursor / LobeChat"]] + gw["MCP gateway
(CT 104 → CT 109 when 109 lands)
mcp.nuclide.systems"] + client -- Bearer / OIDC --> gw + subgraph "CT 104 · ai-internal docker net" + direction TB + coderm[coder-mcp] + immm[mcp-immich] + n8nm[mcp-n8n] + fetch[mcp-fetch] + time[mcp-time] + cw[mcp-crawl4ai] + seq[mcp-sequential-thinking] + ham[home-assistant-mcp] + kr[kroki-mcp] + others[...18 more] + end + gw --> coderm + gw --> immm + gw --> n8nm + gw --> fetch + gw --> time + gw --> cw + gw --> seq + gw --> ham + gw --> kr + gw --> others + coderm -.spawns/controls.-> CoderWS[(Coder workspaces
CT 111)] + ham -.bridges to.-> HAOS[(Home Assistant
VM 100)] +``` + +### Data plane — what lives where + +```mermaid +flowchart TB + subgraph Tier1["Tier-1 / latency-sensitive · local NVMe"] + pid_d[Pocket-ID sqlite · CT 110] + immich_pg[Immich Postgres · CT 104] + shared_pg[shared-postgres · CT 104] + coder_pg[coder-db · CT 111] + gitea_pg[gitea-db · CT 111] + garage["Garage S3 · CT 104
(moved off NFS 2026-05-19)"] + n8n_local["n8n data · CT 104
(reverted from NFS 2026-05-19)"] + end + subgraph Bulk["Bulk · UNAS NFSv3"] + media["Immich media, Paperless docs,
arr-stack media, audiobooks"] + coder_homes[Coder workspace homes · /mnt/pve/unas/services/coder] + gitea_data[Gitea repos · /mnt/pve/unas/services/gitea] + vw_data["Vaultwarden data
(tier-1 leak — plan to move local)"] + end + subgraph BulkCIFS["Bulk · UNAS CIFS (Nextcloud only)"] + nc_data[Nextcloud user files] + end + subgraph Backup["Off-host backup"] + jottacloud[(jottacloud
via Backrest)] + end + Tier1 -. WAL-G .-> garage + Bulk -. only media/data-dir .-> jottacloud + classDef gap fill:#5a2a2a,stroke:#c44,color:#fcc + class vw_data,jottacloud gap +``` + +The red blocks above are gaps: Vaultwarden is on NFS when it shouldn't be; off-host backup currently covers only one UNAS path, not service data. See `stacks/storage.md` for the verified state and the cleanup TODO list.