From 8a6422bd7dc3eaaa012d2e088bb010183d250ae3 Mon Sep 17 00:00:00 2001 From: dtoro Date: Mon, 6 Jul 2026 14:35:23 +0200 Subject: [PATCH] docs: move narrative wiki under knowledge/wiki/ (phase 3) Problem: node and cross-cutting narratives lived at the repo root (containers/, vms/, infrastructure/, host .md files), interleaved with the machine-readable substrate. Change: - Move containers/ -> knowledge/wiki/containers/, vms/ -> knowledge/wiki/vms/, infrastructure/ -> knowledge/wiki/infrastructure/, hosts/{hubris,strong}.md -> knowledge/wiki/hosts/, infrastructure/references/ -> knowledge/sources/references/, GLOSSARY.md -> knowledge/GLOSSARY.md. - Add knowledge/{index.md,log.md,sources/index.md} scaffolding. - Rewrite all relative links repo-wide via a path-resolving mapper (inbound + outbound + between-moved-files), including .hermes/, runbooks, operations, investigations, plans, README, AGENTS. - Repoint inventory.yaml doc_page fields and regenerate hosts/*.yaml (which embed doc_page); update oikos/gen-topology.py output path, candidate doc paths, and footer links; update code-comment doc paths. Substrate untouched in place: inventory.yaml, hosts/*.yaml (regenerated, idempotent), oikos/ code, mcp/, secrets/, bin/. Verification: - Logical broken-link set identical to pre-move baseline (net 128 -> 127; the topology regen fixed one, introduced none). Remaining are pre-existing refs to destroyed/archived nodes, out of scope for this move. - gen-topology.py --check exit 0 (in sync); cards carry knowledge/wiki/ doc paths. - build_host_files.py idempotent; all inventory doc_page targets resolve. - MCP contract verified: get_page/search_docs/get_changelog resolve moved pages. Co-Authored-By: Claude Opus 4.8 --- .agents/OIKOS.md | 2 +- AGENTS.md | 13 +++--- README.md | 42 +++++++++---------- hosts/apps.yaml | 6 +-- hosts/arriman.yaml | 4 +- hosts/auth-outpost.yaml | 2 - hosts/caddy.yaml | 4 +- hosts/dns.yaml | 4 +- hosts/elementsynapse.yaml | 4 +- hosts/gitea.yaml | 4 +- hosts/grimmory.yaml | 2 - hosts/haos.yaml | 4 +- hosts/house.yaml | 2 - hosts/hubris.yaml | 4 +- hosts/jellyfin.yaml | 4 +- hosts/mule-images.yaml | 4 +- hosts/netbird-vps.yaml | 2 +- hosts/nextcloud.yaml | 4 +- hosts/nfs-export.yaml | 2 - hosts/paperless.yaml | 4 +- hosts/romm.yaml | 2 - hosts/seanime.yaml | 2 - hosts/sophia.yaml | 2 - hosts/strong.yaml | 2 - hosts/teddycloud.yaml | 4 +- hosts/trmnl.yaml | 4 +- hosts/zimaos.yaml | 4 +- inventory.yaml | 34 +++++++-------- .../2026-06-06-authentik-session-lifetime.md | 4 +- .../2026-06-06-caddyfile-truncation.md | 4 +- GLOSSARY.md => knowledge/GLOSSARY.md | 6 +-- knowledge/index.md | 14 +++++++ knowledge/log.md | 7 ++++ knowledge/sources/index.md | 9 ++++ .../cert-sync-and-traefik-config.md | 0 .../wiki/containers}/101-jellyfin.md | 4 +- .../wiki/containers}/102-nfs-export.md | 2 +- .../wiki/containers}/103-paperless.md | 0 .../wiki/containers}/104-gitea.md | 0 .../wiki/containers}/105-apps.md | 0 .../wiki/containers}/106-auth-outpost.md | 10 ++--- .../wiki/containers}/107-dns.md | 4 +- .../wiki/containers}/114-nextcloud.md | 0 .../wiki/containers}/118-elementsynapse.md | 0 .../wiki/containers}/119-sophia.md | 0 .../wiki/containers}/120-mule-images.md | 0 .../wiki/containers}/121-caddy.md | 2 +- .../wiki/containers}/122-arriman.md | 0 .../wiki/containers}/128-trmnl.md | 2 +- .../wiki/containers}/129-house.md | 2 +- .../wiki/containers}/130-grimmory.md | 0 .../wiki/containers}/131-teddycloud.md | 4 +- .../wiki/containers}/132-rclone.md | 2 +- .../wiki/containers}/133-seanime.md | 0 .../wiki/containers}/134-romm.md | 0 .../containers}/archive/123-claudio-bot.md | 0 .../archive/127-mule-photos-new.md | 0 .../wiki/containers}/index.md | 2 +- {hosts => knowledge/wiki/hosts}/hubris.md | 22 +++++----- {hosts => knowledge/wiki/hosts}/strong.md | 10 ++--- .../wiki/infrastructure}/auto-deploy.md | 6 +-- .../wiki/infrastructure}/backups.md | 10 ++--- .../wiki/infrastructure}/dns.md | 8 ++-- .../wiki/infrastructure}/homelab-context.md | 6 +-- .../wiki/infrastructure}/index.md | 6 +-- .../wiki/infrastructure}/ingress.md | 4 +- .../wiki/infrastructure}/media-permissions.md | 0 .../wiki/infrastructure}/mesh.md | 2 +- .../wiki/infrastructure}/monitoring.md | 0 .../wiki/infrastructure}/network.md | 8 ++-- .../wiki/infrastructure}/ssh-access.md | 4 +- .../wiki/infrastructure}/topology.md | 4 +- .../wiki/infrastructure}/vps-hardening.md | 0 {vms => knowledge/wiki/vms}/100-zimaos.md | 2 +- {vms => knowledge/wiki/vms}/108-haos.md | 0 {vms => knowledge/wiki/vms}/index.md | 2 +- oikos/approve.py | 2 +- oikos/cards/host-apps.md | 4 +- oikos/cards/host-arriman.md | 2 +- oikos/cards/host-auth-outpost.md | 2 +- oikos/cards/host-caddy.md | 2 +- oikos/cards/host-dns.md | 4 +- oikos/cards/host-elementsynapse.md | 2 +- oikos/cards/host-gitea.md | 2 +- oikos/cards/host-grimmory.md | 2 +- oikos/cards/host-haos.md | 2 +- oikos/cards/host-house.md | 2 +- oikos/cards/host-hubris.md | 2 +- oikos/cards/host-jellyfin.md | 2 +- oikos/cards/host-mule-images.md | 2 +- oikos/cards/host-nextcloud.md | 2 +- oikos/cards/host-nfs-export.md | 2 +- oikos/cards/host-paperless.md | 2 +- oikos/cards/host-romm.md | 2 +- oikos/cards/host-seanime.md | 2 +- oikos/cards/host-sophia.md | 2 +- oikos/cards/host-strong.md | 2 +- oikos/cards/host-teddycloud.md | 4 +- oikos/cards/host-trmnl.md | 2 +- oikos/cards/host-zimaos.md | 2 +- oikos/cards/service-arr_stack.md | 2 +- oikos/cards/service-artifacto.md | 2 +- oikos/cards/service-authentik.md | 2 +- oikos/cards/service-caddy.md | 4 +- oikos/cards/service-dns.md | 2 +- oikos/cards/service-gitea.md | 2 +- oikos/cards/service-haos.md | 2 +- oikos/cards/service-homelab_mcp.md | 2 +- oikos/cards/service-jellyfin.md | 2 +- oikos/cards/service-matrix.md | 2 +- oikos/cards/service-nextcloud.md | 2 +- oikos/cards/service-paperless.md | 2 +- oikos/cards/service-photos.md | 2 +- oikos/cards/service-proxmox_ui.md | 2 +- oikos/cards/service-teddycloud.md | 2 +- oikos/cards/service-trmnl.md | 2 +- oikos/cards/service-zimaos.md | 2 +- oikos/console/deploy/README.md | 2 +- oikos/drift.py | 2 +- oikos/gen-topology.py | 16 +++---- oikos/gen_topology_lib.py | 2 +- oikos/report.py | 2 +- oikos/scheduler.py | 2 +- operations/agent-enrollment.md | 10 ++--- operations/commands.md | 26 ++++++------ operations/hermes-agent.md | 6 +-- plans/2026-06-24-trmnl-plugins-lxc.md | 6 +-- runbooks/config-change-deploy.md | 2 +- runbooks/runbook-dpkg-interrupted.md | 4 +- 129 files changed, 249 insertions(+), 264 deletions(-) rename GLOSSARY.md => knowledge/GLOSSARY.md (87%) create mode 100644 knowledge/index.md create mode 100644 knowledge/log.md create mode 100644 knowledge/sources/index.md rename {infrastructure => knowledge/sources}/references/cert-sync-and-traefik-config.md (100%) rename {containers => knowledge/wiki/containers}/101-jellyfin.md (97%) rename {containers => knowledge/wiki/containers}/102-nfs-export.md (98%) rename {containers => knowledge/wiki/containers}/103-paperless.md (100%) rename {containers => knowledge/wiki/containers}/104-gitea.md (100%) rename {containers => knowledge/wiki/containers}/105-apps.md (100%) rename {containers => knowledge/wiki/containers}/106-auth-outpost.md (81%) rename {containers => knowledge/wiki/containers}/107-dns.md (93%) rename {containers => knowledge/wiki/containers}/114-nextcloud.md (100%) rename {containers => knowledge/wiki/containers}/118-elementsynapse.md (100%) rename {containers => knowledge/wiki/containers}/119-sophia.md (100%) rename {containers => knowledge/wiki/containers}/120-mule-images.md (100%) rename {containers => knowledge/wiki/containers}/121-caddy.md (97%) rename {containers => knowledge/wiki/containers}/122-arriman.md (100%) rename {containers => knowledge/wiki/containers}/128-trmnl.md (98%) rename {containers => knowledge/wiki/containers}/129-house.md (97%) rename {containers => knowledge/wiki/containers}/130-grimmory.md (100%) rename {containers => knowledge/wiki/containers}/131-teddycloud.md (94%) rename {containers => knowledge/wiki/containers}/132-rclone.md (99%) rename {containers => knowledge/wiki/containers}/133-seanime.md (100%) rename {containers => knowledge/wiki/containers}/134-romm.md (100%) rename {containers => knowledge/wiki/containers}/archive/123-claudio-bot.md (100%) rename {containers => knowledge/wiki/containers}/archive/127-mule-photos-new.md (100%) rename {containers => knowledge/wiki/containers}/index.md (98%) rename {hosts => knowledge/wiki/hosts}/hubris.md (93%) rename {hosts => knowledge/wiki/hosts}/strong.md (95%) rename {infrastructure => knowledge/wiki/infrastructure}/auto-deploy.md (97%) rename {infrastructure => knowledge/wiki/infrastructure}/backups.md (94%) rename {infrastructure => knowledge/wiki/infrastructure}/dns.md (89%) rename {infrastructure => knowledge/wiki/infrastructure}/homelab-context.md (97%) rename {infrastructure => knowledge/wiki/infrastructure}/index.md (94%) rename {infrastructure => knowledge/wiki/infrastructure}/ingress.md (90%) rename {infrastructure => knowledge/wiki/infrastructure}/media-permissions.md (100%) rename {infrastructure => knowledge/wiki/infrastructure}/mesh.md (99%) rename {infrastructure => knowledge/wiki/infrastructure}/monitoring.md (100%) rename {infrastructure => knowledge/wiki/infrastructure}/network.md (94%) rename {infrastructure => knowledge/wiki/infrastructure}/ssh-access.md (98%) rename {infrastructure => knowledge/wiki/infrastructure}/topology.md (95%) rename {infrastructure => knowledge/wiki/infrastructure}/vps-hardening.md (100%) rename {vms => knowledge/wiki/vms}/100-zimaos.md (99%) rename {vms => knowledge/wiki/vms}/108-haos.md (100%) rename {vms => knowledge/wiki/vms}/index.md (94%) diff --git a/.agents/OIKOS.md b/.agents/OIKOS.md index 715f169..fe1fa17 100644 --- a/.agents/OIKOS.md +++ b/.agents/OIKOS.md @@ -80,7 +80,7 @@ stored as `state:` in inventory (absent = active). Destroyed nodes live in the `archaeology:` section. Each transition is a runbook checklist; deprecation completes only when inbound edges reach zero. -Generated views: [infrastructure/topology.md](../infrastructure/topology.md) +Generated views: [infrastructure/topology.md](../knowledge/wiki/infrastructure/topology.md) (Mermaid, regenerated from inventory) and the live, clickable version at `oikos.hubris.network/graph` once the Console is deployed. diff --git a/AGENTS.md b/AGENTS.md index 8349188..ea72a46 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -33,10 +33,10 @@ the operator to run `homelab client add ` from an existing client. - `/opt/homelab-context/inventory.yaml` — every host, LXC, VM, and workstation with their mesh addresses, roles, and service mappings. Treat this file as authoritative; anything you read in narrative pages should agree with it. -- `/opt/homelab-context/infrastructure/mesh.md` — Tailscale → Netbird state. +- `/opt/homelab-context/knowledge/wiki/infrastructure/mesh.md` — Tailscale → Netbird state. Both meshes are accepted today; Netbird is preferred for new traffic. -- `/opt/homelab-context/infrastructure/dns.md` — split-horizon DNS via - Technitium on [dns (107)](../containers/107-dns.md). `*.hubris.network` +- `/opt/homelab-context/knowledge/wiki/infrastructure/dns.md` — split-horizon DNS via + Technitium on [dns (107)](knowledge/wiki/containers/107-dns.md). `*.hubris.network` resolves to 192.168.x.x on the LAN and to mesh addresses off-LAN. - `/opt/homelab-context/operations/commands.md` — the operator's cheatsheet for pct, caddy, DNS, and the Oikos command surface. Use these verbs when @@ -75,8 +75,9 @@ Grep is fine for browsing or when MCP is unreachable. ## 4. Wiki conventions -- Pages live under `containers/`, `hosts/`, `vms/`, `infrastructure/`, - `investigations/`, `operations/`. Cross-link liberally; orphans are bugs. +- Narrative pages live under `knowledge/wiki/{containers,hosts,vms,infrastructure}/`; + procedural pages under `investigations/`, `operations/`, `runbooks/`, `plans/`. + Cross-link liberally; orphans are bugs. - Every page ends with a `## Changelog` section, entries in reverse-chrono order: @@ -105,7 +106,7 @@ Grep is fine for browsing or when MCP is unreachable. a valid `--approval-id` from `homelab approval request` — see OIKOS.md. For ad-hoc work, SSH and edit directly — but commit changes that touch tracked configs (caddy, gitea custom, artifacto, mule-image, etc.; see - `infrastructure/auto-deploy.md`). + `knowledge/wiki/infrastructure/auto-deploy.md`). - **Wiki updates**: same-session rule applies to any meaningful state change this client makes. diff --git a/README.md b/README.md index 1e92e05..6515cc0 100644 --- a/README.md +++ b/README.md @@ -12,23 +12,23 @@ Living documentation for the **hubris** Proxmox homelab. Every node, every cross - [Cross-cutting infrastructure](#cross-cutting-infrastructure) — DNS, ingress, mesh, storage, auth - [Investigations](#investigations) — incident timelines - [Operations](#operations) — cheatsheet, enrollment, runbooks -- [Glossary](GLOSSARY.md) — term definitions +- [Glossary](knowledge/GLOSSARY.md) — term definitions - [Conventions](#conventions) — wiki style, changelog hygiene, same-session update rule ## Map ### Proxmox hosts -- [`hubris`](hosts/hubris.md) — PVE node, GMKtec NucBox M6 Ultra, `192.168.8.77` — primary host, runs [8 LXCs](containers/index.md) + 2 VMs -- [`strong`](hosts/strong.md) — PVE node (cluster hostname `strong`), `192.168.178.181` — 2nd member of `Homelab` cluster. Hosts [7 LXCs](containers/index.md) migrated from hubris (Phase 1+2, 2026-07-05) +- [`hubris`](knowledge/wiki/hosts/hubris.md) — PVE node, GMKtec NucBox M6 Ultra, `192.168.8.77` — primary host, runs [8 LXCs](knowledge/wiki/containers/index.md) + 2 VMs +- [`strong`](knowledge/wiki/hosts/strong.md) — PVE node (cluster hostname `strong`), `192.168.178.181` — 2nd member of `Homelab` cluster. Hosts [7 LXCs](knowledge/wiki/containers/index.md) migrated from hubris (Phase 1+2, 2026-07-05) ### VMs -- [100 — `zimaos`](vms/100-zimaos.md) — ZimaOS 1.6.1, NAS frontend (evaluation) -- [108 — `haos-16.3`](vms/108-haos.md) — Home Assistant OS -- See [vms/index.md](vms/index.md) for the full table +- [100 — `zimaos`](knowledge/wiki/vms/100-zimaos.md) — ZimaOS 1.6.1, NAS frontend (evaluation) +- [108 — `haos-16.3`](knowledge/wiki/vms/108-haos.md) — Home Assistant OS +- See [vms/index.md](knowledge/wiki/vms/index.md) for the full table ### LXC containers See the full table with IPs, hosts, mounts, and status in -[`containers/index.md`](containers/index.md). Quick summary: +[`containers/index.md`](knowledge/wiki/containers/index.md). Quick summary: - **hubris** (10 active): 102 nfs-export, 103 paperless, 104 gitea, 105 apps, 114 nextcloud, 119 sophia, 120 mule-images, 121 caddy, 124 authentik (outpost), @@ -38,21 +38,21 @@ See the full table with IPs, hosts, mounts, and status in - **Destroyed (archaeology)**: 100 arr, 106 flaresolverr, 107 marimo, 109 syncthing, 110 photoprism, 111 karakeep, 112 immich, 115 reticulum, 123 claudio-bot, 125 seafile, 126 plato, 127 mule-photos-new — see - [containers/index.md](containers/index.md#recently-destroyed-kept-for-archaeology) + [containers/index.md](knowledge/wiki/containers/index.md#recently-destroyed-kept-for-archaeology) ### Cross-cutting infrastructure -- [Infrastructure index](infrastructure/index.md) — map of every cross-cutting system -- [Glossary](GLOSSARY.md) — term definitions -- [DNS — split-horizon](infrastructure/dns.md) -- [Ingress — Caddy + VPS traefik](infrastructure/ingress.md) -- [Mesh — Tailscale → Netbird migration](infrastructure/mesh.md) -- [Monitoring — Hermes health watchdog](infrastructure/monitoring.md) -- [Media permissions — `media` GID 10000](infrastructure/media-permissions.md) -- [SSH access](infrastructure/ssh-access.md) -- [Backups — rclone → Proton Drive (LXC 132); restic-on-USB deprecated](infrastructure/backups.md) -- [Auto-deploy — gitea-webhook pipelines](infrastructure/auto-deploy.md) -- [VPS hardening — IONOS / netbird control plane](infrastructure/vps-hardening.md) -- [Homelab context distribution](infrastructure/homelab-context.md) — cross-client `/opt/homelab-context` + MCP + secrets-issuance +- [Infrastructure index](knowledge/wiki/infrastructure/index.md) — map of every cross-cutting system +- [Glossary](knowledge/GLOSSARY.md) — term definitions +- [DNS — split-horizon](knowledge/wiki/infrastructure/dns.md) +- [Ingress — Caddy + VPS traefik](knowledge/wiki/infrastructure/ingress.md) +- [Mesh — Tailscale → Netbird migration](knowledge/wiki/infrastructure/mesh.md) +- [Monitoring — Hermes health watchdog](knowledge/wiki/infrastructure/monitoring.md) +- [Media permissions — `media` GID 10000](knowledge/wiki/infrastructure/media-permissions.md) +- [SSH access](knowledge/wiki/infrastructure/ssh-access.md) +- [Backups — rclone → Proton Drive (LXC 132); restic-on-USB deprecated](knowledge/wiki/infrastructure/backups.md) +- [Auto-deploy — gitea-webhook pipelines](knowledge/wiki/infrastructure/auto-deploy.md) +- [VPS hardening — IONOS / netbird control plane](knowledge/wiki/infrastructure/vps-hardening.md) +- [Homelab context distribution](knowledge/wiki/infrastructure/homelab-context.md) — cross-client `/opt/homelab-context` + MCP + secrets-issuance ### Investigations Time-stamped incident notes / experiments in [`investigations/index.md`](investigations/index.md). @@ -71,7 +71,7 @@ Resolved cases move to [`investigations/archive/`](investigations/archive/). ``` - **Cross-linking is mandatory.** If a page references another node or system, link to it. Treat orphans as a bug. - **Live state wins.** When something here disagrees with `pct config` / `docker inspect` / running config, fix the wiki *and* note the change in the relevant changelog. -- **Tracked configs.** A node whose config lives in a Gitea repo (Caddy, Gitea customizations, Artifacto, mule-image) is auto-deployed via webhook — see [auto-deploy](infrastructure/auto-deploy.md). Edits there must be pushed, not left local. +- **Tracked configs.** A node whose config lives in a Gitea repo (Caddy, Gitea customizations, Artifacto, mule-image) is auto-deployed via webhook — see [auto-deploy](knowledge/wiki/infrastructure/auto-deploy.md). Edits there must be pushed, not left local. - **No secrets.** This is a private repo on `git.hubris.network`, but still: paths to secret files are fine, secret values are not. ## Maintaining this wiki diff --git a/hosts/apps.yaml b/hosts/apps.yaml index ad5a2cb..48cbb8d 100644 --- a/hosts/apps.yaml +++ b/hosts/apps.yaml @@ -34,7 +34,7 @@ services_hosted: - name: artifacto backend: apps url: https://artifacto.hubris.network - doc_page: containers/105-apps.md + doc_page: knowledge/wiki/containers/105-apps.md config_repo: dtoro/Artifacto - name: homelab_mcp backend: apps @@ -42,7 +42,7 @@ services_hosted: systemd_unit: homelab-mcp public_host: mcp.hubris.network endpoint: https://mcp.hubris.network/mcp - doc_page: infrastructure/homelab-context.md + doc_page: knowledge/wiki/infrastructure/homelab-context.md config_repo: dtoro/Homelab-Docs note: MCP server. Read-only context + management. Reachable on the LAN via Caddy and from off-LAN via Netbird (192.168.8.0/24 is a network resource routed through hubris). @@ -58,7 +58,5 @@ services_hosted: note: Issues per-client age private keys. Gated at source-IP layer (mesh + LAN subnets in MESH_SUBNETS). risk_notes: "identity issuance \u2014 any change is security-sensitive; key operations are destructive-class" age_pubkey: age1duyl8mkpgu80uv934dy8q7enqjms6yvdz264hme8uryuxmvvqesq6rusq0 -see_also: -- containers/105-apps.md mcp_endpoint: https://mcp.hubris.network/mcp secrets_issuance_endpoint: https://secrets.hubris.network/issue diff --git a/hosts/arriman.yaml b/hosts/arriman.yaml index 27c0933..58e7cf3 100644 --- a/hosts/arriman.yaml +++ b/hosts/arriman.yaml @@ -29,12 +29,10 @@ services_hosted: - name: arr_stack backend: arriman note: jellyseerr / qbit / sab on docker compose - doc_page: containers/122-arriman.md + doc_page: knowledge/wiki/containers/122-arriman.md notes: - Migrated from hubris to strong 2026-07-05 (Phase 2). Library on ludo-lvm. - Contains homarr, radarr, sonarr, lidarr, sabnzbd, qbittorrent, bazarr, flaresolverr, prowlarr, jellyseerr - qBittorrent auth subnet whitelist expanded to 192.168.8.0/24 (for seanime + Caddy access) -see_also: -- containers/122-arriman.md mcp_endpoint: https://mcp.hubris.network/mcp secrets_issuance_endpoint: https://secrets.hubris.network/issue diff --git a/hosts/auth-outpost.yaml b/hosts/auth-outpost.yaml index e849205..29a0fae 100644 --- a/hosts/auth-outpost.yaml +++ b/hosts/auth-outpost.yaml @@ -16,7 +16,5 @@ mesh_globals: - tailscale notes: - Runs Authentik outpost (reverse-proxy/SSO enforcement) for protected services -see_also: -- containers/106-auth-outpost.md mcp_endpoint: https://mcp.hubris.network/mcp secrets_issuance_endpoint: https://secrets.hubris.network/issue diff --git a/hosts/caddy.yaml b/hosts/caddy.yaml index 02c0b8a..c533e36 100644 --- a/hosts/caddy.yaml +++ b/hosts/caddy.yaml @@ -24,14 +24,12 @@ services_hosted: backend: caddy role: reverse-proxy note: terminates all *.hubris.network - doc_page: containers/121-caddy.md + doc_page: knowledge/wiki/containers/121-caddy.md config_repo: dtoro/caddy-conf risk_notes: "wide blast radius \u2014 every *.hubris.network route rides on it (see oikos/policy.yaml\ \ service_overrides)" notes: - Terminates all *.hubris.network - /etc/caddy is a git checkout of dtoro/caddy-conf -see_also: -- containers/121-caddy.md mcp_endpoint: https://mcp.hubris.network/mcp secrets_issuance_endpoint: https://secrets.hubris.network/issue diff --git a/hosts/dns.yaml b/hosts/dns.yaml index 8a966d2..67bc722 100644 --- a/hosts/dns.yaml +++ b/hosts/dns.yaml @@ -20,12 +20,10 @@ services_hosted: - name: dns backend: dns note: Technitium DNS, split-horizon zone - doc_page: containers/107-dns.md + doc_page: knowledge/wiki/containers/107-dns.md risk_notes: "LAN-wide resolver \u2014 misconfig breaks name resolution for every client" notes: - Technitium DNS, split-horizon zone for *.hubris.network - Primary DNS for 192.168.8.0/24 LAN (inventory.services.dns references this) -see_also: -- containers/107-dns.md mcp_endpoint: https://mcp.hubris.network/mcp secrets_issuance_endpoint: https://secrets.hubris.network/issue diff --git a/hosts/elementsynapse.yaml b/hosts/elementsynapse.yaml index 2f2dbad..535b6fa 100644 --- a/hosts/elementsynapse.yaml +++ b/hosts/elementsynapse.yaml @@ -23,11 +23,9 @@ services_hosted: - name: matrix url: https://matrix.hubris.network backend: elementsynapse - doc_page: containers/118-elementsynapse.md + doc_page: knowledge/wiki/containers/118-elementsynapse.md risk_notes: "alert/approval channel for Oikos \u2014 outage silences agent escalation" notes: - Migrated from hubris to strong 2026-07-05 (Phase 1 of strong migration plan). -see_also: -- containers/118-elementsynapse.md mcp_endpoint: https://mcp.hubris.network/mcp secrets_issuance_endpoint: https://secrets.hubris.network/issue diff --git a/hosts/gitea.yaml b/hosts/gitea.yaml index 01e8963..1aa4297 100644 --- a/hosts/gitea.yaml +++ b/hosts/gitea.yaml @@ -27,12 +27,10 @@ services_hosted: url: https://git.hubris.network backend: gitea backend_url: http://192.168.8.121:3000 - doc_page: containers/104-gitea.md + doc_page: knowledge/wiki/containers/104-gitea.md config_repo: dtoro/gitea-customizations risk_notes: hosts all config repos + deploy webhooks; outage blocks auto-deploy and sync notes: - Bare repos live at /mnt/library/repos/dtoro/*.git -see_also: -- containers/104-gitea.md mcp_endpoint: https://mcp.hubris.network/mcp secrets_issuance_endpoint: https://secrets.hubris.network/issue diff --git a/hosts/grimmory.yaml b/hosts/grimmory.yaml index d04d322..740ea4c 100644 --- a/hosts/grimmory.yaml +++ b/hosts/grimmory.yaml @@ -21,7 +21,5 @@ notes: - Docker host for Grimmory (community fork of Booklore). Created 2026-06-29. - Migrated from hubris to strong 2026-07-05 (Phase 2d). Books on ludo-lvm. age_pubkey: age1uellsemnjrzgfg9fxw4jefpy05laxzggwnwhh6ny3wl7alyp6v8q0muxet -see_also: -- containers/130-grimmory.md mcp_endpoint: https://mcp.hubris.network/mcp secrets_issuance_endpoint: https://secrets.hubris.network/issue diff --git a/hosts/haos.yaml b/hosts/haos.yaml index 909198b..467fd4a 100644 --- a/hosts/haos.yaml +++ b/hosts/haos.yaml @@ -22,8 +22,6 @@ runs: services_hosted: - name: haos backend: haos - doc_page: vms/108-haos.md -see_also: -- vms/108-haos.md + doc_page: knowledge/wiki/vms/108-haos.md mcp_endpoint: https://mcp.hubris.network/mcp secrets_issuance_endpoint: https://secrets.hubris.network/issue diff --git a/hosts/house.yaml b/hosts/house.yaml index edcf89c..34f6c72 100644 --- a/hosts/house.yaml +++ b/hosts/house.yaml @@ -22,7 +22,5 @@ notes: - 192.168.8.212 was the hubris IP before migration (briefly picked up by teddycloud via DHCP; teddycloud has since been given a static IP, see hosts.teddycloud) age_pubkey: age1s07zs83ehtlg8jtwvr75ltc3c4cdlemfwjuxrwjtwkqxkl9tpggsyrzn2h -see_also: -- containers/129-house.md mcp_endpoint: https://mcp.hubris.network/mcp secrets_issuance_endpoint: https://secrets.hubris.network/issue diff --git a/hosts/hubris.yaml b/hosts/hubris.yaml index 0f3d065..2c14eab 100644 --- a/hosts/hubris.yaml +++ b/hosts/hubris.yaml @@ -29,10 +29,8 @@ services_hosted: url: https://proxmox.hubris.network backend: hubris port: 8006 - doc_page: hosts/hubris.md + doc_page: knowledge/wiki/hosts/hubris.md risk_notes: "hypervisor UI \u2014 changes here affect every guest on the node" age_pubkey: age1xkklkvnk5z0fsnh6cfgv70hy9ksfy8rdprwerzw4yk3p4p7cxcqs2yvpz6 -see_also: -- hosts/hubris.md mcp_endpoint: https://mcp.hubris.network/mcp secrets_issuance_endpoint: https://secrets.hubris.network/issue diff --git a/hosts/jellyfin.yaml b/hosts/jellyfin.yaml index 982a6df..67a871d 100644 --- a/hosts/jellyfin.yaml +++ b/hosts/jellyfin.yaml @@ -26,7 +26,7 @@ services_hosted: - name: jellyfin url: https://media.hubris.network backend: jellyfin - doc_page: containers/101-jellyfin.md + doc_page: knowledge/wiki/containers/101-jellyfin.md risk_notes: native Authentik OIDC via SSO-Auth plugin, no Caddy forward-auth gate; VAAPI transcode depends on GPU passthrough on strong notes: @@ -35,7 +35,5 @@ notes: - SSO-Auth plugin v4.0.0.4 with Authentik OIDC (no Caddy forward-auth gate) - GPU passed via dev0+dev1: /dev/dri/renderD128 + card0 - Migrated from hubris to strong 2026-07-05 (Phase 2). Library on ludo-lvm. -see_also: -- containers/101-jellyfin.md mcp_endpoint: https://mcp.hubris.network/mcp secrets_issuance_endpoint: https://secrets.hubris.network/issue diff --git a/hosts/mule-images.yaml b/hosts/mule-images.yaml index 26d4c12..ea596d1 100644 --- a/hosts/mule-images.yaml +++ b/hosts/mule-images.yaml @@ -26,9 +26,7 @@ services_hosted: - name: photos url: https://photos.hubris.network backend: mule-images - doc_page: containers/120-mule-images.md + doc_page: knowledge/wiki/containers/120-mule-images.md config_repo: dtoro/mule-image -see_also: -- containers/120-mule-images.md mcp_endpoint: https://mcp.hubris.network/mcp secrets_issuance_endpoint: https://secrets.hubris.network/issue diff --git a/hosts/netbird-vps.yaml b/hosts/netbird-vps.yaml index c3be6b5..fc468d1 100644 --- a/hosts/netbird-vps.yaml +++ b/hosts/netbird-vps.yaml @@ -23,7 +23,7 @@ services_hosted: - name: authentik url: https://auth.hubris.network backend: netbird-vps - doc_page: containers/106-auth-outpost.md + doc_page: knowledge/wiki/containers/106-auth-outpost.md note: core runs on the VPS since 2026-05-31; LAN forward-auth outpost is auth-outpost (LXC 106) at 192.168.8.6:9000. Previous backend value "authentik" referenced the retired embedded-outpost host (LXC 124). risk_notes: "SSO provider \u2014 outage locks login to OIDC/forward-auth services" diff --git a/hosts/nextcloud.yaml b/hosts/nextcloud.yaml index f2611cc..b81b3a4 100644 --- a/hosts/nextcloud.yaml +++ b/hosts/nextcloud.yaml @@ -26,8 +26,6 @@ services_hosted: - name: nextcloud url: https://cloud.hubris.network backend: nextcloud - doc_page: containers/114-nextcloud.md -see_also: -- containers/114-nextcloud.md + doc_page: knowledge/wiki/containers/114-nextcloud.md mcp_endpoint: https://mcp.hubris.network/mcp secrets_issuance_endpoint: https://secrets.hubris.network/issue diff --git a/hosts/nfs-export.yaml b/hosts/nfs-export.yaml index ab2e332..abcff5c 100644 --- a/hosts/nfs-export.yaml +++ b/hosts/nfs-export.yaml @@ -14,7 +14,5 @@ mesh_globals: accepted: - netbird - tailscale -see_also: -- containers/102-nfs-export.md mcp_endpoint: https://mcp.hubris.network/mcp secrets_issuance_endpoint: https://secrets.hubris.network/issue diff --git a/hosts/paperless.yaml b/hosts/paperless.yaml index 17bb657..b836978 100644 --- a/hosts/paperless.yaml +++ b/hosts/paperless.yaml @@ -26,9 +26,7 @@ services_hosted: - name: paperless url: https://paperless.hubris.network backend: paperless - doc_page: containers/103-paperless.md + doc_page: knowledge/wiki/containers/103-paperless.md risk_notes: "document archive \u2014 treat data as irreplaceable; DB operations are destructive-class" -see_also: -- containers/103-paperless.md mcp_endpoint: https://mcp.hubris.network/mcp secrets_issuance_endpoint: https://secrets.hubris.network/issue diff --git a/hosts/romm.yaml b/hosts/romm.yaml index 03b9a91..91c2674 100644 --- a/hosts/romm.yaml +++ b/hosts/romm.yaml @@ -22,7 +22,5 @@ notes: - MariaDB sidecar at /opt/romm/docker-compose.yml. - ROMs on ludo-lvm media volume at /mnt/media_local/roms. - 1 core / 2 GiB RAM / 16 GiB rootfs (ludo-lvm). -see_also: -- containers/134-romm.md mcp_endpoint: https://mcp.hubris.network/mcp secrets_issuance_endpoint: https://secrets.hubris.network/issue diff --git a/hosts/seanime.yaml b/hosts/seanime.yaml index 1857d2a..8344a10 100644 --- a/hosts/seanime.yaml +++ b/hosts/seanime.yaml @@ -25,7 +25,5 @@ notes: - /anime mounted from strong ludo-lvm (/mnt/media_local/anime) - Caddy: "https://seanime.hubris.network \u2192 192.168.8.248:43211" - qBittorrent auth subnet whitelist expanded to 192.168.8.0/24 for seanime access -see_also: -- containers/133-seanime.md mcp_endpoint: https://mcp.hubris.network/mcp secrets_issuance_endpoint: https://secrets.hubris.network/issue diff --git a/hosts/sophia.yaml b/hosts/sophia.yaml index c564bb6..87e3efe 100644 --- a/hosts/sophia.yaml +++ b/hosts/sophia.yaml @@ -19,7 +19,5 @@ mesh_globals: - tailscale mounts: - /mnt/library -see_also: -- containers/119-sophia.md mcp_endpoint: https://mcp.hubris.network/mcp secrets_issuance_endpoint: https://secrets.hubris.network/issue diff --git a/hosts/strong.yaml b/hosts/strong.yaml index 463a0f4..b0d2fab 100644 --- a/hosts/strong.yaml +++ b/hosts/strong.yaml @@ -28,7 +28,5 @@ notes: \ (filename kept as-is, it's a historical planning doc). Only Phase 1 (Proxmox install + cluster join)\ \ is done; no physical drive move, service migration, or GPU passthrough has happened yet." age_pubkey: age1rtwvdct6avjkr3cyxv3vue3vqx4d524fjfr3vk7xrnvyrylnry5sm54sn4 -see_also: -- hosts/strong.md mcp_endpoint: https://mcp.hubris.network/mcp secrets_issuance_endpoint: https://secrets.hubris.network/issue diff --git a/hosts/teddycloud.yaml b/hosts/teddycloud.yaml index f39c622..15bb9a3 100644 --- a/hosts/teddycloud.yaml +++ b/hosts/teddycloud.yaml @@ -23,7 +23,7 @@ services_hosted: - name: teddycloud url: https://teddy.hubris.network backend: teddycloud - doc_page: containers/131-teddycloud.md + doc_page: knowledge/wiki/containers/131-teddycloud.md note: self-hosted TeddyCloud (Toniebox cloud reimplementation), docker compose risk_notes: "no Caddy forward-auth gate (unlike sab.hubris.network on the same Caddyfile) \u2014 reachable\ \ to anyone on the LAN/mesh who can resolve teddy.hubris.network; undocumented in inventory.yaml until\ @@ -38,7 +38,5 @@ notes: \ DHCP before that \u2014 see hosts/strong.md's 2026-07-05 changelog)." - "No age_pubkey / homelab-context enrollment \u2014 not a homelab CLI client, just a docker-compose app\ \ container. Not a required follow-up unless it needs secrets." -see_also: -- containers/131-teddycloud.md mcp_endpoint: https://mcp.hubris.network/mcp secrets_issuance_endpoint: https://secrets.hubris.network/issue diff --git a/hosts/trmnl.yaml b/hosts/trmnl.yaml index 2e80ecc..937dea7 100644 --- a/hosts/trmnl.yaml +++ b/hosts/trmnl.yaml @@ -22,9 +22,7 @@ services_hosted: backend: trmnl url: https://trmnl.hubris.network note: self-hosted middleware for TRMNL e-ink plugins (polled by TRMNL cloud) - doc_page: containers/128-trmnl.md + doc_page: knowledge/wiki/containers/128-trmnl.md config_repo: dtoro/terminalito -see_also: -- containers/128-trmnl.md mcp_endpoint: https://mcp.hubris.network/mcp secrets_issuance_endpoint: https://secrets.hubris.network/issue diff --git a/hosts/zimaos.yaml b/hosts/zimaos.yaml index 3fd4ac6..a384967 100644 --- a/hosts/zimaos.yaml +++ b/hosts/zimaos.yaml @@ -21,8 +21,6 @@ services_hosted: - name: zimaos url: https://zimaos.hubris.network backend: zimaos - doc_page: vms/100-zimaos.md -see_also: -- vms/100-zimaos.md + doc_page: knowledge/wiki/vms/100-zimaos.md mcp_endpoint: https://mcp.hubris.network/mcp secrets_issuance_endpoint: https://secrets.hubris.network/issue diff --git a/inventory.yaml b/inventory.yaml index 47d2398..db8bedb 100644 --- a/inventory.yaml +++ b/inventory.yaml @@ -48,26 +48,26 @@ services: url: https://proxmox.hubris.network backend: hubris port: 8006 - doc_page: hosts/hubris.md + doc_page: knowledge/wiki/hosts/hubris.md risk_notes: hypervisor UI — changes here affect every guest on the node gitea: url: https://git.hubris.network backend: gitea backend_url: http://192.168.8.121:3000 - doc_page: containers/104-gitea.md + doc_page: knowledge/wiki/containers/104-gitea.md config_repo: dtoro/gitea-customizations risk_notes: hosts all config repos + deploy webhooks; outage blocks auto-deploy and sync caddy: backend: caddy role: reverse-proxy note: terminates all *.hubris.network - doc_page: containers/121-caddy.md + doc_page: knowledge/wiki/containers/121-caddy.md config_repo: dtoro/caddy-conf risk_notes: wide blast radius — every *.hubris.network route rides on it (see oikos/policy.yaml service_overrides) authentik: url: https://auth.hubris.network backend: netbird-vps - doc_page: containers/106-auth-outpost.md + doc_page: knowledge/wiki/containers/106-auth-outpost.md note: >- core runs on the VPS since 2026-05-31; LAN forward-auth outpost is auth-outpost (LXC 106) at 192.168.8.6:9000. Previous backend value @@ -76,58 +76,58 @@ services: dns: backend: dns note: Technitium DNS, split-horizon zone - doc_page: containers/107-dns.md + doc_page: knowledge/wiki/containers/107-dns.md risk_notes: LAN-wide resolver — misconfig breaks name resolution for every client jellyfin: url: https://media.hubris.network backend: jellyfin - doc_page: containers/101-jellyfin.md + doc_page: knowledge/wiki/containers/101-jellyfin.md risk_notes: native Authentik OIDC via SSO-Auth plugin, no Caddy forward-auth gate; VAAPI transcode depends on GPU passthrough on strong nextcloud: url: https://cloud.hubris.network backend: nextcloud - doc_page: containers/114-nextcloud.md + doc_page: knowledge/wiki/containers/114-nextcloud.md paperless: url: https://paperless.hubris.network backend: paperless - doc_page: containers/103-paperless.md + doc_page: knowledge/wiki/containers/103-paperless.md risk_notes: document archive — treat data as irreplaceable; DB operations are destructive-class matrix: url: https://matrix.hubris.network backend: elementsynapse - doc_page: containers/118-elementsynapse.md + doc_page: knowledge/wiki/containers/118-elementsynapse.md risk_notes: alert/approval channel for Oikos — outage silences agent escalation photos: url: https://photos.hubris.network backend: mule-images - doc_page: containers/120-mule-images.md + doc_page: knowledge/wiki/containers/120-mule-images.md config_repo: dtoro/mule-image arr_stack: backend: arriman note: jellyseerr / qbit / sab on docker compose - doc_page: containers/122-arriman.md + doc_page: knowledge/wiki/containers/122-arriman.md artifacto: backend: apps url: https://artifacto.hubris.network - doc_page: containers/105-apps.md + doc_page: knowledge/wiki/containers/105-apps.md config_repo: dtoro/Artifacto trmnl: backend: trmnl url: https://trmnl.hubris.network note: self-hosted middleware for TRMNL e-ink plugins (polled by TRMNL cloud) - doc_page: containers/128-trmnl.md + doc_page: knowledge/wiki/containers/128-trmnl.md config_repo: dtoro/terminalito zimaos: url: https://zimaos.hubris.network backend: zimaos - doc_page: vms/100-zimaos.md + doc_page: knowledge/wiki/vms/100-zimaos.md haos: backend: haos - doc_page: vms/108-haos.md + doc_page: knowledge/wiki/vms/108-haos.md teddycloud: url: https://teddy.hubris.network backend: teddycloud - doc_page: containers/131-teddycloud.md + doc_page: knowledge/wiki/containers/131-teddycloud.md note: self-hosted TeddyCloud (Toniebox cloud reimplementation), docker compose risk_notes: no Caddy forward-auth gate (unlike sab.hubris.network on the same Caddyfile) — reachable to anyone on the LAN/mesh who can resolve teddy.hubris.network; undocumented @@ -138,7 +138,7 @@ services: systemd_unit: homelab-mcp public_host: mcp.hubris.network endpoint: https://mcp.hubris.network/mcp - doc_page: infrastructure/homelab-context.md + doc_page: knowledge/wiki/infrastructure/homelab-context.md config_repo: dtoro/Homelab-Docs note: MCP server. Read-only context + management. Reachable on the LAN via Caddy and from off-LAN via Netbird (192.168.8.0/24 is a network resource routed through diff --git a/investigations/2026-06-06-authentik-session-lifetime.md b/investigations/2026-06-06-authentik-session-lifetime.md index 0de009e..faf9613 100644 --- a/investigations/2026-06-06-authentik-session-lifetime.md +++ b/investigations/2026-06-06-authentik-session-lifetime.md @@ -90,9 +90,9 @@ print("session_duration:", stage.session_duration) # → "days=30" ## Related -- [Container 106 — auth-outpost](../containers/106-auth-outpost.md) +- [Container 106 — auth-outpost](../knowledge/wiki/containers/106-auth-outpost.md) - [Authentik VPS migration](2026-05-31-authentik-vps-migration.md) -- [Ingress (VPS Traefik)](../infrastructure/ingress.md) +- [Ingress (VPS Traefik)](../knowledge/wiki/infrastructure/ingress.md) - `.hermes/plans/2026-06-06_232200-authentik-frequent-login-fix.md` — original plan ## Changelog diff --git a/investigations/2026-06-06-caddyfile-truncation.md b/investigations/2026-06-06-caddyfile-truncation.md index 856b074..cf3203a 100644 --- a/investigations/2026-06-06-caddyfile-truncation.md +++ b/investigations/2026-06-06-caddyfile-truncation.md @@ -55,7 +55,7 @@ This is the same class of drift as the June 5th incidents (paperless, HAOS, apps ## Related - [DHCP drift investigation (previous incident)](2026-06-05-homelab-dhcp-drift.md) -- [Caddy (121)](../containers/121-caddy.md) -- [elementsynapse (118)](../containers/118-elementsynapse.md) +- [Caddy (121)](../knowledge/wiki/containers/121-caddy.md) +- [elementsynapse (118)](../knowledge/wiki/containers/118-elementsynapse.md) - [dns-sync script](../scripts/dns-sync.py) - [check-caddy-backends script](../scripts/check-caddy-backends.sh) \ No newline at end of file diff --git a/GLOSSARY.md b/knowledge/GLOSSARY.md similarity index 87% rename from GLOSSARY.md rename to knowledge/GLOSSARY.md index b88f94f..9b68dc0 100644 --- a/GLOSSARY.md +++ b/knowledge/GLOSSARY.md @@ -18,7 +18,7 @@ Terms and abbreviations used throughout the homelab wiki. | **Mesh** | Overlay VPN for off-LAN connectivity. Netbird is current; Tailscale is legacy | | **Netbird** | Preferred mesh VPN. VPS hosts the management plane; all homelab nodes are members | | **OIDC** | OpenID Connect. Protocol used by Authentik for SSO login flows | -| **Oikos** | Agent operating model ([.agents/OIKOS.md](.agents/OIKOS.md)). OODA loop, risk classes, policy, ontology | +| **Oikos** | Agent operating model ([.agents/OIKOS.md](../.agents/OIKOS.md)). OODA loop, risk classes, policy, ontology | | **PVE** | Proxmox Virtual Environment — the hypervisor on both hubris and strong | | **SOPS** | `sops` — Mozilla SOPS. Encrypts secrets with age keys so they live in the git repo | | **Strong** | Secondary Proxmox VE node. Cluster member 2 (hostname `strong`, nickname ludo/ludo-mini) | @@ -29,5 +29,5 @@ Terms and abbreviations used throughout the homelab wiki. ## See also -- [Infrastructure index](infrastructure/index.md) — cross-cutting systems each with their own doc page -- [OIKOS operating model](.agents/OIKOS.md) — agent policy, risk classes, lifecycle \ No newline at end of file +- [Infrastructure index](wiki/infrastructure/index.md) — cross-cutting systems each with their own doc page +- [OIKOS operating model](../.agents/OIKOS.md) — agent policy, risk classes, lifecycle \ No newline at end of file diff --git a/knowledge/index.md b/knowledge/index.md new file mode 100644 index 0000000..0067f7f --- /dev/null +++ b/knowledge/index.md @@ -0,0 +1,14 @@ +# Knowledge + +The durable, authoritative current-state documentation of the homelab: one page per node and per +cross-cutting system, synthesized from live state and evidence. Structure and rules are in +[the knowledge schema](../.agents/domains/knowledge/schema.md). + +| Section | What it covers | +|---------|----------------| +| [wiki/hosts/](wiki/hosts/) | Proxmox host narratives — `hubris`, `strong`. | +| [wiki/containers/](wiki/containers/index.md) | LXC fleet — one page per container, plus the master table and archaeology. | +| [wiki/vms/](wiki/vms/index.md) | Virtual machines — ZimaOS, Home Assistant OS. | +| [wiki/infrastructure/](wiki/infrastructure/index.md) | Cross-cutting systems — DNS, ingress, mesh, storage, auth, monitoring, generated topology. | +| [sources/](sources/index.md) | External reference docs and the pointer to incident evidence. | +| [GLOSSARY.md](GLOSSARY.md) | Term definitions. | diff --git a/knowledge/log.md b/knowledge/log.md new file mode 100644 index 0000000..4cb195a --- /dev/null +++ b/knowledge/log.md @@ -0,0 +1,7 @@ +# Knowledge — operations log + +Append-only record of documentation-maintenance operations on the knowledge wiki (restructures, +source ingests, lint sweeps). One line per operation, newest last. Infrastructure changes belong in +each page's `## Changelog` and the Oikos change ledger, not here. + +## [2026-07-06] restructure | moved node/infrastructure narratives under knowledge/wiki/; references under knowledge/sources/; repointed inventory doc_page fields and gen-topology.py output. diff --git a/knowledge/sources/index.md b/knowledge/sources/index.md new file mode 100644 index 0000000..f4559f3 --- /dev/null +++ b/knowledge/sources/index.md @@ -0,0 +1,9 @@ +# Sources + +Immutable evidence the wiki synthesizes from. External reference docs live under `references/`; +incident evidence lives in [`investigations/`](../../investigations/index.md) (written once at +incident time, then linked from the changelogs of the nodes they implicate). + +| Slug | Reference | Summary | +|------|-----------|---------| +| cert-sync-and-traefik-config | [references/cert-sync-and-traefik-config.md](references/cert-sync-and-traefik-config.md) | VPS traefik config and the LAN↔VPS certificate mirror. | diff --git a/infrastructure/references/cert-sync-and-traefik-config.md b/knowledge/sources/references/cert-sync-and-traefik-config.md similarity index 100% rename from infrastructure/references/cert-sync-and-traefik-config.md rename to knowledge/sources/references/cert-sync-and-traefik-config.md diff --git a/containers/101-jellyfin.md b/knowledge/wiki/containers/101-jellyfin.md similarity index 97% rename from containers/101-jellyfin.md rename to knowledge/wiki/containers/101-jellyfin.md index 597e231..28c68ac 100644 --- a/containers/101-jellyfin.md +++ b/knowledge/wiki/containers/101-jellyfin.md @@ -78,7 +78,7 @@ User → media.hubris.network → Caddy (TLS, no forward-auth) → Jellyfin :809 `http://` redirect URIs that Authentik rejects) - `EnableAuthorization`: `false` (prevents plugin from overwriting admin permissions on each SSO login — see - [jellyfin-sso-plugin](../devops/homelab-authentik-admin/references/jellyfin-sso-plugin.md)) + [jellyfin-sso-plugin](../../../devops/homelab-authentik-admin/references/jellyfin-sso-plugin.md)) - `OidScopes`: `["email"]` (openid+profile added by default by the plugin; must be non-null or `OidChallenge()` throws `ArgumentNullException`) @@ -133,7 +133,7 @@ Member of the [media GID 10000](../infrastructure/media-permissions.md) standard - [Media permissions](../infrastructure/media-permissions.md) - [arriman](122-arriman.md) — \*arr stack writes the libraries jellyfin reads - [DNS split-horizon](../infrastructure/dns.md) -- [Authentik admin](../devops/homelab-authentik-admin/SKILL.md) — OIDC provider creation, SSO plugin config +- [Authentik admin](../../../devops/homelab-authentik-admin/SKILL.md) — OIDC provider creation, SSO plugin config ## Changelog diff --git a/containers/102-nfs-export.md b/knowledge/wiki/containers/102-nfs-export.md similarity index 98% rename from containers/102-nfs-export.md rename to knowledge/wiki/containers/102-nfs-export.md index c8efe5a..15c69d9 100644 --- a/containers/102-nfs-export.md +++ b/knowledge/wiki/containers/102-nfs-export.md @@ -54,7 +54,7 @@ We considered three options before building this: | Option | Outcome | |---|---| -| **NFS on hubris bare-metal host** | Best performance, but adds long-lived NFS/RPC daemons to a host with a recent crash episode ([hubris crash 2026-04-21/22](../investigations/index.md)). Rejected. | +| **NFS on hubris bare-metal host** | Best performance, but adds long-lived NFS/RPC daemons to a host with a recent crash episode ([hubris crash 2026-04-21/22](../../../investigations/index.md)). Rejected. | | **SMB on host** | Same host-blast-radius problem, plus 30–50% lower throughput than NFS on Linux↔Linux. Rejected. | | **NFS in a dedicated LXC** ← this | Within ~2% of host performance (LXC is namespace isolation; IO path is unchanged), zero new daemons on hubris, matches the existing fleet pattern. Selected. | diff --git a/containers/103-paperless.md b/knowledge/wiki/containers/103-paperless.md similarity index 100% rename from containers/103-paperless.md rename to knowledge/wiki/containers/103-paperless.md diff --git a/containers/104-gitea.md b/knowledge/wiki/containers/104-gitea.md similarity index 100% rename from containers/104-gitea.md rename to knowledge/wiki/containers/104-gitea.md diff --git a/containers/105-apps.md b/knowledge/wiki/containers/105-apps.md similarity index 100% rename from containers/105-apps.md rename to knowledge/wiki/containers/105-apps.md diff --git a/containers/106-auth-outpost.md b/knowledge/wiki/containers/106-auth-outpost.md similarity index 81% rename from containers/106-auth-outpost.md rename to knowledge/wiki/containers/106-auth-outpost.md index c1293a4..19c16b1 100644 --- a/containers/106-auth-outpost.md +++ b/knowledge/wiki/containers/106-auth-outpost.md @@ -1,6 +1,6 @@ # 106 — `auth-outpost` -Authentik **forward-auth outpost** for LAN-gated apps. A stateless proxy that connects outbound to the [VPS Authentik core](../investigations/2026-05-31-authentik-vps-migration.md) and serves forward-auth locally, so [Caddy (121)](121-caddy.md) never hairpins auth through VPS Traefik. +Authentik **forward-auth outpost** for LAN-gated apps. A stateless proxy that connects outbound to the [VPS Authentik core](../../../investigations/2026-05-31-authentik-vps-migration.md) and serves forward-auth locally, so [Caddy (121)](121-caddy.md) never hairpins auth through VPS Traefik. ## At a glance - **Hostname:** `auth-outpost` @@ -12,7 +12,7 @@ Authentik **forward-auth outpost** for LAN-gated apps. A stateless proxy that co ## Role -Runs one container — `ghcr.io/goauthentik/proxy` — that opens an outbound websocket to `https://auth.hubris.network` (the VPS core), pulls its proxy-provider config, and answers Caddy's `forward_auth` subrequests on `192.168.8.6:9000` (LAN-only bind). Because the call path is **Caddy → outpost (LAN)**, with no Traefik in between, `X-Forwarded-Host` is preserved — the failure that 404s when Caddy is pointed at `https://auth.hubris.network` directly (Traefik rewrites the header). See the [migration investigation](../investigations/2026-05-31-authentik-vps-migration.md). +Runs one container — `ghcr.io/goauthentik/proxy` — that opens an outbound websocket to `https://auth.hubris.network` (the VPS core), pulls its proxy-provider config, and answers Caddy's `forward_auth` subrequests on `192.168.8.6:9000` (LAN-only bind). Because the call path is **Caddy → outpost (LAN)**, with no Traefik in between, `X-Forwarded-Host` is preserved — the failure that 404s when Caddy is pointed at `https://auth.hubris.network` directly (Traefik rewrites the header). See the [migration investigation](../../../investigations/2026-05-31-authentik-vps-migration.md). ## Service / port map | Service | Listen | Notes | @@ -45,12 +45,12 @@ Fix: the LAN outpost gets its **own** domain. - [124 — authentik](124-authentik.md) — old embedded-outpost host (now DNS-only) - [Caddy (121)](121-caddy.md) — forward-auth consumer - [Ingress (VPS traefik)](../infrastructure/ingress.md) -- [Authentik VPS migration](../investigations/2026-05-31-authentik-vps-migration.md) +- [Authentik VPS migration](../../../investigations/2026-05-31-authentik-vps-migration.md) ## Changelog ### 2026-06-06 — Authentik session lifetime extended to 30 days -VPS Authentik core `user_login` stage updated: `session_duration` changed from `seconds=0` (session cookie, cleared on browser close) to `days=30` (persistent 30-day cookie). Also set `AUTHENTIK_SESSIONS__UNAUTHENTICATED_AGE=days=30` in `/opt/authentik.env` on the VPS. See [investigation](../investigations/2026-06-06-authentik-session-lifetime.md). +VPS Authentik core `user_login` stage updated: `session_duration` changed from `seconds=0` (session cookie, cleared on browser close) to `days=30` (persistent 30-day cookie). Also set `AUTHENTIK_SESSIONS__UNAUTHENTICATED_AGE=days=30` in `/opt/authentik.env` on the VPS. See [investigation](../../../investigations/2026-06-06-authentik-session-lifetime.md). ### 2026-06-01 — created; forward-auth cut over from LXC 124 -New dedicated LXC for the LAN forward-auth outpost (Phase 1 of the [architecture migration](../investigations/2026-05-31-authentik-vps-migration.md)). Deployed `goauthentik/proxy:2026.5.2` pointed at the VPS core; repointed Caddy `(authentik)` from `192.168.8.180:9000` → `192.168.8.6:9000`. Verified Paperless/qBittorrent/Artifacto return the SSO redirect with **124-Authentik stopped**, confirming the frozen instance is out of the path. dnsmasq stays on 124 until [DNS is relocated](124-authentik.md). +New dedicated LXC for the LAN forward-auth outpost (Phase 1 of the [architecture migration](../../../investigations/2026-05-31-authentik-vps-migration.md)). Deployed `goauthentik/proxy:2026.5.2` pointed at the VPS core; repointed Caddy `(authentik)` from `192.168.8.180:9000` → `192.168.8.6:9000`. Verified Paperless/qBittorrent/Artifacto return the SSO redirect with **124-Authentik stopped**, confirming the frozen instance is out of the path. dnsmasq stays on 124 until [DNS is relocated](124-authentik.md). diff --git a/containers/107-dns.md b/knowledge/wiki/containers/107-dns.md similarity index 93% rename from containers/107-dns.md rename to knowledge/wiki/containers/107-dns.md index a07e2e7..74a99a6 100644 --- a/containers/107-dns.md +++ b/knowledge/wiki/containers/107-dns.md @@ -29,7 +29,7 @@ Authoritative split-horizon DNS for `hubris.network` on the LAN/mesh, plus recur - **Plain LAN clients (`192.168.178.x`):** Fritz!Box DHCP still hands out Fritz!Box itself (`192.168.178.1`) as DNS — no split-horizon for non-mesh clients. Changing this requires a secondary DNS fallback, which Fritz!OS 8.x doesn't expose in a single DHCP field. ## dns-sync (Technitium = authoring source) -`/opt/dns-sync/sync.py` (cron `*/10`, logs `/var/log/dns-sync.log`) reconciles this zone's named A-records → the NetBird managed DNS zone via the NetBird API (`/api/dns/zones/{id}/records`). Token at `/opt/dns-sync/netbird-token` (mode 600; source of truth in sops `secrets/netbird-pat.yaml`). **Edit DNS only here**; the sync propagates to the mesh. It deletes NetBird records absent from Technitium. Tracked: [scripts/dns-sync.py](../scripts/dns-sync.py). *Why this exists:* NetBird won't forward to Technitium for mesh peers (self-IP / nameserver-group quirks), so we sync into the managed zone instead — see [dns.md](../infrastructure/dns.md). +`/opt/dns-sync/sync.py` (cron `*/10`, logs `/var/log/dns-sync.log`) reconciles this zone's named A-records → the NetBird managed DNS zone via the NetBird API (`/api/dns/zones/{id}/records`). Token at `/opt/dns-sync/netbird-token` (mode 600; source of truth in sops `secrets/netbird-pat.yaml`). **Edit DNS only here**; the sync propagates to the mesh. It deletes NetBird records absent from Technitium. Tracked: [scripts/dns-sync.py](../../../scripts/dns-sync.py). *Why this exists:* NetBird won't forward to Technitium for mesh peers (self-IP / nameserver-group quirks), so we sync into the managed zone instead — see [dns.md](../infrastructure/dns.md). ## DHCP @@ -55,7 +55,7 @@ Added for [trmnl (128)](128-trmnl.md) (LAN path via [Caddy (121)](121-caddy.md)) Although the 2026-06-03 changelog claimed "cron */10", **no crontab was actually configured** on the LXC. The sync was running only via ad-hoc manual invocations during incident debugging. Fixed by adding `/etc/cron.d/dns-sync`. ### 2026-06-03 — DHCP pool narrowed to `.241–.254` -Previous pool `.100–.240` overlapped with all static LXCs/VMs (`.101–.239`). Shrunk via API (`/api/dhcp/scopes/set`). 11 stale DHCP leases in `.101–.110` remain until natural expiry (2026-06-04). See [plan](../plans/2026-06-03-dhcp-pool-exclude-static-ips.md). +Previous pool `.100–.240` overlapped with all static LXCs/VMs (`.101–.239`). Shrunk via API (`/api/dhcp/scopes/set`). 11 stale DHCP leases in `.101–.110` remain until natural expiry (2026-06-04). See [plan](../../../plans/2026-06-03-dhcp-pool-exclude-static-ips.md). ### 2026-06-03 — dns-sync added (Technitium → NetBird managed zone) This Technitium became the single DNS authoring source; `/opt/dns-sync/sync.py` (cron */10) reconciles named A-records into the NetBird managed zone via the API. Fixed previously-broken mesh names (`sso`, `nfs-export`, `mcp`, `secrets`) by adding them to the managed zone; reaped obsolete `files`/`photos-new`. See [dns.md](../infrastructure/dns.md). diff --git a/containers/114-nextcloud.md b/knowledge/wiki/containers/114-nextcloud.md similarity index 100% rename from containers/114-nextcloud.md rename to knowledge/wiki/containers/114-nextcloud.md diff --git a/containers/118-elementsynapse.md b/knowledge/wiki/containers/118-elementsynapse.md similarity index 100% rename from containers/118-elementsynapse.md rename to knowledge/wiki/containers/118-elementsynapse.md diff --git a/containers/119-sophia.md b/knowledge/wiki/containers/119-sophia.md similarity index 100% rename from containers/119-sophia.md rename to knowledge/wiki/containers/119-sophia.md diff --git a/containers/120-mule-images.md b/knowledge/wiki/containers/120-mule-images.md similarity index 100% rename from containers/120-mule-images.md rename to knowledge/wiki/containers/120-mule-images.md diff --git a/containers/121-caddy.md b/knowledge/wiki/containers/121-caddy.md similarity index 97% rename from containers/121-caddy.md rename to knowledge/wiki/containers/121-caddy.md index 3e7ff19..a607a60 100644 --- a/containers/121-caddy.md +++ b/knowledge/wiki/containers/121-caddy.md @@ -85,7 +85,7 @@ Gitea webhook id 2 on `dtoro/caddy-conf`. Receiver, deploy script, install scrip - **Dirty-tree auto-stash:** stashes local changes before `git pull --ff-only` so the webhook doesn't fail on local edits - **Auto-backup:** saves `Caddyfile.bak.` before any modifications, keeps last 5 -Also: [elementsynapse LXC 118](../containers/118-elementsynapse.md) found to have DHCP-overridden static IP (actual `.244` vs config `.239`) during incident investigation — fixed. +Also: [elementsynapse LXC 118](118-elementsynapse.md) found to have DHCP-overridden static IP (actual `.244` vs config `.239`) during incident investigation — fixed. ### 2026-06-02 — caddy.service unit missing; recreated After the Slate AX → SODOLA network migration, Caddy was not listening (ports 80/443 dead). Root cause: the custom hubris1 Debian package (`caddy_1:2.11.3-hubris1_amd64`) does not ship a systemd service unit file. The unit had previously existed but was lost (likely on a package reinstall). Recreated at `/lib/systemd/system/caddy.service` with standard Caddy service config + `EnvironmentFile=/etc/caddy/caddy.env` (already present in `caddy.service.d/override.conf`). **Risk:** the unit will be lost again if the package is reinstalled without the file being tracked. Fix: add the service unit to the `caddy-conf` repo or rebuild the hubris1 package to include it. diff --git a/containers/122-arriman.md b/knowledge/wiki/containers/122-arriman.md similarity index 100% rename from containers/122-arriman.md rename to knowledge/wiki/containers/122-arriman.md diff --git a/containers/128-trmnl.md b/knowledge/wiki/containers/128-trmnl.md similarity index 98% rename from containers/128-trmnl.md rename to knowledge/wiki/containers/128-trmnl.md index e15e569..c56f466 100644 --- a/containers/128-trmnl.md +++ b/knowledge/wiki/containers/128-trmnl.md @@ -35,7 +35,7 @@ Not yet SOPS-enrolled. The poll token is set directly in `/etc/trmnl-plugins/env - [VPS ingress](../infrastructure/ingress.md) — public edge (cert mirror + traefik router) - [DNS (107)](107-dns.md) — Technitium A record `trmnl → 192.168.8.175` (LAN path via Caddy) - [Gitea (104)](104-gitea.md) — source repo `dtoro/terminalito` -- [Plan: 2026-06-24 TRMNL plugins LXC](../plans/2026-06-24-trmnl-plugins-lxc.md) +- [Plan: 2026-06-24 TRMNL plugins LXC](../../../plans/2026-06-24-trmnl-plugins-lxc.md) ## Changelog ### 2026-06-24 — auto-deploy + LAN DNS wired diff --git a/containers/129-house.md b/knowledge/wiki/containers/129-house.md similarity index 97% rename from containers/129-house.md rename to knowledge/wiki/containers/129-house.md index 333fabb..30f3b44 100644 --- a/containers/129-house.md +++ b/knowledge/wiki/containers/129-house.md @@ -40,7 +40,7 @@ Yuvomi family planner (formerly Oikos). Self-hosted family planner with 14 modul - [DNS (107)](107-dns.md) — Technitium A record `house → 192.168.8.175` (LAN path via Caddy) - [Paperless (103)](103-paperless.md) — native DMS connector (API at `:8000`) - [TRMNL (128)](128-trmnl.md) — Google Calendar tokens source -- [Deployment plan](../plans/2026-06-25-yuvomi-deployment.md) +- [Deployment plan](../../../plans/2026-06-25-yuvomi-deployment.md) ## Changelog diff --git a/containers/130-grimmory.md b/knowledge/wiki/containers/130-grimmory.md similarity index 100% rename from containers/130-grimmory.md rename to knowledge/wiki/containers/130-grimmory.md diff --git a/containers/131-teddycloud.md b/knowledge/wiki/containers/131-teddycloud.md similarity index 94% rename from containers/131-teddycloud.md rename to knowledge/wiki/containers/131-teddycloud.md index 3531cab..518e9cf 100644 --- a/containers/131-teddycloud.md +++ b/knowledge/wiki/containers/131-teddycloud.md @@ -7,7 +7,7 @@ audio content against a local server instead of the official cloud. Predates the client-enrollment convention entirely; nobody wrote it down. Found and documented on 2026-07-06 after Oikos's drift detector (`oikos/drift.py`) flagged `pve_id 131` as live on hubris (via `pct list`) with no `inventory.yaml` entry — see -[OIKOS.md](../OIKOS.md)'s Week 3 build-status note. `containers/132-rclone.md` had already +[OIKOS.md](../../../OIKOS.md)'s Week 3 build-status note. `containers/132-rclone.md` had already mentioned it in passing ("LXC 131 was already taken by an undocumented `teddycloud` container"), and `hosts/strong.md`'s 2026-07-05 migration changelog fixed a DHCP conflict for it — but it never got its own inventory entry or doc page until now. @@ -50,7 +50,7 @@ for the first time. - [rclone (132)](132-rclone.md) — landed on pve_id 132 specifically because 131 was already taken by this container - [Containers index](index.md) -- [OIKOS.md](../OIKOS.md) — drift detector that caught this +- [OIKOS.md](../../../OIKOS.md) — drift detector that caught this ## Changelog diff --git a/containers/132-rclone.md b/knowledge/wiki/containers/132-rclone.md similarity index 99% rename from containers/132-rclone.md rename to knowledge/wiki/containers/132-rclone.md index 800adda..c322a6e 100644 --- a/containers/132-rclone.md +++ b/knowledge/wiki/containers/132-rclone.md @@ -95,7 +95,7 @@ after this set. Fixed by symlinking `/usr/local/bin/{sops,homelab}` into `/usr/bin` (always on the minimal PATH), rather than relying on `/etc/environment`. Same category as the documented [`pct exec` no-initgroups gotcha](../infrastructure/media-permissions.md#gotchas) — worth adding to -[agent-enrollment.md troubleshooting](../operations/agent-enrollment.md#troubleshooting) if it recurs +[agent-enrollment.md troubleshooting](../../../operations/agent-enrollment.md#troubleshooting) if it recurs on future LXC bootstraps. ## Known issue: `rclone-rcd.service` OOM-killed under 1 GiB RAM (root cause, resolved) diff --git a/containers/133-seanime.md b/knowledge/wiki/containers/133-seanime.md similarity index 100% rename from containers/133-seanime.md rename to knowledge/wiki/containers/133-seanime.md diff --git a/containers/134-romm.md b/knowledge/wiki/containers/134-romm.md similarity index 100% rename from containers/134-romm.md rename to knowledge/wiki/containers/134-romm.md diff --git a/containers/archive/123-claudio-bot.md b/knowledge/wiki/containers/archive/123-claudio-bot.md similarity index 100% rename from containers/archive/123-claudio-bot.md rename to knowledge/wiki/containers/archive/123-claudio-bot.md diff --git a/containers/archive/127-mule-photos-new.md b/knowledge/wiki/containers/archive/127-mule-photos-new.md similarity index 100% rename from containers/archive/127-mule-photos-new.md rename to knowledge/wiki/containers/archive/127-mule-photos-new.md diff --git a/containers/index.md b/knowledge/wiki/containers/index.md similarity index 98% rename from containers/index.md rename to knowledge/wiki/containers/index.md index ca8486d..9119132 100644 --- a/containers/index.md +++ b/knowledge/wiki/containers/index.md @@ -32,7 +32,7 @@ Most containers live on [`hubris`](../hosts/hubris.md). Some have been | 106 | flaresolverr | ~2026-04-28 | Folded into the arriman docker compose | | 116 | heaper | 2026-05-14 | Decommissioned by user; data subtree at `/mnt/library/heaper` (224 MiB) retained | | 126 | plato | 2026-06-28 | Notes/discovery workspace decommissioned; data at `/mnt/library/documents/plato` retained for archaeology | -| 123 | claudio-bot (destroyed — see [archive](archive/123-claudio-bot.md)) | 2026-06-04 | Replaced by Hermes Agent on mac-mini; monitoring migrated to `homelab-health-watchdog` cron. See [deprecation plan](../plans/2026-06-04_130000-deprecate-claudio-bot.md) | +| 123 | claudio-bot (destroyed — see [archive](archive/123-claudio-bot.md)) | 2026-06-04 | Replaced by Hermes Agent on mac-mini; monitoring migrated to `homelab-health-watchdog` cron. See [deprecation plan](../../../plans/2026-06-04_130000-deprecate-claudio-bot.md) | | 109 | syncthing | 2026-05-14 | Decommissioned by user; `/mnt/library/syncthing` was already empty | | 125 | seafile | 2026-05-13 | Seafile Pro evaluation, user disliked the product; teardown also removed `files.hubris.network` from caddy + dnsmasq | | 107 | marimo | between 2026-04-21 and 2026-04-28 | Decommissioned | diff --git a/hosts/hubris.md b/knowledge/wiki/hosts/hubris.md similarity index 93% rename from hosts/hubris.md rename to knowledge/wiki/hosts/hubris.md index d14e10a..d5155b2 100644 --- a/hosts/hubris.md +++ b/knowledge/wiki/hosts/hubris.md @@ -8,7 +8,7 @@ workloads still live here. As of 2026-07-01, hubris is node 1 of the 2-node ## At a glance - **Role:** Proxmox VE 9.1.2 hypervisor (kernel `6.14.11-4-pve`) - **Hardware:** GMKtec NucBox M6 Ultra — AMD Ryzen 5 7640HS (Phoenix APU), 12 vCPU / ~28 GiB RAM, 2× Samsung 990 EVO Plus NVMe (one SSD primary, one for `library` LVM). 2× Realtek RTL8125 NICs (`r8169`). -- **BIOS:** 1.02 (2025-08-06) — vendor not on LVFS, no automated update path. See [investigations](../investigations/2026-04-21-hubris-crash-loop.md). +- **BIOS:** 1.02 (2025-08-06) — vendor not on LVFS, no automated update path. See [investigations](../../../investigations/2026-04-21-hubris-crash-loop.md). - **Uplink:** `vmbr1` (slave: `eno1`) → SODOLA switch → Fritz!Box 7590. DHCP-reserved `192.168.178.10/24`, gateway `192.168.178.1`. - **Homelab bridge:** `vmbr0` — portless internal bridge, `192.168.8.77/24` + `192.168.8.1/24` alias (LXC default gateway). All 16 LXCs and the HAOS VM are on `vmbr0`. Proxmox routes between `vmbr0` and `vmbr1`; Fritz!Box has a static route `192.168.8.0/24 → 192.168.178.10`. - **WiFi:** disabled 2026-06-02 — `wlp3s0` removed from `/etc/network/interfaces`, wpa config deleted. Was used as a failover to the now-retired Slate AX AP. @@ -46,7 +46,7 @@ Member of `Homelab`, a 2-node Proxmox cluster with [strong](strong.md) it's a physical thinpool that only exists on this host's hardware. - strong currently hosts no LXCs/VMs — it exists solely as a cluster member so far. See [strong.md](strong.md) and the [library-SSD - migration plan](../.hermes/plans/2026-06-03_110000-library-ssd-migration-to-ludo-mini.md) + migration plan](../../../.hermes/plans/2026-06-03_110000-library-ssd-migration-to-ludo-mini.md) for what comes next (physical drive move, service migration — not started). ## Tenants @@ -113,20 +113,20 @@ OpenSSH on `0.0.0.0:22`. Netbird's built-in SSH server is on `100.122.38.109:220 - [Media permissions](../infrastructure/media-permissions.md) - [Monitoring](../infrastructure/monitoring.md) - [Backups (disabled)](../infrastructure/backups.md) -- [Operations cheatsheet](../operations/commands.md) -- [Investigation: 2026-04-21 crash loop](../investigations/2026-04-21-hubris-crash-loop.md) +- [Operations cheatsheet](../../../operations/commands.md) +- [Investigation: 2026-04-21 crash loop](../../../investigations/2026-04-21-hubris-crash-loop.md) - [strong — Proxmox host](strong.md) ## Changelog ### 2026-07-01 — strong joined as a 2nd cluster node ("Homelab") -User reformatted `strong` (formerly a Linux dev workstation, `192.168.178.181`) to Proxmox VE 9.2.3. Cluster/OS hostname on that box is `strong` (left as-is from install). Bootstrapped root SSH on strong from a one-time console password (installed hubris's existing trusted key set: `root@hubris`, `d.toro.v@pm.me`), then generated a keypair on strong and pre-authorized it here (`root@strong`) so `pvecm add 192.168.8.77 --use_ssh 1` (run from strong) could join without an interactive password prompt. No cabling/routing changes needed — strong reaches hubris's corosync address (`192.168.8.77`) via the existing Fritz!Box static route. Cluster now 2 nodes, quorate, **no QDevice** (explicit choice — see [Cluster](#cluster) above for the quorum tradeoff this implies). strong hosts no guests yet; this is Phase 1 of the [library-SSD migration plan](../.hermes/plans/2026-06-03_110000-library-ssd-migration-to-ludo-mini.md), nothing further from that plan has been executed. +User reformatted `strong` (formerly a Linux dev workstation, `192.168.178.181`) to Proxmox VE 9.2.3. Cluster/OS hostname on that box is `strong` (left as-is from install). Bootstrapped root SSH on strong from a one-time console password (installed hubris's existing trusted key set: `root@hubris`, `d.toro.v@pm.me`), then generated a keypair on strong and pre-authorized it here (`root@strong`) so `pvecm add 192.168.8.77 --use_ssh 1` (run from strong) could join without an interactive password prompt. No cabling/routing changes needed — strong reaches hubris's corosync address (`192.168.8.77`) via the existing Fritz!Box static route. Cluster now 2 nodes, quorate, **no QDevice** (explicit choice — see [Cluster](#cluster) above for the quorum tradeoff this implies). strong hosts no guests yet; this is Phase 1 of the [library-SSD migration plan](../../../.hermes/plans/2026-06-03_110000-library-ssd-migration-to-ludo-mini.md), nothing further from that plan has been executed. ### 2026-06-02 — Slate AX retired; SODOLA switch added; network restructured -Replaced GL.iNet Slate AX sub-router with SODOLA 5-Port 2.5Gbit managed switch. Fritz!OS 8.x lacks second-IP-network support on LAN ports, so Proxmox now acts as the subnet router: `vmbr1` (eno1 → SODOLA → Fritz!Box) is the uplink at `192.168.178.10/24`; `vmbr0` is a portless internal bridge holding all LXCs/VMs with `192.168.8.1` as an alias (unchanged LXC gateway). Fritz!Box static route `192.168.8.0/24 → 192.168.178.10` enables inbound routing. No LXC configs changed. Eliminated double-NAT. WiFi (`wlp3s0`) also removed — was pointing at the Slate AX SSID, no longer useful. See [network](../infrastructure/network.md) and [migration plan](../plans/2026-06-01-slate-ax-to-sodola-migration.md). +Replaced GL.iNet Slate AX sub-router with SODOLA 5-Port 2.5Gbit managed switch. Fritz!OS 8.x lacks second-IP-network support on LAN ports, so Proxmox now acts as the subnet router: `vmbr1` (eno1 → SODOLA → Fritz!Box) is the uplink at `192.168.178.10/24`; `vmbr0` is a portless internal bridge holding all LXCs/VMs with `192.168.8.1` as an alias (unchanged LXC gateway). Fritz!Box static route `192.168.8.0/24 → 192.168.178.10` enables inbound routing. No LXC configs changed. Eliminated double-NAT. WiFi (`wlp3s0`) also removed — was pointing at the Slate AX SSID, no longer useful. See [network](../infrastructure/network.md) and [migration plan](../../../plans/2026-06-01-slate-ax-to-sodola-migration.md). ### 2026-05-14 — LXC 109 (syncthing) decommissioned -User destroyed the syncthing LXC (had been stopped since 2026-04-21, never re-enabled). `pct destroy 109 --purge` cleaned `vm-109-disk-0` on `local-lvm` and the `/etc/pve/lxc/109.conf` entry. Data subtree `/mnt/library/syncthing` was already empty and retained as an empty dir. No DNS, Caddy, NFS-export, or claudio-monitor references to clean up. Entry moved to the "recently destroyed" table in [containers/index](../containers/index.md#recently-destroyed-kept-for-archaeology); references stripped from [README](../README.md), [media-permissions](../infrastructure/media-permissions.md), [vms/100-zimaos](../vms/100-zimaos.md), and [containers/102-nfs-export](../containers/102-nfs-export.md). +User destroyed the syncthing LXC (had been stopped since 2026-04-21, never re-enabled). `pct destroy 109 --purge` cleaned `vm-109-disk-0` on `local-lvm` and the `/etc/pve/lxc/109.conf` entry. Data subtree `/mnt/library/syncthing` was already empty and retained as an empty dir. No DNS, Caddy, NFS-export, or claudio-monitor references to clean up. Entry moved to the "recently destroyed" table in [containers/index](../containers/index.md#recently-destroyed-kept-for-archaeology); references stripped from [README](../../../README.md), [media-permissions](../infrastructure/media-permissions.md), [vms/100-zimaos](../vms/100-zimaos.md), and [containers/102-nfs-export](../containers/102-nfs-export.md). ### 2026-05-14 — network performance baseline captured First explicit speed snapshot: WAN ↓113.5 / ↑19.9 Mbit (24.6 ms), `eno1` 1 Gb full-duplex negotiated, intra-host `vmbr0` ~34.7 Gbit/s host↔LXC and ~34.8 Gbit/s LXC↔LXC (single TCP stream, zero retransmits). `iperf3` + `speedtest-cli` installed on host. Noted `eno1` `rx_errors` at 1.62 M (~1.7 % of 96 M RX packets in 14 d uptime) plus 10.9 k `align_errors` — flagged for follow-up; expect to recheck the trend in ~1 week, suspect patch cable / switch port first if still climbing. See new "Network performance baseline" section above. @@ -138,7 +138,7 @@ User destroyed the heaper LXC. No `116.conf.bak` left behind in `/etc/pve/lxc/`. `/etc/sysctl.d/99-bbr.conf` switches `net.ipv4.tcp_congestion_control` from `cubic` to `bbr` and `net.core.default_qdisc` from `fq_codel` to `fq`. Also bumps `rmem_max`/`wmem_max` to 64 MiB and widens `tcp_rmem`/`tcp_wmem`. `tcp_bbr` module pinned at boot via `/etc/modules-load.d/bbr.conf`. Triggered by Nextcloud client downloads from a WiFi laptop pulling ~2 MB/s despite a 152 Mbps link — server-side baseline through Caddy with BBR is ~400 MB/s single-stream loopback, so any client-perceived single-stream improvement is pure congestion-control win. Touches every LXC's outbound TCP since they all share this kernel. ### 2026-04-29 — relocated to better-ventilated spot -User physically moved the host to a new location with improved airflow. Post-move idle baseline (45 min uptime, light load): k10temp Tctl **47.2 °C**, amdgpu edge 42 °C, nvme0 composite 34.9 °C / sensor1 32.9 °C, nvme1 composite 38.9 °C / sensor1 52.9 °C, DRAM 34–35.5 °C, ACPI zone 47–49 °C. Compares well against the 2026-04-23 thermal-pad steady-state (nvme0 sensor1 60–61 °C). Watch the lifetime NVMe warning-time counter over the coming days for confirmation. See [investigation](../investigations/2026-04-21-hubris-crash-loop.md#2026-04-29-physical-relocation). +User physically moved the host to a new location with improved airflow. Post-move idle baseline (45 min uptime, light load): k10temp Tctl **47.2 °C**, amdgpu edge 42 °C, nvme0 composite 34.9 °C / sensor1 32.9 °C, nvme1 composite 38.9 °C / sensor1 52.9 °C, DRAM 34–35.5 °C, ACPI zone 47–49 °C. Compares well against the 2026-04-23 thermal-pad steady-state (nvme0 sensor1 60–61 °C). Watch the lifetime NVMe warning-time counter over the coming days for confirmation. See [investigation](../../../investigations/2026-04-21-hubris-crash-loop.md#2026-04-29-physical-relocation). ### 2026-04-28 — Phase 1 WiFi failover Host now dual-homed: LAN `192.168.8.77` (primary) + WiFi `192.168.8.141` (failover, metric 200) on the GL-AXT1800-714-5G AP. Installed `wpasupplicant`+`iw`; added `wlp3s0` stanza to `/etc/network/interfaces` with `wpa-conf`; ARP isolation sysctls in `post-up`. Built `wan-failover.service` to remove the vmbr0 default route on `eno1` carrier loss, since the bridge's carrier doesn't follow `eno1` (the LXC veths keep it `1`). LXC/VM guests are still LAN-only — Phase 2 will migrate them. @@ -147,10 +147,10 @@ Host now dual-homed: LAN `192.168.8.77` (primary) + WiFi `192.168.8.141` (failov This wiki created. Live state at this date: 14 LXCs running (109 syncthing stopped), 1 VM, kernel `6.14.11-4-pve`, uptime 3 d 0 h post drive-removal A/B test. Compared to memory snapshot from a week ago, **destroyed**: LXC 100 (yunohost arr), 106 (flaresolverr), 107 (marimo), 110 (photoprism), 111 (karakeep), 112 (immich), 115 (reticulum). 100 + 106 destroyed per the planned 2026-04-21 \*arr migration retention; the others removed since. ### 2026-04-23 — SSD cooling + thermal pads installed -Thermal pads on both NVMe drives. Steady-state nvme0 composite 47 °C / sensor1 60–61 °C, nvme1 38–40 °C. Zero new warning-time minutes after install. Watch the lifetime warning-time counter going forward, not absolute sensor1. See [investigation](../investigations/2026-04-21-hubris-crash-loop.md#2026-04-23-thermal-pad-verdict). +Thermal pads on both NVMe drives. Steady-state nvme0 composite 47 °C / sensor1 60–61 °C, nvme1 38–40 °C. Zero new warning-time minutes after install. Watch the lifetime warning-time counter going forward, not absolute sensor1. See [investigation](../../../investigations/2026-04-21-hubris-crash-loop.md#2026-04-23-thermal-pad-verdict). ### 2026-04-22 — drive removal A/B test -Removed external USB backup drive (Silicon Motion `090c:2320`). Disabled the four `backup-library*.timer` units, commented the fstab entry. Goal: confirm whether the drive + UAS interaction on the AMD USB4 PCIe tunnel is the dominant root cause of the silent hard-locks. Pre-drive uptime was 33 days; with drive, repeated crashes despite UAS blacklist + mount-on-demand. **Result so far:** 3+ days uptime — the drive looks like the primary contributor; `cpu-epp` remains as belt-and-suspenders thermal protection. See [investigation](../investigations/2026-04-21-hubris-crash-loop.md). +Removed external USB backup drive (Silicon Motion `090c:2320`). Disabled the four `backup-library*.timer` units, commented the fstab entry. Goal: confirm whether the drive + UAS interaction on the AMD USB4 PCIe tunnel is the dominant root cause of the silent hard-locks. Pre-drive uptime was 33 days; with drive, repeated crashes despite UAS blacklist + mount-on-demand. **Result so far:** 3+ days uptime — the drive looks like the primary contributor; `cpu-epp` remains as belt-and-suspenders thermal protection. See [investigation](../../../investigations/2026-04-21-hubris-crash-loop.md). ### 2026-04-22 — `cpu-epp.service` ordering bug fixed Was `After=multi-user.target` + `WantedBy=multi-user.target` — queued behind `pve-guests.service`, so the hottest boot window (20+ guests starting on `performance`) preceded EPP application. Now `After=sysinit.target` + `Before=pve-guests.service`. @@ -159,4 +159,4 @@ Was `After=multi-user.target` + `WantedBy=multi-user.target` — queued behind ` `60-crash-capture.conf`, softdog `soft_panic=1`, RuntimeWatchdog 15 s. `rasdaemon` installed and enabled. Pure silicon hangs still leave no trace; this catches everything else. ### 2026-04-21 — `cpu-epp.service` deployed -Pinned governor=`powersave`, EPP=`balance_power` at boot. Stopped the host idling at ~95 °C with everything pinned at 4.4 GHz. First fix in the [crash-loop incident](../investigations/2026-04-21-hubris-crash-loop.md). +Pinned governor=`powersave`, EPP=`balance_power` at boot. Stopped the host idling at ~95 °C with everything pinned at 4.4 GHz. First fix in the [crash-loop incident](../../../investigations/2026-04-21-hubris-crash-loop.md). diff --git a/hosts/strong.md b/knowledge/wiki/hosts/strong.md similarity index 95% rename from hosts/strong.md rename to knowledge/wiki/hosts/strong.md index 1778a4b..e3df409 100644 --- a/hosts/strong.md +++ b/knowledge/wiki/hosts/strong.md @@ -28,7 +28,7 @@ Proxmox VE on 2026-07-01. No LXCs/VMs deployed on it yet. was actually created live; not worth renaming), restricted `nodes strong` in `/etc/pve/storage.cfg` — same pattern as hubris's `library` pool. Empty so far; this is separate from the [library-SSD migration - plan](../.hermes/plans/2026-06-03_110000-library-ssd-migration-to-ludo-mini.md)'s + plan](../../../.hermes/plans/2026-06-03_110000-library-ssd-migration-to-ludo-mini.md)'s planned drive move from hubris (that hasn't happened) — this is general-purpose VM/CT capacity. - **Network:** `vmbr0` is bridged straight onto the household LAN — @@ -50,13 +50,13 @@ Proxmox VE on 2026-07-01. No LXCs/VMs deployed on it yet. `bootstrap.sh --no-secrets` (reused the operator's existing Gitea PAT for the initial clone). `/opt/homelab-context`, the `homelab` CLI, and the 5-min sync timer are live; `homelab whoami` resolves correctly. See - [agent-enrollment.md](../operations/agent-enrollment.md). + [agent-enrollment.md](../../../operations/agent-enrollment.md). - **Age key / secrets:** issued the same day over plain LAN (no Netbird needed — see the `--no-mesh` bootstrap.sh fix below). Key lives at `/etc/age/key.txt`; pubkey `age1rtwvdct6avjkr3cyxv3vue3vqx4d524fjfr3vk7xrnvyrylnry5sm54sn4` recorded in `inventory.yaml`. Not yet a recipient on any actual secret (`hello.yaml`, `gitea-pat.yaml`, etc.) — that's a separate grant, see - ["Granting a secret to a new client"](../operations/agent-enrollment.md#granting-a-secret-to-a-new-client). + ["Granting a secret to a new client"](../../../operations/agent-enrollment.md#granting-a-secret-to-a-new-client). ## Cluster membership @@ -76,10 +76,10 @@ needed going forward. ## Related - [hubris — Proxmox host](hubris.md) -- [Library SSD migration plan](../.hermes/plans/2026-06-03_110000-library-ssd-migration-to-ludo-mini.md) — the larger project this is Phase 1 of (filename kept as-is, historical) +- [Library SSD migration plan](../../../.hermes/plans/2026-06-03_110000-library-ssd-migration-to-ludo-mini.md) — the larger project this is Phase 1 of (filename kept as-is, historical) - [Network](../infrastructure/network.md) - [SSH access](../infrastructure/ssh-access.md) -- [Agent enrollment](../operations/agent-enrollment.md) +- [Agent enrollment](../../../operations/agent-enrollment.md) ## Changelog diff --git a/infrastructure/auto-deploy.md b/knowledge/wiki/infrastructure/auto-deploy.md similarity index 97% rename from infrastructure/auto-deploy.md rename to knowledge/wiki/infrastructure/auto-deploy.md index f8fa9e6..f998201 100644 --- a/infrastructure/auto-deploy.md +++ b/knowledge/wiki/infrastructure/auto-deploy.md @@ -44,13 +44,13 @@ The app repo at `/opt/` is the working tree, but the deploy tooling (`web | `dtoro/Homelab-Docs` → homelab-mcp | [apps (105)](../containers/105-apps.md) `/opt/homelab-mcp/` | B | `http://192.168.8.205:9811/deploy` | 10 | reinstalls `homelab-mcp.service` + restart | | `dtoro/Homelab-Docs` → secrets-issuance | [apps (105)](../containers/105-apps.md) `/opt/secrets-issuance/` | B | `http://192.168.8.205:9821/deploy` | 11 | reinstalls `secrets-issuance.service` + restart | | `dtoro/terminalito` | [trmnl (128)](../containers/128-trmnl.md) `/opt/terminalito/` | B | `http://192.168.8.211:9797/deploy` | 12 | reinstalls units + `systemctl restart trmnl-plugins` | -| `dtoro/Homelab-Docs` → oikos-console | [apps (105)](../containers/105-apps.md) `/opt/oikos-console/` | B | `http://192.168.8.205:9831/deploy` | 14 | reinstalls `oikos-console.service` + restart — see [oikos/console/deploy/README.md](../oikos/console/deploy/README.md) | +| `dtoro/Homelab-Docs` → oikos-console | [apps (105)](../containers/105-apps.md) `/opt/oikos-console/` | B | `http://192.168.8.205:9831/deploy` | 14 | reinstalls `oikos-console.service` + restart — see [oikos/console/deploy/README.md](../../../oikos/console/deploy/README.md) | > Note: `dtoro/Homelab-Docs` has **three webhooks** firing on the same push. > Each owns its own clone on LXC 105. They don't conflict because each > deploy.sh only touches its own service unit + venv. -> **Not yet wired:** `dtoro/claudio-monitor` (push, then `/opt/claudio-monitor/scripts/deploy.sh` manually). The former authentik LXC (124) is destroyed — Authentik runs on the [VPS](../hosts/netbird-vps.md). DNS moved to [Technitium on dns (107)](../containers/107-dns.md). +> **Not yet wired:** `dtoro/claudio-monitor` (push, then `/opt/claudio-monitor/scripts/deploy.sh` manually). The former authentik LXC (124) is destroyed — Authentik runs on the [VPS](../../../hosts/netbird-vps.md). DNS moved to [Technitium on dns (107)](../containers/107-dns.md). ## When you change a tracked config @@ -117,7 +117,7 @@ If you're not sure what's already lurking, run `homelab apt-audit --fleet` and l - [Gitea (104)](../containers/104-gitea.md) — webhook source for all of these - [Caddy (121)](../containers/121-caddy.md), [apps (105)](../containers/105-apps.md), [mule-images (120)](../containers/120-mule-images.md), [hubris host](../hosts/hubris.md) — webhook targets - [Backups (disabled)](backups.md) -- [Operations cheatsheet](../operations/commands.md) — `homelab apt-audit` / `homelab apt-upgrade` reference +- [Operations cheatsheet](../../../operations/commands.md) — `homelab apt-audit` / `homelab apt-upgrade` reference ## Changelog diff --git a/infrastructure/backups.md b/knowledge/wiki/infrastructure/backups.md similarity index 94% rename from infrastructure/backups.md rename to knowledge/wiki/infrastructure/backups.md index eba2dd7..99d9bd9 100644 --- a/infrastructure/backups.md +++ b/knowledge/wiki/infrastructure/backups.md @@ -24,7 +24,7 @@ See [132-rclone](../containers/132-rclone.md) for the full design. ## Legacy — restic on external drive (DISABLED 2026-04-22) -Chunked monthly restic backup of `/mnt/library`'s irreplaceable subset. **Disabled 2026-04-22** as part of the [hubris crash-loop A/B test](../investigations/2026-04-21-hubris-crash-loop.md). +Chunked monthly restic backup of `/mnt/library`'s irreplaceable subset. **Disabled 2026-04-22** as part of the [hubris crash-loop A/B test](../../../investigations/2026-04-21-hubris-crash-loop.md). ## Status @@ -36,7 +36,7 @@ Chunked monthly restic backup of `/mnt/library`'s irreplaceable subset. **Disabl Fstab entry commented out. USB drive de-authorized and physically removed. `backup-library-deploy.service` left enabled (harmless webhook receiver). -**Reason:** the host hang recurred 2026-04-22 18:42 after 30h despite the `cpu-epp` fix, the UAS blacklist, and mount-on-demand. User wants to confirm host stability without the drive at all (was stable 33 days before the drive arrived). See [investigation](../investigations/2026-04-21-hubris-crash-loop.md). +**Reason:** the host hang recurred 2026-04-22 18:42 after 30h despite the `cpu-epp` fix, the UAS blacklist, and mount-on-demand. User wants to confirm host stability without the drive at all (was stable 33 days before the drive arrived). See [investigation](../../../investigations/2026-04-21-hubris-crash-loop.md). **To re-enable:** uncomment fstab line, `systemctl enable --now` the four timers, re-attach drive. @@ -111,7 +111,7 @@ Single drive. RECOVERY.md flags the 3-2-1 gap. Mitigations (second drive, cloud The `Silicon Motion Portable SSD` (vid:pid `090c:2320`) drops under sustained heavy writes through a hub chain. Bypass all hubs / use a rear motherboard USB 3 port if attaching it again. -After it was first attached on 2026-04-19, hubris crashed twice in 2.5 days (46h then 12h uptime). Kernel logs ended abruptly with routine apparmor entries — no panic, OOM, or MCE — the classic hard-lock signature. Preceded by `uas_eh_abort_handler` storms and xHCI resets on port 6-1. The UAS blacklist + mount-on-demand mitigations didn't fully eliminate it (recurrence 2026-04-22), prompting drive removal as the cleaner test. See [investigation](../investigations/2026-04-21-hubris-crash-loop.md). +After it was first attached on 2026-04-19, hubris crashed twice in 2.5 days (46h then 12h uptime). Kernel logs ended abruptly with routine apparmor entries — no panic, OOM, or MCE — the classic hard-lock signature. Preceded by `uas_eh_abort_handler` storms and xHCI resets on port 6-1. The UAS blacklist + mount-on-demand mitigations didn't fully eliminate it (recurrence 2026-04-22), prompting drive removal as the cleaner test. See [investigation](../../../investigations/2026-04-21-hubris-crash-loop.md). ## Thermal monitoring @@ -122,7 +122,7 @@ Moved out of this repo to `dtoro/claudio-monitor` on 2026-04-21 (commit `50dc213 - ~~[claudio-bot (123)](../containers/123-claudio-bot.md)~~ (destroyed 2026-06-04) - [Monitoring](monitoring.md) - [Auto-deploy](auto-deploy.md) -- [Investigation: 2026-04-21 crash loop](../investigations/2026-04-21-hubris-crash-loop.md) +- [Investigation: 2026-04-21 crash loop](../../../investigations/2026-04-21-hubris-crash-loop.md) ## Changelog @@ -133,7 +133,7 @@ Off-host backup moved to a plain `rclone sync` mirror on the new [LXC 132 `rclon Initial documentation. Status remains DISABLED. ### 2026-04-22 — DISABLED -Drive removed as the A/B test in the [crash investigation](../investigations/2026-04-21-hubris-crash-loop.md). Timers disabled, fstab commented, drive de-authorized. +Drive removed as the A/B test in the [crash investigation](../../../investigations/2026-04-21-hubris-crash-loop.md). Timers disabled, fstab commented, drive de-authorized. ### 2026-04-21 — UAS blacklist + mount-on-demand shipped; root-caused host hangs to drive Drive identified as the source of the hangs after hubris crashed twice in 2.5 days. UAS blacklist forces BOT; helper script toggles `/sys/bus/usb/.../authorized` so the drive is de-authorized when not backing up. Recovery drill (restore 188KB PDF + hash compare) had passed earlier. Bug fixed in `backup-library.sh`: `python3 -c '…' KEY=VAL` does NOT pass env vars — env-var prefix must precede the command. Caused false-failure even after successful backups. diff --git a/infrastructure/dns.md b/knowledge/wiki/infrastructure/dns.md similarity index 89% rename from infrastructure/dns.md rename to knowledge/wiki/infrastructure/dns.md index 011f7ee..c64eccd 100644 --- a/infrastructure/dns.md +++ b/knowledge/wiki/infrastructure/dns.md @@ -7,7 +7,7 @@ There is **no wildcard on the LAN side**. Every subdomain needs an explicit entr ## Components - **Authoritative public DNS:** IONOS. `*.hubris.network → 82.165.190.79` (was `74.118.126.4` until 2026-04-22). -- **LAN authoritative for `hubris.network` records:** [Technitium DNS](https://technitium.com) on [dns (107)](../containers/107-dns.md) at `192.168.8.2:53`. Syncs A records to the NetBird managed DNS zone via cron (see [dns-sync.py](../scripts/dns-sync.py)). Formerly dnsmasq on [authentik (124)](../containers/124-authentik.md) (decommissioned 2026-06-04). +- **LAN authoritative for `hubris.network` records:** [Technitium DNS](https://technitium.com) on [dns (107)](../containers/107-dns.md) at `192.168.8.2:53`. Syncs A records to the NetBird managed DNS zone via cron (see [dns-sync.py](../../../scripts/dns-sync.py)). Formerly dnsmasq on [authentik (124)](../containers/124-authentik.md) (decommissioned 2026-06-04). - **PVE host** (`192.168.8.77`): resolver is the local Netbird daemon at `100.122.38.109:53`, which forwards to the LAN/upstream and learns hubris.network answers via that path. `netbird status` says "Nameservers: 0/0 Available" — confirming netbird does NOT manage a hubris.network zone; it just caches whatever the system resolver returns. - **Some LXCs** keep router DNS (`192.168.8.1`) or Tailscale MagicDNS (`100.100.100.100`), both of which return the public IONOS A record. Those LXCs need either a `/etc/hosts` override or local dnsmasq — see [mesh migration](mesh.md) for which technique applies where. @@ -53,7 +53,7 @@ Creating a new Caddyfile site block is necessary but **not sufficient**. Without 3. Verify: `dig @192.168.8.2 +short .hubris.network` → `192.168.8.175`. 4. On macOS clients, flush: `sudo dscacheutil -flushcache && sudo killall -HUP mDNSResponder`. -> The Technitium config on LXC 107 is the single source of truth. Never hand-edit the NetBird managed zone directly — the [`scripts/dns-sync.py`](../scripts/dns-sync.py) cron on 107 reconciles them and reaps stale records. See [dns.md changelog 2026-06-03](#2026-06-03--single-authoring-source-technitium--netbird-managed-zone-sync). +> The Technitium config on LXC 107 is the single source of truth. Never hand-edit the NetBird managed zone directly — the [`scripts/dns-sync.py`](../../../scripts/dns-sync.py) cron on 107 reconciles them and reaps stale records. See [dns.md changelog 2026-06-03](#2026-06-03--single-authoring-source-technitium--netbird-managed-zone-sync). ## Public path — what does and doesn't follow the LAN map @@ -94,7 +94,7 @@ The "delete NetBird managed zone → forward everything to Technitium" plan was **Cleanup done same day:** removed the inert Mac-Mini Technitium secondary (mesh-only, served nobody); reverted the primary's `zoneTransfer=Allow`; fixed `home-lab-dns` group → `[192.168.8.2]` (dropped the self-referencing Mac IP → now `1/1 Available`); deleted the vestigial `Proxmox Names` group. -> Reference: [scripts/dns-sync.py](../scripts/dns-sync.py). The sync's source of truth is Technitium; it **deletes** NetBird records absent from Technitium (so obsolete names like `files`, `photos-new` get reaped). +> Reference: [scripts/dns-sync.py](../../../scripts/dns-sync.py). The sync's source of truth is Technitium; it **deletes** NetBird records absent from Technitium (so obsolete names like `files`, `photos-new` get reaped). ### 2026-06-06 — dns-sync cron finally installed (had been dormant since 2026-06-04 deployment) The `dns-sync.py` script on LXC 107 had been placed at `/opt/dns-sync/sync.py` on 2026-06-04 but **no crontab was configured** — the sync had never run automatically. The NetBird managed DNS zone was only in sync because manual runs happened during incident debugging. @@ -110,7 +110,7 @@ All LXCs that Caddy reverse-proxies to by IP were on `ip=dhcp` and could float o Split-horizon DNS moved off [124](../containers/124-authentik.md) to a dedicated **Technitium** LXC at **`192.168.8.2`** (zone: specific A overrides + wildcard→VPS + replicated MX/SPF/CAA). NetBird `home-lab-dns` nameserver group cut over to `192.168.8.2` (with `.180` as a now-dead fallback). dnsmasq stopped, all names verified via Technitium, **LXC 124 shut down**. **Caveat:** the [NetBird managed DNS zone](../containers/124-authentik.md) still answers most app names *directly* (bypassing the nameserver group) — three overlapping DNS sources remain; see the single-source-of-truth decision (Phase 4). **Action needed:** update router DHCP DNS from the dead `.180` → `192.168.8.2` for any plain-LAN (non-mesh) clients. ### 2026-05-31 — `auth.hubris.network` re-pointed to the VPS (`82.165.190.79`) -Authentik migrated off LXC 124 onto the VPS (see [investigation](../investigations/2026-05-31-authentik-vps-migration.md)). The dnsmasq entry changed from `192.168.8.175` (home Caddy) to `82.165.190.79` (VPS traefik). This is the first LAN entry that intentionally points at the VPS rather than Caddy — `auth` is now a genuinely public service served directly from the VPS. **Gotcha logged:** the NetBird per-client resolver (`100.122.255.254`) caches dnsmasq answers and does **not** clear on `netbird down/up`; clients needed `/etc/hosts` overrides or `resolvectl flush-caches` to pick up the change. Since the service is now fully public, the long-term cleaner option is to drop the override entirely and let it fall through to the IONOS wildcard (which also points at the VPS). +Authentik migrated off LXC 124 onto the VPS (see [investigation](../../../investigations/2026-05-31-authentik-vps-migration.md)). The dnsmasq entry changed from `192.168.8.175` (home Caddy) to `82.165.190.79` (VPS traefik). This is the first LAN entry that intentionally points at the VPS rather than Caddy — `auth` is now a genuinely public service served directly from the VPS. **Gotcha logged:** the NetBird per-client resolver (`100.122.255.254`) caches dnsmasq answers and does **not** clear on `netbird down/up`; clients needed `/etc/hosts` overrides or `resolvectl flush-caches` to pick up the change. Since the service is now fully public, the long-term cleaner option is to drop the override entirely and let it fall through to the IONOS wildcard (which also points at the VPS). ### 2026-05-14 — `nfs-export.hubris.network` added (direct, non-HTTP) NFSv4 export server [nfs-export (102)](../containers/102-nfs-export.md) at `192.168.8.200`. Direct entry, not Caddy-fronted — NFS is L4, no HTTP reverse-proxy meaningful. diff --git a/infrastructure/homelab-context.md b/knowledge/wiki/infrastructure/homelab-context.md similarity index 97% rename from infrastructure/homelab-context.md rename to knowledge/wiki/infrastructure/homelab-context.md index fb58481..a0b76c9 100644 --- a/infrastructure/homelab-context.md +++ b/knowledge/wiki/infrastructure/homelab-context.md @@ -5,7 +5,7 @@ Code, Hermes Agent, future MCP-capable clients) on every machine in the lab self-locating and able to read the same source of truth. Operational walkthrough for enrolling a new client lives in -[operations/agent-enrollment.md](../operations/agent-enrollment.md); this +[operations/agent-enrollment.md](../../../operations/agent-enrollment.md); this page is the architecture reference. ## What's where @@ -121,7 +121,7 @@ The MCP server and secrets-issuance each have their own clone ## Related -- [Operations: agent enrollment](../operations/agent-enrollment.md) — the +- [Operations: agent enrollment](../../../operations/agent-enrollment.md) — the step-by-step for adding a new client - [Auto-deploy](auto-deploy.md) — the `homelab-mcp` + `secrets-issuance` pipelines (and the rest of the lab's webhook pipelines) @@ -133,7 +133,7 @@ The MCP server and secrets-issuance each have their own clone ## Changelog ### 2026-05-20 — system live across hubris, apps, republic-laptop -Phase 1 of the [cross-client context plan](../README.md) merged. Three +Phase 1 of the [cross-client context plan](../../../README.md) merged. Three clients enrolled end-to-end: PAT-based bootstrap, age-key issuance, SOPS decrypt verified on each. Webhook auto-deploy for both LXC 105 services wired (hook ids 10 + 11). `homelab refresh-creds` + atomic diff --git a/infrastructure/index.md b/knowledge/wiki/infrastructure/index.md similarity index 94% rename from infrastructure/index.md rename to knowledge/wiki/infrastructure/index.md index 3b86737..bb451f1 100644 --- a/infrastructure/index.md +++ b/knowledge/wiki/infrastructure/index.md @@ -60,7 +60,7 @@ are documented in their own pages. Each system below links to its full doc. ## Related -- [README](../README.md) — entry point +- [README](../../../README.md) — entry point - [Containers index](../containers/index.md) -- [Operations cheatsheet](../operations/commands.md) -- [OIKOS operating model](../OIKOS.md) \ No newline at end of file +- [Operations cheatsheet](../../../operations/commands.md) +- [OIKOS operating model](../../../OIKOS.md) \ No newline at end of file diff --git a/infrastructure/ingress.md b/knowledge/wiki/infrastructure/ingress.md similarity index 90% rename from infrastructure/ingress.md rename to knowledge/wiki/infrastructure/ingress.md index cb06518..75e6c5f 100644 --- a/infrastructure/ingress.md +++ b/knowledge/wiki/infrastructure/ingress.md @@ -49,7 +49,7 @@ LAN clients resolve via the [Technitium DNS on dns (107)](dns.md) → `192.168.8 ### `auth.hubris.network` — different pattern (local container, not cert-mirror) -Since 2026-05-31 [Authentik runs on the VPS itself](../investigations/2026-05-31-authentik-vps-migration.md), so `auth.hubris.network` is served by a **local Docker container**, not proxied to a home backend. It therefore does **not** use the file-provider + cert-mirror pattern above: +Since 2026-05-31 [Authentik runs on the VPS itself](../../../investigations/2026-05-31-authentik-vps-migration.md), so `auth.hubris.network` is served by a **local Docker container**, not proxied to a home backend. It therefore does **not** use the file-provider + cert-mirror pattern above: - Routed via traefik **Docker provider labels** on the `authentik-server` service (`/opt/docker-compose.yml`), not `traefik-dynamic.yaml`. - TLS via traefik's own `letsencrypt` resolver (works here because it's a normal HTTP router, not the HostSNI passthrough). @@ -91,7 +91,7 @@ No cert-mirror entry and no `hubris-public-cert-sync.sh` mapping is needed for ` TRMNL plugins middleware on [trmnl (128)](../containers/128-trmnl.md). File-provider router `trmnl-public` → `192.168.8.211:9851`, `trmnl-ratelimit` (20 rps / 40 burst), cert mirrored as `trmnl.fullchain.crt`/`trmnl.privkey.key`. Verified live from the internet (200 with token / 401 without). It was provisioned during a mesh outage — the `home-lab-network` (192.168.8.0/24) route had no active routing peer because the **mac-mini routing peer's netbird was down** (all home-backed public services 504'd). Bringing netbird up on mac-mini restored the route; no traefik change was needed. ### 2026-05-31 — `auth.hubris.network` now served locally on the VPS -Authentik migrated onto the VPS ([investigation](../investigations/2026-05-31-authentik-vps-migration.md)). Unlike the home-backed services above, `auth` is a local container routed via traefik Docker-provider labels with traefik-managed Let's Encrypt — no cert-mirror, no `traefik-dynamic.yaml` router. Admin UI gated by an ipAllowList middleware. Traefik gained a second Docker network (`auth`, `172.30.1.0/24`) to reach it while keeping its DB/Redis isolated from the netbird stack. +Authentik migrated onto the VPS ([investigation](../../../investigations/2026-05-31-authentik-vps-migration.md)). Unlike the home-backed services above, `auth` is a local container routed via traefik Docker-provider labels with traefik-managed Let's Encrypt — no cert-mirror, no `traefik-dynamic.yaml` router. Admin UI gated by an ipAllowList middleware. Traefik gained a second Docker network (`auth`, `172.30.1.0/24`) to reach it while keeping its DB/Redis isolated from the netbird stack. ### 2026-04-28 — wiki entry created Initial documentation. diff --git a/infrastructure/media-permissions.md b/knowledge/wiki/infrastructure/media-permissions.md similarity index 100% rename from infrastructure/media-permissions.md rename to knowledge/wiki/infrastructure/media-permissions.md diff --git a/infrastructure/mesh.md b/knowledge/wiki/infrastructure/mesh.md similarity index 99% rename from infrastructure/mesh.md rename to knowledge/wiki/infrastructure/mesh.md index 1d7d78b..52711cc 100644 --- a/infrastructure/mesh.md +++ b/knowledge/wiki/infrastructure/mesh.md @@ -117,7 +117,7 @@ Recipe for container-config changes (e.g. adding `extra_hosts`) on Portainer-man ## Changelog ### 2026-05-31 (later) — Authentik moved to the VPS; mesh-dependency for auth eliminated (supersedes the band-aid below) -The earlier same-day fix routed `auth.hubris.network` through VPS Traefik → Caddy → LXC 124 **over the mesh**. That restored service but re-created the original fragility: if the mesh is dark when management restarts, the `192.168.8.175` backend is unreachable and management crash-loops again (the "Bootstrap note" in the entry below). That note is now **obsolete** — Authentik was migrated onto the VPS itself, so OIDC no longer touches the mesh. The `auth-authentik` → `192.168.8.175` route and its `skip-verify` transport were removed from `/opt/traefik-dynamic.yaml`; `auth.hubris.network` is now served by a local `authentik-server` container via Traefik Docker-provider labels, and netbird-mgmt has `depends_on: authentik-server: condition: service_healthy`. The socat / reverse-SSH bootstrap dance is no longer needed. Full detail: [2026-05-31 Authentik VPS migration](../investigations/2026-05-31-authentik-vps-migration.md). +The earlier same-day fix routed `auth.hubris.network` through VPS Traefik → Caddy → LXC 124 **over the mesh**. That restored service but re-created the original fragility: if the mesh is dark when management restarts, the `192.168.8.175` backend is unreachable and management crash-loops again (the "Bootstrap note" in the entry below). That note is now **obsolete** — Authentik was migrated onto the VPS itself, so OIDC no longer touches the mesh. The `auth-authentik` → `192.168.8.175` route and its `skip-verify` transport were removed from `/opt/traefik-dynamic.yaml`; `auth.hubris.network` is now served by a local `authentik-server` container via Traefik Docker-provider labels, and netbird-mgmt has `depends_on: authentik-server: condition: service_healthy`. The socat / reverse-SSH bootstrap dance is no longer needed. Full detail: [2026-05-31 Authentik VPS migration](../../../investigations/2026-05-31-authentik-vps-migration.md). ### 2026-05-31 — Netbird mesh recovered; auth.hubris.network exposed via VPS Traefik diff --git a/infrastructure/monitoring.md b/knowledge/wiki/infrastructure/monitoring.md similarity index 100% rename from infrastructure/monitoring.md rename to knowledge/wiki/infrastructure/monitoring.md diff --git a/infrastructure/network.md b/knowledge/wiki/infrastructure/network.md similarity index 94% rename from infrastructure/network.md rename to knowledge/wiki/infrastructure/network.md index 54ded7b..fb94a5d 100644 --- a/infrastructure/network.md +++ b/knowledge/wiki/infrastructure/network.md @@ -64,7 +64,7 @@ No NAT on Proxmox — traffic flows without double-NAT. ## Remote access -- **NetBird mesh** — primary path for remote administration. Authenticated via [Authentik on the VPS](../vps/). +- **NetBird mesh** — primary path for remote administration. Authenticated via [Authentik on the VPS](../../../vps/). - **Tailscale** — legacy, being phased out. See [mesh.md](mesh.md). ## Related @@ -79,10 +79,10 @@ No NAT on Proxmox — traffic flows without double-NAT. ### 2026-06-17 — Fritz!Box DNSv4 server set to Technitium (192.168.8.2) Household LAN clients (192.168.178.x) now resolve `*.hubris.network` to LAN IPs. Configured in Fritz!Box at Internet → Filter → DNS Server → DNSv4 Server → "Use other DNSv4 servers" → Preferred = `192.168.8.2`. No per-device or Netbird setup needed. -Previous pool `.100–.240` overlapped with all static LXCs/VMs (` .101–.239`), creating IP conflict risk (DHCP could hand out an IP that a static service expects). Shrunk pool to `.241–.254` via Technitium API. No services re-IP'd. 11 stale DHCP leases in `.101–.110` will expire naturally. **Open:** ZimaOS (VM 100) holds DHCP lease `.103` but inventory expects `.195` — needs static IP set inside VM. See [plan](../plans/2026-06-03-dhcp-pool-exclude-static-ips.md). +Previous pool `.100–.240` overlapped with all static LXCs/VMs (` .101–.239`), creating IP conflict risk (DHCP could hand out an IP that a static service expects). Shrunk pool to `.241–.254` via Technitium API. No services re-IP'd. 11 stale DHCP leases in `.101–.110` will expire naturally. **Open:** ZimaOS (VM 100) holds DHCP lease `.103` but inventory expects `.195` — needs static IP set inside VM. See [plan](../../../plans/2026-06-03-dhcp-pool-exclude-static-ips.md). ### 2026-06-02 — Executed migration; Proxmox as subnet router -Fritz!OS 8.x does not support second IP networks on LAN ports, so the final design uses Proxmox as the router: `vmbr1` (eno1 → SODOLA → Fritz!Box) is the uplink at `192.168.178.10`; `vmbr0` is a portless internal bridge with `192.168.8.1` alias as the LXC gateway. Technitium DHCP enabled for `192.168.8.100–240`. Caddy service unit was missing and recreated. See [migration plan](../plans/2026-06-01-slate-ax-to-sodola-migration.md). +Fritz!OS 8.x does not support second IP networks on LAN ports, so the final design uses Proxmox as the router: `vmbr1` (eno1 → SODOLA → Fritz!Box) is the uplink at `192.168.178.10`; `vmbr0` is a portless internal bridge with `192.168.8.1` alias as the LXC gateway. Technitium DHCP enabled for `192.168.8.100–240`. Caddy service unit was missing and recreated. See [migration plan](../../../plans/2026-06-01-slate-ax-to-sodola-migration.md). ### 2026-06-01 — Initial network doc; Slate AX retired; SODOLA switch added -Replaced the GL.iNet Slate AX sub-router with the SODOLA 5-Port 2.5Gbit managed switch. Eliminated double-NAT. See [migration plan](../plans/2026-06-01-slate-ax-to-sodola-migration.md). +Replaced the GL.iNet Slate AX sub-router with the SODOLA 5-Port 2.5Gbit managed switch. Eliminated double-NAT. See [migration plan](../../../plans/2026-06-01-slate-ax-to-sodola-migration.md). diff --git a/infrastructure/ssh-access.md b/knowledge/wiki/infrastructure/ssh-access.md similarity index 98% rename from infrastructure/ssh-access.md rename to knowledge/wiki/infrastructure/ssh-access.md index 44c05eb..fa8ea73 100644 --- a/infrastructure/ssh-access.md +++ b/knowledge/wiki/infrastructure/ssh-access.md @@ -178,8 +178,8 @@ done - [Mesh migration](mesh.md) - [VPS hardening](vps-hardening.md) -- [Agent enrollment](../operations/agent-enrollment.md) -- [Homelab CLI](../bin/homelab) +- [Agent enrollment](../../../operations/agent-enrollment.md) +- [Homelab CLI](../../../bin/homelab) ## Changelog diff --git a/infrastructure/topology.md b/knowledge/wiki/infrastructure/topology.md similarity index 95% rename from infrastructure/topology.md rename to knowledge/wiki/infrastructure/topology.md index ee1764d..5bb1a9d 100644 --- a/infrastructure/topology.md +++ b/knowledge/wiki/infrastructure/topology.md @@ -3,8 +3,8 @@ # Topology (generated) -Source: [inventory.yaml](../inventory.yaml) — 2 hypervisors, 20 LXCs, 2 VMs, 2 workstations, 18 services. -Edge semantics: [oikos/ontology.yaml](../oikos/ontology.yaml). Operating model: [OIKOS.md](../OIKOS.md). +Source: [inventory.yaml](../../../inventory.yaml) — 2 hypervisors, 20 LXCs, 2 VMs, 2 workstations, 18 services. +Edge semantics: [oikos/ontology.yaml](../../../oikos/ontology.yaml). Operating model: [OIKOS.md](../../../.agents/OIKOS.md). ## Compute & ingress diff --git a/infrastructure/vps-hardening.md b/knowledge/wiki/infrastructure/vps-hardening.md similarity index 100% rename from infrastructure/vps-hardening.md rename to knowledge/wiki/infrastructure/vps-hardening.md diff --git a/vms/100-zimaos.md b/knowledge/wiki/vms/100-zimaos.md similarity index 99% rename from vms/100-zimaos.md rename to knowledge/wiki/vms/100-zimaos.md index 54a7959..ac58ac6 100644 --- a/vms/100-zimaos.md +++ b/knowledge/wiki/vms/100-zimaos.md @@ -59,7 +59,7 @@ The alternative (dedicated virtual data disk on the `library` lvmthin pool, e.g. ## Changelog ### 2026-06-03 — Static IP set to `.195`; DHCP drift fixed -ZimaOS had drifted from `.195` (Slate AX DHCP) → `.103` (Technitium DHCP), causing Caddy 502s. Injected `/etc/systemd/network/10-static.network` into overlay (match `en*/eth*`, address `192.168.8.195/24`, gateway `.1`, DNS `.2`). VM restarted; verified reachable at `.195`. Caddy (`zimaos.hubris.network`) now returns 200. See [plan](../plans/2026-06-03-dhcp-pool-exclude-static-ips.md). +ZimaOS had drifted from `.195` (Slate AX DHCP) → `.103` (Technitium DHCP), causing Caddy 502s. Injected `/etc/systemd/network/10-static.network` into overlay (match `en*/eth*`, address `192.168.8.195/24`, gateway `.1`, DNS `.2`). VM restarted; verified reachable at `.195`. Caddy (`zimaos.hubris.network`) now returns 200. See [plan](../../../plans/2026-06-03-dhcp-pool-exclude-static-ips.md). ### 2026-05-15 — NFS mount relocated to `/media/library` (UI delete fix) diff --git a/vms/108-haos.md b/knowledge/wiki/vms/108-haos.md similarity index 100% rename from vms/108-haos.md rename to knowledge/wiki/vms/108-haos.md diff --git a/vms/index.md b/knowledge/wiki/vms/index.md similarity index 94% rename from vms/index.md rename to knowledge/wiki/vms/index.md index ad649c1..f61daae 100644 --- a/vms/index.md +++ b/knowledge/wiki/vms/index.md @@ -11,4 +11,4 @@ Two QEMU VMs running on [hubris](../hosts/hubris.md): - [Hubris host](../hosts/hubris.md) — both VMs run here - [Containers index](../containers/index.md) — LXCs on both nodes -- [README](../README.md) \ No newline at end of file +- [README](../../../README.md) \ No newline at end of file diff --git a/oikos/approve.py b/oikos/approve.py index a10fd47..89449b5 100644 --- a/oikos/approve.py +++ b/oikos/approve.py @@ -5,7 +5,7 @@ Repo-side half of the Week-3 approval flow. This module owns the request/ grant lifecycle and the HMAC signing; it does NOT talk to Matrix directly. There is no dedicated Matrix bot in this homelab — alerts already go out as the operator's own Hermes agent posting to @dtoro:avispero (see -infrastructure/monitoring.md's homelab-health-watchdog). The integration +knowledge/wiki/infrastructure/monitoring.md's homelab-health-watchdog). The integration contract is: 1. An agent or the Week-3 scheduler calls `request()` (or the CLI diff --git a/oikos/cards/host-apps.md b/oikos/cards/host-apps.md index 038fa80..4afe8ac 100644 --- a/oikos/cards/host-apps.md +++ b/oikos/cards/host-apps.md @@ -6,7 +6,7 @@ - role: docker-apps - address: 192.168.8.205 (mesh: tailscale:apps) - mounts: /mnt/library -- doc: containers/105-apps.md +- doc: knowledge/wiki/containers/105-apps.md - secrets: enrolled (age key present) ## Blast radius @@ -18,4 +18,4 @@ - see the services this host runs for action-level risk classes ## Recent changes -- (none yet) +- 2026-07-06T11:57:21+00:00 deploy-oikos-console (config_mutation) — ok diff --git a/oikos/cards/host-arriman.md b/oikos/cards/host-arriman.md index f641fa5..5db5481 100644 --- a/oikos/cards/host-arriman.md +++ b/oikos/cards/host-arriman.md @@ -6,7 +6,7 @@ - role: arr-stack - address: 192.168.8.245 (mesh: tailscale:arr) - mounts: /mnt/media_local -- doc: containers/122-arriman.md +- doc: knowledge/wiki/containers/122-arriman.md ## Blast radius - impacts: service:arr_stack diff --git a/oikos/cards/host-auth-outpost.md b/oikos/cards/host-auth-outpost.md index b450032..525c654 100644 --- a/oikos/cards/host-auth-outpost.md +++ b/oikos/cards/host-auth-outpost.md @@ -5,7 +5,7 @@ - runs-on: host:hubris - role: authentik-gateway - address: 192.168.8.6 -- doc: containers/106-auth-outpost.md +- doc: knowledge/wiki/containers/106-auth-outpost.md ## Blast radius - impacts: (none) diff --git a/oikos/cards/host-caddy.md b/oikos/cards/host-caddy.md index 73a53a3..ee80bca 100644 --- a/oikos/cards/host-caddy.md +++ b/oikos/cards/host-caddy.md @@ -5,7 +5,7 @@ - runs-on: host:hubris - role: reverse-proxy - address: 192.168.8.175 -- doc: containers/121-caddy.md +- doc: knowledge/wiki/containers/121-caddy.md ## Blast radius - impacts: service:caddy diff --git a/oikos/cards/host-dns.md b/oikos/cards/host-dns.md index 7157d47..0b76c3f 100644 --- a/oikos/cards/host-dns.md +++ b/oikos/cards/host-dns.md @@ -5,7 +5,7 @@ - runs-on: host:hubris - role: dns-server - address: 192.168.8.2 -- doc: containers/107-dns.md +- doc: knowledge/wiki/containers/107-dns.md ## Blast radius - impacts: service:dns @@ -16,4 +16,4 @@ - see the services this host runs for action-level risk classes ## Recent changes -- (none yet) +- 2026-07-06T11:40:28+00:00 add-record (config_mutation) — ok diff --git a/oikos/cards/host-elementsynapse.md b/oikos/cards/host-elementsynapse.md index 461ace3..6509c96 100644 --- a/oikos/cards/host-elementsynapse.md +++ b/oikos/cards/host-elementsynapse.md @@ -5,7 +5,7 @@ - runs-on: host:strong - role: matrix-server - address: 192.168.8.242 -- doc: containers/118-elementsynapse.md +- doc: knowledge/wiki/containers/118-elementsynapse.md ## Blast radius - impacts: service:matrix diff --git a/oikos/cards/host-gitea.md b/oikos/cards/host-gitea.md index ad600a6..a77eb7f 100644 --- a/oikos/cards/host-gitea.md +++ b/oikos/cards/host-gitea.md @@ -6,7 +6,7 @@ - role: git-server - address: 192.168.8.121 (mesh: tailscale:gitea) - mounts: /mnt/library -- doc: containers/104-gitea.md +- doc: knowledge/wiki/containers/104-gitea.md ## Blast radius - impacts: service:gitea diff --git a/oikos/cards/host-grimmory.md b/oikos/cards/host-grimmory.md index d6a0c72..706377c 100644 --- a/oikos/cards/host-grimmory.md +++ b/oikos/cards/host-grimmory.md @@ -6,7 +6,7 @@ - role: book-library - address: 192.168.8.247 - mounts: /mnt/media_local -- doc: containers/130-grimmory.md +- doc: knowledge/wiki/containers/130-grimmory.md - secrets: enrolled (age key present) ## Blast radius diff --git a/oikos/cards/host-haos.md b/oikos/cards/host-haos.md index ada4e76..45f2aba 100644 --- a/oikos/cards/host-haos.md +++ b/oikos/cards/host-haos.md @@ -5,7 +5,7 @@ - runs-on: host:hubris - role: home-automation - address: 192.168.8.101 (mesh: tailscale:homeassistant) -- doc: vms/108-haos.md +- doc: knowledge/wiki/vms/108-haos.md ## Blast radius - impacts: service:haos diff --git a/oikos/cards/host-house.md b/oikos/cards/host-house.md index 3ad239e..5f3808c 100644 --- a/oikos/cards/host-house.md +++ b/oikos/cards/host-house.md @@ -5,7 +5,7 @@ - runs-on: host:strong - role: family-planner - address: 192.168.8.244 -- doc: containers/129-house.md +- doc: knowledge/wiki/containers/129-house.md - secrets: enrolled (age key present) ## Blast radius diff --git a/oikos/cards/host-hubris.md b/oikos/cards/host-hubris.md index 9ae7e5d..57236b6 100644 --- a/oikos/cards/host-hubris.md +++ b/oikos/cards/host-hubris.md @@ -5,7 +5,7 @@ - role: hypervisor - address: 192.168.8.77 (mesh: netbird:proxmox-server.netbird.selfhosted) - mounts: /mnt/library -- doc: hosts/hubris.md +- doc: knowledge/wiki/hosts/hubris.md - secrets: enrolled (age key present) ## Blast radius diff --git a/oikos/cards/host-jellyfin.md b/oikos/cards/host-jellyfin.md index 162b908..178c58d 100644 --- a/oikos/cards/host-jellyfin.md +++ b/oikos/cards/host-jellyfin.md @@ -6,7 +6,7 @@ - role: media-server - address: 192.168.8.246 (mesh: tailscale:jellyfin) - mounts: /mnt/media_local -- doc: containers/101-jellyfin.md +- doc: knowledge/wiki/containers/101-jellyfin.md ## Blast radius - impacts: service:jellyfin diff --git a/oikos/cards/host-mule-images.md b/oikos/cards/host-mule-images.md index 242f193..c1c98ca 100644 --- a/oikos/cards/host-mule-images.md +++ b/oikos/cards/host-mule-images.md @@ -6,7 +6,7 @@ - role: photo-management - address: 192.168.8.136 (mesh: tailscale:muleimage) - mounts: /mnt/library -- doc: containers/120-mule-images.md +- doc: knowledge/wiki/containers/120-mule-images.md ## Blast radius - impacts: service:photos diff --git a/oikos/cards/host-nextcloud.md b/oikos/cards/host-nextcloud.md index f3de0e1..2cd8e3b 100644 --- a/oikos/cards/host-nextcloud.md +++ b/oikos/cards/host-nextcloud.md @@ -6,7 +6,7 @@ - role: file-sync - address: 192.168.8.224 (mesh: tailscale:nextcloud) - mounts: /mnt/library -- doc: containers/114-nextcloud.md +- doc: knowledge/wiki/containers/114-nextcloud.md ## Blast radius - impacts: service:nextcloud diff --git a/oikos/cards/host-nfs-export.md b/oikos/cards/host-nfs-export.md index 53102e9..3731f13 100644 --- a/oikos/cards/host-nfs-export.md +++ b/oikos/cards/host-nfs-export.md @@ -5,7 +5,7 @@ - runs-on: host:hubris - role: storage-export - address: 192.168.8.200 -- doc: containers/102-nfs-export.md +- doc: knowledge/wiki/containers/102-nfs-export.md ## Blast radius - impacts: (none) diff --git a/oikos/cards/host-paperless.md b/oikos/cards/host-paperless.md index f21f1ab..f993551 100644 --- a/oikos/cards/host-paperless.md +++ b/oikos/cards/host-paperless.md @@ -6,7 +6,7 @@ - role: document-archive - address: 192.168.8.130 (mesh: tailscale:paperless) - mounts: /mnt/library -- doc: containers/103-paperless.md +- doc: knowledge/wiki/containers/103-paperless.md ## Blast radius - impacts: service:paperless diff --git a/oikos/cards/host-romm.md b/oikos/cards/host-romm.md index 6e9d58b..31fd618 100644 --- a/oikos/cards/host-romm.md +++ b/oikos/cards/host-romm.md @@ -6,7 +6,7 @@ - role: rom-manager - address: 192.168.8.249 - mounts: /mnt/media_local -- doc: containers/134-romm.md +- doc: knowledge/wiki/containers/134-romm.md ## Blast radius - impacts: (none) diff --git a/oikos/cards/host-seanime.md b/oikos/cards/host-seanime.md index d2d58eb..155d991 100644 --- a/oikos/cards/host-seanime.md +++ b/oikos/cards/host-seanime.md @@ -6,7 +6,7 @@ - role: anime-media-server - address: 192.168.8.248 - mounts: /mnt/media_local/anime -- doc: containers/133-seanime.md +- doc: knowledge/wiki/containers/133-seanime.md ## Blast radius - impacts: (none) diff --git a/oikos/cards/host-sophia.md b/oikos/cards/host-sophia.md index 505ebef..196efef 100644 --- a/oikos/cards/host-sophia.md +++ b/oikos/cards/host-sophia.md @@ -6,7 +6,7 @@ - role: workshop - address: 192.168.8.109 (mesh: tailscale:sophia) - mounts: /mnt/library -- doc: containers/119-sophia.md +- doc: knowledge/wiki/containers/119-sophia.md ## Blast radius - impacts: (none) diff --git a/oikos/cards/host-strong.md b/oikos/cards/host-strong.md index 2ada6c4..9b921c1 100644 --- a/oikos/cards/host-strong.md +++ b/oikos/cards/host-strong.md @@ -4,7 +4,7 @@ - state: active - role: hypervisor - address: 192.168.178.181 -- doc: hosts/strong.md +- doc: knowledge/wiki/hosts/strong.md - secrets: enrolled (age key present) ## Blast radius diff --git a/oikos/cards/host-teddycloud.md b/oikos/cards/host-teddycloud.md index f496a1e..57bc992 100644 --- a/oikos/cards/host-teddycloud.md +++ b/oikos/cards/host-teddycloud.md @@ -6,7 +6,7 @@ - role: teddycloud - address: 192.168.8.150 - mounts: /mnt/library -- doc: containers/131-teddycloud.md +- doc: knowledge/wiki/containers/131-teddycloud.md ## Blast radius - impacts: service:teddycloud @@ -17,4 +17,4 @@ - see the services this host runs for action-level risk classes ## Recent changes -- (none yet) +- 2026-07-06T11:05:35+00:00 activate (config_mutation) — ok diff --git a/oikos/cards/host-trmnl.md b/oikos/cards/host-trmnl.md index d5d8d33..741b975 100644 --- a/oikos/cards/host-trmnl.md +++ b/oikos/cards/host-trmnl.md @@ -5,7 +5,7 @@ - runs-on: host:hubris - role: trmnl-middleware - address: 192.168.8.211 -- doc: containers/128-trmnl.md +- doc: knowledge/wiki/containers/128-trmnl.md ## Blast radius - impacts: service:trmnl diff --git a/oikos/cards/host-zimaos.md b/oikos/cards/host-zimaos.md index 877b8f1..b249fa4 100644 --- a/oikos/cards/host-zimaos.md +++ b/oikos/cards/host-zimaos.md @@ -5,7 +5,7 @@ - runs-on: host:hubris - role: nas-frontend-eval - address: 192.168.8.195 -- doc: vms/100-zimaos.md +- doc: knowledge/wiki/vms/100-zimaos.md ## Blast radius - impacts: service:zimaos diff --git a/oikos/cards/service-arr_stack.md b/oikos/cards/service-arr_stack.md index 335f5c5..2c09e3b 100644 --- a/oikos/cards/service-arr_stack.md +++ b/oikos/cards/service-arr_stack.md @@ -1,7 +1,7 @@ # arr_stack (service:arr_stack) - backend: host:arriman -- doc: containers/122-arriman.md +- doc: knowledge/wiki/containers/122-arriman.md ## Blast radius - impacts: (none) diff --git a/oikos/cards/service-artifacto.md b/oikos/cards/service-artifacto.md index 756c158..fc857a3 100644 --- a/oikos/cards/service-artifacto.md +++ b/oikos/cards/service-artifacto.md @@ -2,7 +2,7 @@ - backend: host:apps - url: https://artifacto.hubris.network -- doc: containers/105-apps.md +- doc: knowledge/wiki/containers/105-apps.md - config repo: dtoro/Artifacto ## Blast radius diff --git a/oikos/cards/service-authentik.md b/oikos/cards/service-authentik.md index 252ddea..93ec04b 100644 --- a/oikos/cards/service-authentik.md +++ b/oikos/cards/service-authentik.md @@ -2,7 +2,7 @@ - backend: host:netbird-vps - url: https://auth.hubris.network -- doc: containers/106-auth-outpost.md +- doc: knowledge/wiki/containers/106-auth-outpost.md - risk notes: SSO provider — outage locks login to OIDC/forward-auth services ## Blast radius diff --git a/oikos/cards/service-caddy.md b/oikos/cards/service-caddy.md index 69e013d..a26093d 100644 --- a/oikos/cards/service-caddy.md +++ b/oikos/cards/service-caddy.md @@ -1,7 +1,7 @@ # caddy (service:caddy) - backend: host:caddy -- doc: containers/121-caddy.md +- doc: knowledge/wiki/containers/121-caddy.md - config repo: dtoro/caddy-conf - risk notes: wide blast radius — every *.hubris.network route rides on it (see oikos/policy.yaml service_overrides) @@ -17,4 +17,4 @@ - edit-config-and-deploy — config_mutation (approval: operator) ## Recent changes -- (none yet) +- 2026-07-06T11:29:56+00:00 add-site-block (config_mutation) — ok diff --git a/oikos/cards/service-dns.md b/oikos/cards/service-dns.md index 1b3af2e..a7a3d9e 100644 --- a/oikos/cards/service-dns.md +++ b/oikos/cards/service-dns.md @@ -1,7 +1,7 @@ # dns (service:dns) - backend: host:dns -- doc: containers/107-dns.md +- doc: knowledge/wiki/containers/107-dns.md - risk notes: LAN-wide resolver — misconfig breaks name resolution for every client ## Blast radius diff --git a/oikos/cards/service-gitea.md b/oikos/cards/service-gitea.md index ab6e9bf..89dca93 100644 --- a/oikos/cards/service-gitea.md +++ b/oikos/cards/service-gitea.md @@ -2,7 +2,7 @@ - backend: host:gitea - url: https://git.hubris.network -- doc: containers/104-gitea.md +- doc: knowledge/wiki/containers/104-gitea.md - config repo: dtoro/gitea-customizations - risk notes: hosts all config repos + deploy webhooks; outage blocks auto-deploy and sync diff --git a/oikos/cards/service-haos.md b/oikos/cards/service-haos.md index 60e6473..4c3401b 100644 --- a/oikos/cards/service-haos.md +++ b/oikos/cards/service-haos.md @@ -1,7 +1,7 @@ # haos (service:haos) - backend: host:haos -- doc: vms/108-haos.md +- doc: knowledge/wiki/vms/108-haos.md ## Blast radius - impacts: (none) diff --git a/oikos/cards/service-homelab_mcp.md b/oikos/cards/service-homelab_mcp.md index 29462c6..fb5a156 100644 --- a/oikos/cards/service-homelab_mcp.md +++ b/oikos/cards/service-homelab_mcp.md @@ -2,7 +2,7 @@ - backend: host:apps - url: https://mcp.hubris.network/mcp -- doc: infrastructure/homelab-context.md +- doc: knowledge/wiki/infrastructure/homelab-context.md - config repo: dtoro/Homelab-Docs - risk notes: agents' primary read surface — outage degrades every agent to grepping the clone diff --git a/oikos/cards/service-jellyfin.md b/oikos/cards/service-jellyfin.md index 75085ca..e12e850 100644 --- a/oikos/cards/service-jellyfin.md +++ b/oikos/cards/service-jellyfin.md @@ -2,7 +2,7 @@ - backend: host:jellyfin - url: https://media.hubris.network -- doc: containers/101-jellyfin.md +- doc: knowledge/wiki/containers/101-jellyfin.md - risk notes: native Authentik OIDC via SSO-Auth plugin, no Caddy forward-auth gate; VAAPI transcode depends on GPU passthrough on strong ## Blast radius diff --git a/oikos/cards/service-matrix.md b/oikos/cards/service-matrix.md index 6d109a4..d321ee2 100644 --- a/oikos/cards/service-matrix.md +++ b/oikos/cards/service-matrix.md @@ -2,7 +2,7 @@ - backend: host:elementsynapse - url: https://matrix.hubris.network -- doc: containers/118-elementsynapse.md +- doc: knowledge/wiki/containers/118-elementsynapse.md - risk notes: alert/approval channel for Oikos — outage silences agent escalation ## Blast radius diff --git a/oikos/cards/service-nextcloud.md b/oikos/cards/service-nextcloud.md index 88d1df8..995644b 100644 --- a/oikos/cards/service-nextcloud.md +++ b/oikos/cards/service-nextcloud.md @@ -2,7 +2,7 @@ - backend: host:nextcloud - url: https://cloud.hubris.network -- doc: containers/114-nextcloud.md +- doc: knowledge/wiki/containers/114-nextcloud.md ## Blast radius - impacts: (none) diff --git a/oikos/cards/service-paperless.md b/oikos/cards/service-paperless.md index 7f0c516..cc02065 100644 --- a/oikos/cards/service-paperless.md +++ b/oikos/cards/service-paperless.md @@ -2,7 +2,7 @@ - backend: host:paperless - url: https://paperless.hubris.network -- doc: containers/103-paperless.md +- doc: knowledge/wiki/containers/103-paperless.md - risk notes: document archive — treat data as irreplaceable; DB operations are destructive-class ## Blast radius diff --git a/oikos/cards/service-photos.md b/oikos/cards/service-photos.md index 76b64d8..a8f9442 100644 --- a/oikos/cards/service-photos.md +++ b/oikos/cards/service-photos.md @@ -2,7 +2,7 @@ - backend: host:mule-images - url: https://photos.hubris.network -- doc: containers/120-mule-images.md +- doc: knowledge/wiki/containers/120-mule-images.md - config repo: dtoro/mule-image ## Blast radius diff --git a/oikos/cards/service-proxmox_ui.md b/oikos/cards/service-proxmox_ui.md index 4c394ee..a83de8c 100644 --- a/oikos/cards/service-proxmox_ui.md +++ b/oikos/cards/service-proxmox_ui.md @@ -2,7 +2,7 @@ - backend: host:hubris - url: https://proxmox.hubris.network -- doc: hosts/hubris.md +- doc: knowledge/wiki/hosts/hubris.md - risk notes: hypervisor UI — changes here affect every guest on the node ## Blast radius diff --git a/oikos/cards/service-teddycloud.md b/oikos/cards/service-teddycloud.md index 23eb88d..1533be8 100644 --- a/oikos/cards/service-teddycloud.md +++ b/oikos/cards/service-teddycloud.md @@ -2,7 +2,7 @@ - backend: host:teddycloud - url: https://teddy.hubris.network -- doc: containers/131-teddycloud.md +- doc: knowledge/wiki/containers/131-teddycloud.md - risk notes: no Caddy forward-auth gate (unlike sab.hubris.network on the same Caddyfile) — reachable to anyone on the LAN/mesh who can resolve teddy.hubris.network; undocumented in inventory.yaml until 2026-07-06 (drift-caught) ## Blast radius diff --git a/oikos/cards/service-trmnl.md b/oikos/cards/service-trmnl.md index 690d35e..29e9540 100644 --- a/oikos/cards/service-trmnl.md +++ b/oikos/cards/service-trmnl.md @@ -2,7 +2,7 @@ - backend: host:trmnl - url: https://trmnl.hubris.network -- doc: containers/128-trmnl.md +- doc: knowledge/wiki/containers/128-trmnl.md - config repo: dtoro/terminalito ## Blast radius diff --git a/oikos/cards/service-zimaos.md b/oikos/cards/service-zimaos.md index f834747..6c8f048 100644 --- a/oikos/cards/service-zimaos.md +++ b/oikos/cards/service-zimaos.md @@ -2,7 +2,7 @@ - backend: host:zimaos - url: https://zimaos.hubris.network -- doc: vms/100-zimaos.md +- doc: knowledge/wiki/vms/100-zimaos.md ## Blast radius - impacts: (none) diff --git a/oikos/console/deploy/README.md b/oikos/console/deploy/README.md index ce69fc5..f3e8283 100644 --- a/oikos/console/deploy/README.md +++ b/oikos/console/deploy/README.md @@ -3,7 +3,7 @@ Deploys the same way `homelab-mcp` and `secrets-issuance` already do: Shape B webhook (own checkout, own systemd units, own deploy secret) on LXC 105 (apps), reading `HOMELAB_CONTEXT_DIR=/opt/homelab-context` for -all data. See [infrastructure/auto-deploy.md](../../../infrastructure/auto-deploy.md) +all data. See [infrastructure/auto-deploy.md](../../../knowledge/wiki/infrastructure/auto-deploy.md) for the general pattern; webhook ids 10 (homelab-mcp, :9811) and 11 (secrets-issuance, :9821) are the direct precedent — this is a third webhook on `dtoro/Homelab-Docs`, port :9831. diff --git a/oikos/drift.py b/oikos/drift.py index 513ed9d..8e598f9 100644 --- a/oikos/drift.py +++ b/oikos/drift.py @@ -215,7 +215,7 @@ def check_pct_list(inv: dict | None = None) -> list[dict]: def check_caddy_backends(inv: dict | None = None) -> list[dict]: """Caddy's /etc/caddy (a git checkout of dtoro/caddy-conf, per - containers/121-caddy.md) vs inventory service backend IPs. Best-effort + knowledge/wiki/containers/121-caddy.md) vs inventory service backend IPs. Best-effort grep for reverse_proxy targets; skips services whose Caddyfile snippet doesn't use a bare IP (e.g. references a Caddy snippet/import).""" inv = inv or _load_inventory() diff --git a/oikos/gen-topology.py b/oikos/gen-topology.py index 87505d8..ca68d46 100644 --- a/oikos/gen-topology.py +++ b/oikos/gen-topology.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 """ -Generate infrastructure/topology.md (Mermaid views) and per-entity context +Generate knowledge/wiki/infrastructure/topology.md (Mermaid views) and per-entity context cards from inventory.yaml. Views: @@ -42,7 +42,7 @@ from oikos import policy as oikos_policy # noqa: E402 from oikos import relations as oikos_relations # noqa: E402 INVENTORY = REPO / "inventory.yaml" -OUTPUT = REPO / "infrastructure" / "topology.md" +OUTPUT = REPO / "knowledge" / "wiki" / "infrastructure" / "topology.md" CARDS_DIR = REPO / "oikos" / "cards" BANNER = ( @@ -82,15 +82,15 @@ def _host_card(name: str, entry: dict, inv: dict) -> str: lines.append(f"- mounts: {', '.join(entry['mounts'])}") doc = None if entry.get("kind") == "lxc" and pve: - cand = REPO / "containers" / f"{pve}-{name}.md" + cand = REPO / "knowledge" / "wiki" / "containers" / f"{pve}-{name}.md" if cand.exists(): doc = str(cand.relative_to(REPO)) elif entry.get("kind") == "vm" and pve: - cand = REPO / "vms" / f"{pve}-{name}.md" + cand = REPO / "knowledge" / "wiki" / "vms" / f"{pve}-{name}.md" if cand.exists(): doc = str(cand.relative_to(REPO)) elif entry.get("kind") == "proxmox-host": - cand = REPO / "hosts" / f"{name}.md" + cand = REPO / "knowledge" / "wiki" / "hosts" / f"{name}.md" if cand.exists(): doc = str(cand.relative_to(REPO)) if doc: @@ -192,9 +192,9 @@ def render(inv: dict) -> str: parts = [ BANNER, "# Topology (generated)\n", - f"Source: [inventory.yaml](../inventory.yaml) — {counts}.", - "Edge semantics: [oikos/ontology.yaml](../oikos/ontology.yaml). " - "Operating model: [OIKOS.md](../OIKOS.md).\n", + f"Source: [inventory.yaml](../../../inventory.yaml) — {counts}.", + "Edge semantics: [oikos/ontology.yaml](../../../oikos/ontology.yaml). " + "Operating model: [OIKOS.md](../../../.agents/OIKOS.md).\n", "## Compute & ingress\n", "\n".join(compute_view(inv)) + "\n", "## Storage (mounts)\n", diff --git a/oikos/gen_topology_lib.py b/oikos/gen_topology_lib.py index 84c3e12..13c8548 100644 --- a/oikos/gen_topology_lib.py +++ b/oikos/gen_topology_lib.py @@ -2,7 +2,7 @@ Split out of oikos/gen-topology.py so it's importable (a hyphenated filename can't be `import`ed as a module). oikos/gen-topology.py is the -CLI entrypoint that writes infrastructure/topology.md + oikos/cards/; +CLI entrypoint that writes knowledge/wiki/infrastructure/topology.md + oikos/cards/; oikos/console/app.py imports this module directly to render the live /graph page without shelling out. """ diff --git a/oikos/report.py b/oikos/report.py index ef75a68..5d7e1a9 100644 --- a/oikos/report.py +++ b/oikos/report.py @@ -4,7 +4,7 @@ Both are generated text, not sent directly to Matrix — same integration contract as oikos/approve.py: there's no dedicated Matrix bot in this homelab, so Hermes (already posting alerts as @dtoro:avispero, see -infrastructure/monitoring.md) is the one that actually delivers this text. +knowledge/wiki/infrastructure/monitoring.md) is the one that actually delivers this text. The daily brief is meant to run once a day (e.g. chained after an early oikos-scheduler.service run, or its own systemd timer); the weekly report is a deeper markdown review. diff --git a/oikos/scheduler.py b/oikos/scheduler.py index fe0853e..b1e05ed 100644 --- a/oikos/scheduler.py +++ b/oikos/scheduler.py @@ -75,7 +75,7 @@ _HEALTH_OVERRIDES = { "url_transform": lambda url: url.rstrip("/").rsplit("/", 1)[0] + "/health", }, # Token-gated at the app level (401 without a token is correct, not - # down) — see containers/128-trmnl.md, which documents a dedicated + # down) — see knowledge/wiki/containers/128-trmnl.md, which documents a dedicated # /health endpoint returning 200 unauthenticated. "trmnl": {"url_transform": lambda url: url.rstrip("/") + "/health"}, } diff --git a/operations/agent-enrollment.md b/operations/agent-enrollment.md index a615c00..dfd41a6 100644 --- a/operations/agent-enrollment.md +++ b/operations/agent-enrollment.md @@ -6,7 +6,7 @@ this repo that auto-syncs every 5 min, a per-client age key for SOPS decryption, the `homelab` CLI, and an MCP endpoint in Claude Code's config. > Onboarding a Nous-Hermes-powered Goose agent on top of standard enrollment? -> See [hermes-agent.md](./hermes-agent.md). It uses the same `bootstrap.sh` +> See [hermes-agent.md](hermes-agent.md). It uses the same `bootstrap.sh` > with an additional `--with-hermes` flag. Architecture in [project_homelab_context_plan](https://… memory link); the @@ -40,7 +40,7 @@ Bootstrap auto-installs netbird and drives `netbird up` if the mesh isn't alread The new client runs bootstrap straight from a fresh OS. Bootstrap installs netbird (apt/dnf/brew based on the OS), then runs `netbird up --management-url https://netbird.hubris.network --ssh-jwt-cache-ttl 86400`. A device-code URL prints inline. The operator opens it (in a browser logged into Authentik), goes through identification → password → consent, and the CLI returns `Connected`. Bootstrap then proceeds with the rest of preflight. -Pre-condition: the operator must be a registered user in Authentik (typically the lab owner). The first user-login against a netbird account with existing peers is added as `pending_approval=1` and needs an sqlite promotion to `owner` — see [124-authentik.md First-time owner promotion gotcha](../containers/124-authentik.md). Only needed once per account. +Pre-condition: the operator must be a registered user in Authentik (typically the lab owner). The first user-login against a netbird account with existing peers is added as `pending_approval=1` and needs an sqlite promotion to `owner` — see [124-authentik.md First-time owner promotion gotcha](../knowledge/wiki/containers/124-authentik.md). Only needed once per account. **Path A — setup-key (headless/scripted onboarding):** @@ -62,7 +62,7 @@ Useful for headless servers (no browser at all) or unattended cloud-init bootstr ### DNS prerequisite `*.hubris.network` resolves via the split-horizon dnsmasq on LXC 124 -([dns.md](../infrastructure/dns.md)) for LAN clients, **but only if the +([dns.md](../knowledge/wiki/infrastructure/dns.md)) for LAN clients, **but only if the client uses 192.168.8.180 as its resolver**. Most LXCs and roaming workstations don't by default. Options: @@ -342,7 +342,7 @@ The CLI prints a follow-up checklist that the operator must do manually: | `homelab` CLI doesn't pick up repo updates | Pre-`02db…` bootstrap copied the binary instead of symlinking | One-time migration: `sudo ln -sfn /opt/homelab-context/bin/homelab /usr/local/bin/homelab`. New bootstraps use the symlink, which auto-tracks the synced repo. | | `homelab-context-sync.service` journal shows `fatal: could not read Username for 'https://git.hubris.network'` | Pre-fix bootstrap set the gitea credential helper via `git config --global`, which writes to `/root/.gitconfig` — invisible to the systemd timer's git process (no HOME set). | One-time migration: `sudo git config --system credential.helper "store --file=/etc/homelab-context/git-credentials"`. New bootstraps store the helper in `/etc/gitconfig` instead. | | Chat-mode `!` shell can't `sudo` (`a terminal is required to read the password`) | Claude Code's `!` invocation doesn't allocate a tty, and standard `sudo` won't read its password from stdin or a non-tty pipe. | Run the sudo'd command in a real terminal outside chat. For commands the agent issues repeatedly, configure passwordless sudo for the narrow set (e.g. `/etc/sudoers.d/homelab-self` with ` ALL=(ALL) NOPASSWD: /usr/bin/dnf upgrade -y, /usr/bin/apt-get *`). | -| `netbird status -d` reports `192.168.8.180:53 ... is Unavailable` but DNS actually works | netbird's UDP-53 probe times out over the relay latency (~90ms), but actual queries still flow through systemd-resolved. Cosmetic. | Ignore unless `dig @192.168.8.180 git.hubris.network` also fails — then check dnsmasq on [LXC 124](../containers/124-authentik.md). | +| `netbird status -d` reports `192.168.8.180:53 ... is Unavailable` but DNS actually works | netbird's UDP-53 probe times out over the relay latency (~90ms), but actual queries still flow through systemd-resolved. Cosmetic. | Ignore unless `dig @192.168.8.180 git.hubris.network` also fails — then check dnsmasq on [LXC 124](../knowledge/wiki/containers/124-authentik.md). | | `netbird ssh` rejected with `JWT authentication failed: validate token (expected issuer=https://netbird.hubris.network/oauth2 ...)` | Peer's SSH JWT validator cached the OLD embedded-Dex issuer from before the 2026-05-21 Authentik migration. `systemctl restart netbird` and `netbird down/up` don't clear it — `client/internal/engine_ssh.go` bails out of `updateSSH()` if the SSH server is already running. | Full daemon bounce: `sudo systemctl stop netbird; sleep 3; sudo systemctl start netbird`. Verify with `grep -iE "issuer\|audience" /var/log/netbird/client.log \| tail`. Apply once per peer post-migration. | | `netbird ssh` JWT passes but session closes with `user privilege check failed: user dtoro not found: unknown user dtoro` | netbird-ssh defaults the remote username to the LOCAL one (operator's laptop user). Hubris and LXCs only have `root`. | Always use explicit `root@` prefix manually: `netbird ssh -p 22022 root@proxmox-server.netbird.selfhosted`. `homelab ssh ` does this automatically via `inventory.yaml`'s per-host `ssh.user` field (defaults to `root`). | | `homelab ssh hubris` (or any host on the LAN) fails with `Connection refused` or hangs, despite mesh routing being up | Off-LAN networks (operator on a VPN / coffee shop / symmetric NAT) sometimes can't reach the LAN IP even with the netbird subnet route. | Newer homelab CLIs probe the LAN with a 1.5s TCP connect and transparently fall back to the netbird FQDN. If your `/usr/local/bin/homelab` is a symlink to `/opt/homelab-context/bin/homelab` it'll pick up the fix on the next 5-min context sync. Otherwise pull the latest from gitea. | @@ -356,7 +356,7 @@ and LAN IP registration. New workstations enrolled via this doc will automatically join the universal SSH mesh. ### 2026-05-31 — cross-link to hermes-agent.md -Added a sibling page covering Nous-Hermes-on-Goose enrollment ([hermes-agent.md](./hermes-agent.md)) and noted it at the top of this page. The Hermes flow extends `bootstrap.sh` with `--with-hermes` and `homelab client add` with the same flag; it does not change the underlying enrollment steps documented here. +Added a sibling page covering Nous-Hermes-on-Goose enrollment ([hermes-agent.md](hermes-agent.md)) and noted it at the top of this page. The Hermes flow extends `bootstrap.sh` with `--with-hermes` and `homelab client add` with the same flag; it does not change the underlying enrollment steps documented here. ### 2026-05-21 — netbird-ssh JWT issuer + username + LAN-fallback troubleshooting rows Added three rows to the troubleshooting table covering issues surfaced during the netbird vanilla migration: (1) post-migration SSH JWT validator cache stuck on old Dex issuer (full `systemctl stop/start` required, not `restart`), (2) `user not found` from netbird-ssh's local-username default (use explicit `root@`), and (3) homelab CLI's LAN→netbird-FQDN fallback for off-LAN operators. Companion code change: per-host `ssh.user` field in `inventory.yaml` + `homelab` CLI's `ssh_target()` helper. diff --git a/operations/commands.md b/operations/commands.md index d2821d3..bdc1cd7 100644 --- a/operations/commands.md +++ b/operations/commands.md @@ -1,6 +1,6 @@ # Operations cheatsheet -Run from the [hubris host](../hosts/hubris.md) as root. When working from `/root` on Linux you're already on hubris — don't `ssh hubris` / `ping hubris`. +Run from the [hubris host](../knowledge/wiki/hosts/hubris.md) as root. When working from `/root` on Linux you're already on hubris — don't `ssh hubris` / `ping hubris`. ## Proxmox CLI @@ -8,13 +8,13 @@ Run from the [hubris host](../hosts/hubris.md) as root. When working from `/root | --- | --- | | `pct list` / `qm list` | List LXC containers / VMs | | `pct config ` / `qm config ` | Container / VM config | -| `pct exec -- ` | Run command inside an LXC without entering it (no initgroups — see [media permissions](../infrastructure/media-permissions.md)) | +| `pct exec -- ` | Run command inside an LXC without entering it (no initgroups — see [media permissions](../knowledge/wiki/infrastructure/media-permissions.md)) | | `pct enter ` | Shell into a container | | `pct start ` / `pct stop ` | Boot / halt a container | | `pvesm status` | Storage pools status | | `pvesh get /nodes --output-format json` | Node summary as JSON | | `pvesh get /nodes/hubris/lxc//status/current` | Live container status | -| `pvesh get /cluster/resources --type vm --output-format json` | Bulk per-LXC CPU/mem/disk (used by the `homelab-health-watchdog` Hermes cron — see [monitoring](../infrastructure/monitoring.md); the old `claudio-monitor` this once fed is deprecated) | +| `pvesh get /cluster/resources --type vm --output-format json` | Bulk per-LXC CPU/mem/disk (used by the `homelab-health-watchdog` Hermes cron — see [monitoring](../knowledge/wiki/infrastructure/monitoring.md); the old `claudio-monitor` this once fed is deprecated) | | `pveversion` | PVE version | | `journalctl -u pve-cluster -n 100` | PVE service logs | @@ -22,22 +22,22 @@ Run from the [hubris host](../hosts/hubris.md) as root. When working from `/root - Shared mount: `/mnt/library` (ext4 on lvmthin `library`). - Bind into a container: `pct set -mp /mnt/library/,mp=/data` -- For the standard whole-tree mount: `pct set -mp0 /mnt/library,mp=/mnt/library`. See [media permissions](../infrastructure/media-permissions.md) for the GID-10000 onboarding recipe. +- For the standard whole-tree mount: `pct set -mp0 /mnt/library,mp=/mnt/library`. See [media permissions](../knowledge/wiki/infrastructure/media-permissions.md) for the GID-10000 onboarding recipe. ## Reverse proxy -- Caddyfile: `/etc/caddy/Caddyfile` on [LXC 121](../containers/121-caddy.md). -- **CRITICAL:** This file is tracked in `dtoro/caddy-conf` (https://git.hubris.network/dtoro/caddy-conf). Never edit it directly on the LXC — commit + push to the repo instead. Caddy auto-deploys on push (see [auto-deploy](../infrastructure/auto-deploy.md)). If you edit directly, the change will be lost on the next pull and agents won't know about it. +- Caddyfile: `/etc/caddy/Caddyfile` on [LXC 121](../knowledge/wiki/containers/121-caddy.md). +- **CRITICAL:** This file is tracked in `dtoro/caddy-conf` (https://git.hubris.network/dtoro/caddy-conf). Never edit it directly on the LXC — commit + push to the repo instead. Caddy auto-deploys on push (see [auto-deploy](../knowledge/wiki/infrastructure/auto-deploy.md)). If you edit directly, the change will be lost on the next pull and agents won't know about it. - Hot reload: `pct exec 121 -- systemctl reload caddy`. - Validate: `pct exec 121 -- caddy validate --config /etc/caddy/Caddyfile`. - Git workflow shortcut: `pct exec 121 -- "cd /etc/caddy && git add Caddyfile && git commit -m '...' && git push"`. ## DNS -- Split-horizon authority: [Technitium DNS](https://technitium.com) on [dns (107)](../containers/107-dns.md) at `192.168.8.2:53`. Web UI at `http://192.168.8.2`. (Formerly dnsmasq on the now-destroyed LXC 124 — decommissioned 2026-06-04.) +- Split-horizon authority: [Technitium DNS](https://technitium.com) on [dns (107)](../knowledge/wiki/containers/107-dns.md) at `192.168.8.2:53`. Web UI at `http://192.168.8.2`. (Formerly dnsmasq on the now-destroyed LXC 124 — decommissioned 2026-06-04.) - Add/edit records in the Technitium UI; the NetBird managed zone sync (`scripts/dns-sync.py` cron on 107) picks changes up within ~10 minutes. - Verify: `dig @192.168.8.2 +short .hubris.network`. -- See [DNS](../infrastructure/dns.md). +- See [DNS](../knowledge/wiki/infrastructure/dns.md). ## Web access @@ -83,9 +83,9 @@ See [OIKOS.md](../OIKOS.md) for the operating model. Quick reference: Oikos Console (read-mostly dashboard): `oikos.hubris.network` once deployed — see [oikos/console/deploy/README.md](../oikos/console/deploy/README.md). ## Related -- [Hubris host](../hosts/hubris.md) -- [Containers index](../containers/index.md) -- [DNS](../infrastructure/dns.md) -- [Monitoring](../infrastructure/monitoring.md) -- [Auto-deploy](../infrastructure/auto-deploy.md) +- [Hubris host](../knowledge/wiki/hosts/hubris.md) +- [Containers index](../knowledge/wiki/containers/index.md) +- [DNS](../knowledge/wiki/infrastructure/dns.md) +- [Monitoring](../knowledge/wiki/infrastructure/monitoring.md) +- [Auto-deploy](../knowledge/wiki/infrastructure/auto-deploy.md) - [Runbook: dpkg-interrupted recovery](runbook-dpkg-interrupted.md) — what to do when apt got killed mid-transaction diff --git a/operations/hermes-agent.md b/operations/hermes-agent.md index 2f87838..7227f09 100644 --- a/operations/hermes-agent.md +++ b/operations/hermes-agent.md @@ -2,7 +2,7 @@ Onboards [Nous Research's Hermes](https://nousresearch.com/) (a fine-tuned Llama variant) as a working terminal agent on a homelab client. Builds on top -of standard client enrollment (see [agent-enrollment.md](./agent-enrollment.md)) +of standard client enrollment (see [agent-enrollment.md](agent-enrollment.md)) — this page covers only the Hermes-specific additions. The agent runs as a [Goose](https://goose-docs.ai/) session. Goose provides: @@ -21,7 +21,7 @@ The persona is `/opt/homelab-context/HERMES.md`, symlinked as Goose's global | Requirement | How | | --- | --- | -| Standard enrollment complete (`homelab whoami` works) | [agent-enrollment.md](./agent-enrollment.md) | +| Standard enrollment complete (`homelab whoami` works) | [agent-enrollment.md](agent-enrollment.md) | | `secrets/openrouter-api-key.yaml` exists with a real `sk-or-...` value | See "Seeding the OpenRouter key" below | | The host's `age_pubkey` is on the openrouter-api-key.yaml sops rule | `homelab client add --finalize-pubkey --with-hermes` | @@ -165,7 +165,7 @@ every tool call, use `approve`. See ## Cross-references -- [agent-enrollment.md](./agent-enrollment.md) — base client onboarding the +- [agent-enrollment.md](agent-enrollment.md) — base client onboarding the Hermes flow assumes is done. - [`HERMES.md`](../HERMES.md) — the persona the Hermes agent reads on every session start (via `~/.config/goose/.goosehints`). diff --git a/plans/2026-06-24-trmnl-plugins-lxc.md b/plans/2026-06-24-trmnl-plugins-lxc.md index c38f9f3..294219d 100644 --- a/plans/2026-06-24-trmnl-plugins-lxc.md +++ b/plans/2026-06-24-trmnl-plugins-lxc.md @@ -15,9 +15,9 @@ wiring, same split as Artifacto/Plato. - No TRMNL middleware in the lab. Highest LXC id is 127 (see `containers/index.md`). - Public hostnames terminate at the [VPS netbird traefik](../hosts/netbird-vps.md) → netbird - mesh → [caddy (121)](../containers/121-caddy.md) → backend LXC. Cert obtained by Caddy + mesh → [caddy (121)](../knowledge/wiki/containers/121-caddy.md) → backend LXC. Cert obtained by Caddy (IONOS DNS-01) and mirrored to the VPS by the daily cert-sync timer on the host. -- Auto-deploy pipelines are gitea-webhook driven, two shapes (see [auto-deploy](../infrastructure/auto-deploy.md)). +- Auto-deploy pipelines are gitea-webhook driven, two shapes (see [auto-deploy](../knowledge/wiki/infrastructure/auto-deploy.md)). ## Target state @@ -74,7 +74,7 @@ TRMNL cloud --GET 15m, Bearer token--> https://trmnl.hubris.network/munich-hom `/etc/terminalito-deploy/git-credentials` (mode 600). Register a gitea webhook on `dtoro/terminalito`; add `192.168.8.<128-ip>` to gitea `app.ini` `ALLOWED_HOST_LIST`. -5. **DNS**: add `trmnl.hubris.network` A → `192.168.8.175` (caddy) on [Technitium (107)](../containers/107-dns.md). +5. **DNS**: add `trmnl.hubris.network` A → `192.168.8.175` (caddy) on [Technitium (107)](../knowledge/wiki/containers/107-dns.md). 6. **Caddy** (`dtoro/caddy-conf`, commit+push auto-deploys): ``` diff --git a/runbooks/config-change-deploy.md b/runbooks/config-change-deploy.md index 8767f47..e86d8d6 100644 --- a/runbooks/config-change-deploy.md +++ b/runbooks/config-change-deploy.md @@ -21,7 +21,7 @@ own repo) and get it live, safely. [OIKOS.md](../OIKOS.md) conventions). 3. Make the change, commit, push to `main`. 4. The Gitea webhook fires the deploy pipeline for that repo (see - [infrastructure/auto-deploy.md](../infrastructure/auto-deploy.md) for + [infrastructure/auto-deploy.md](../knowledge/wiki/infrastructure/auto-deploy.md) for the exact receiver/reload for this service). 5. Run the preflight's verification command. If it fails, check `homelab service log` for the reload/restart error. diff --git a/runbooks/runbook-dpkg-interrupted.md b/runbooks/runbook-dpkg-interrupted.md index 3e71f15..c619142 100644 --- a/runbooks/runbook-dpkg-interrupted.md +++ b/runbooks/runbook-dpkg-interrupted.md @@ -103,8 +103,8 @@ Then `systemctl status apt-recovery` from a fresh ssh to check progress. ## Related - [Operations cheatsheet](commands.md) -- [Auto-deploy pipelines](../infrastructure/auto-deploy.md) -- [Hubris host page](../hosts/hubris.md) +- [Auto-deploy pipelines](../knowledge/wiki/infrastructure/auto-deploy.md) +- [Hubris host page](../knowledge/wiki/hosts/hubris.md) ## Changelog