Two things the entity window redesign surfaced but deliberately left alone. **blast_radius answered the wrong question.** It walked source→target for every relationship type, but which end of an edge is the dependent differs per type: "machine hosts container" means the target breaks, while "service depends-on service" and "ingress routes-to service" mean the SOURCE breaks. Walking everything forwards was right for hosts/provides and backwards for everything else — and swept in 2,800+ documents/involves/targets edges of pure bookkeeping, so the result contained tasks and executions that cannot break. Direction is now declared per relationship type in seeds/ontology.yaml (blast_direction: forward | backward | none), the same shape as the entity types' monitoring: declaration, and defaults to none so an undeclared edge contributes nothing rather than a confidently wrong answer. It also needed a modelling fix: `routes-to` names an ingress's BACKEND, so nothing recorded that all 21 public hostnames are terminated by caddy. A `served-by` edge type now says so. pool:ludo-lvm 2 -> 23 (every container storing on it, then their services) lxc:caddy 4 -> 22 (service:caddy, then all 21 ingress routes) service:authentik 7 (what authenticates via it) **Every ping check was reporting down.** Not a host:strong false positive: all seven, including ws:mac-mini — the Docker host itself. The scheduler runs in Docker on macOS, whose VM does not route ICMP to the LAN; loopback pings succeed and every LAN ping fails. Under health aggregation each broken probe dragged its entity to down. The question the check exists to answer is "is it reachable", and ICMP is only one way to ask it. checkPing now falls back to a TCP connect before concluding anything, which restores an honest verdict for the four hosts that are genuinely up while leaving the genuinely unreachable ones down. TestBlastRadiusTerminatesOnCycles asserted the old direction (caddy=1, authentik=2 — the cycle walked the wrong way); it now asserts the corrected depths, and its exact-node-count check is relaxed because walking the right way also surfaces the seed's own real dependents, which are correct answers. Co-Authored-By: Claude <noreply@anthropic.com>
596 lines
39 KiB
YAML
596 lines
39 KiB
YAML
# Oikos inventory seed — entity instances + relationships.
|
|
#
|
|
# Translated from the legacy /inventory.yaml (2026-07-07). Bootstraps the
|
|
# entities/relationships tables (migration 002); after ingest the DB is
|
|
# authoritative and this file is regenerated by `GET /api/v1/export`.
|
|
#
|
|
# Slug conventions: <prefix>:<name> —
|
|
# host: (proxmox-host, standalone-server) · ws: (workstation) · lxc: · vm:
|
|
# service: · ingress: · repo: (config-repo) · pool: · volume: · mesh: · lan:
|
|
# zone: (dns-zone) · idp: · person: · agent: · cluster: · backup:
|
|
#
|
|
# `state:` omitted = the type's lifecycle default (active).
|
|
# Mount details (mount_point) are attributes on `mounts` edges.
|
|
# Known thin spots are marked # THIN: backfill later.
|
|
|
|
version: 1
|
|
|
|
entities:
|
|
|
|
# ─── Sites, networks ───────────────────────────────────────────────
|
|
- {slug: "site:home", type: site, name: home}
|
|
- {slug: "site:ionos-dc", type: site, name: ionos-dc,
|
|
attributes: {address: IONOS datacenter (VPS)}}
|
|
- slug: "lan:lab"
|
|
type: lan
|
|
name: lab
|
|
attributes: {subnet: 192.168.8.0/24}
|
|
- slug: "lan:household"
|
|
type: lan
|
|
name: household
|
|
attributes: {subnet: 192.168.178.0/24} # Fritz LAN; static route to lab subnet
|
|
- slug: "mesh:netbird"
|
|
type: mesh
|
|
name: netbird
|
|
attributes:
|
|
provider: netbird
|
|
subnet: 100.122.0.0/16
|
|
domain: netbird.selfhosted
|
|
- slug: "mesh:tailscale"
|
|
type: mesh
|
|
name: tailscale
|
|
state: deprecated # migration to netbird in progress (infrastructure/mesh.md)
|
|
attributes: {provider: tailscale}
|
|
- slug: "zone:hubris.network"
|
|
type: dns-zone
|
|
name: hubris.network
|
|
attributes: {zone: hubris.network, authority: "Technitium (LXC 107), split-horizon"}
|
|
- slug: "zone:netbird.selfhosted"
|
|
type: dns-zone
|
|
name: netbird.selfhosted
|
|
attributes: {zone: netbird.selfhosted, authority: netbird-mgmt}
|
|
|
|
# ─── Machines ──────────────────────────────────────────────────────
|
|
- slug: "cluster:homelab"
|
|
type: cluster
|
|
name: Homelab
|
|
attributes: {quorum: "2-node, no QDevice tiebreaker yet"}
|
|
- slug: "host:hubris"
|
|
type: proxmox-host
|
|
name: hubris
|
|
attributes:
|
|
os: linux
|
|
lan_ip: 192.168.8.77
|
|
mesh: {netbird: {ip: 100.122.38.109, fqdn: proxmox-server.netbird.selfhosted}}
|
|
ssh: {port: 22, netbird_port: 22022, user: root}
|
|
age_pubkey: age1xkklkvnk5z0fsnh6cfgv70hy9ksfy8rdprwerzw4yk3p4p7cxcqs2yvpz6
|
|
- slug: "host:strong"
|
|
type: proxmox-host
|
|
name: strong
|
|
attributes:
|
|
os: linux
|
|
lan_ip: 192.168.178.181
|
|
ssh: {user: root}
|
|
age_pubkey: age1rtwvdct6avjkr3cyxv3vue3vqx4d524fjfr3vk7xrnvyrylnry5sm54sn4
|
|
note: >-
|
|
PVE 9.2.3 since 2026-07-01 (formerly workstation ludo-mini). Joined
|
|
Homelab cluster same day. Not yet netbird-enrolled — reachable via
|
|
household LAN / Fritz static route only.
|
|
- slug: "host:netbird-vps"
|
|
type: standalone-server
|
|
name: netbird-vps
|
|
attributes:
|
|
os: linux
|
|
provider: ionos
|
|
control_level: partial # managed via ssh from hubris; not a homelab client
|
|
public_ipv4: 82.165.190.79
|
|
mesh: {netbird: {ip: 100.122.165.149, fqdn: netbird-ionos.netbird.selfhosted}}
|
|
ssh: {user: root}
|
|
note: netbird mgmt+signal+relay+dashboard + coturn; sshd locked to hubris pubkey
|
|
- slug: "ws:mac-mini"
|
|
type: workstation
|
|
name: mac-mini
|
|
attributes:
|
|
os: macos
|
|
user: dtoro
|
|
lan_ip: 192.168.178.182
|
|
mesh: {netbird: {fqdn: mac-mini-234-17.netbird.selfhosted}}
|
|
age_pubkey: age169104ee1a9e1577d493820830560197f0adf56bf8f1c369d57c152c03f9437ae
|
|
note: only macOS in fleet; Oikos Docker host. Enrolled 2026-07-08.
|
|
enrolled_at: "2026-07-08T09:49:03Z"
|
|
mesh_ip: 192.168.178.182
|
|
hostname: mac-mini
|
|
- slug: "ws:republic-laptop"
|
|
type: workstation
|
|
name: republic-laptop
|
|
attributes:
|
|
os: linux
|
|
user: dtoro
|
|
mesh: {netbird: {fqdn: republic-laptop.netbird.selfhosted}}
|
|
|
|
# ─── LXCs ──────────────────────────────────────────────────────────
|
|
- {slug: "lxc:jellyfin", type: lxc, name: jellyfin,
|
|
attributes: {pve_id: 101, role: media-server, lan_ip: 192.168.8.246,
|
|
public_host: media.hubris.network,
|
|
note: "VAAPI transcode via Radeon 680M passthrough; migrated hubris→strong 2026-07-05"}}
|
|
- {slug: "lxc:nfs-export", type: lxc, name: nfs-export,
|
|
attributes: {pve_id: 102, role: storage-export, lan_ip: 192.168.8.200}}
|
|
- {slug: "lxc:paperless", type: lxc, name: paperless,
|
|
attributes: {pve_id: 103, role: document-archive, lan_ip: 192.168.8.130,
|
|
public_host: paperless.hubris.network}}
|
|
- {slug: "lxc:gitea", type: lxc, name: gitea,
|
|
attributes: {pve_id: 104, role: git-server, lan_ip: 192.168.8.121,
|
|
public_host: git.hubris.network,
|
|
note: "bare repos at /mnt/library/repos/dtoro/*.git"}}
|
|
- {slug: "lxc:apps", type: lxc, name: apps,
|
|
attributes: {pve_id: 105, role: docker-apps, lan_ip: 192.168.8.205,
|
|
age_pubkey: age1duyl8mkpgu80uv934dy8q7enqjms6yvdz264hme8uryuxmvvqesq6rusq0,
|
|
note: "legacy Oikos host; fallback during cutover (plan A6)"}}
|
|
- {slug: "lxc:auth-outpost", type: lxc, name: auth-outpost,
|
|
attributes: {pve_id: 106, role: authentik-gateway, lan_ip: 192.168.8.6}}
|
|
- {slug: "lxc:dns", type: lxc, name: dns,
|
|
attributes: {pve_id: 107, role: dns-server, lan_ip: 192.168.8.2}}
|
|
- {slug: "lxc:nextcloud", type: lxc, name: nextcloud,
|
|
attributes: {pve_id: 114, role: file-sync, lan_ip: 192.168.8.224,
|
|
public_host: cloud.hubris.network}}
|
|
- {slug: "lxc:elementsynapse", type: lxc, name: elementsynapse,
|
|
attributes: {pve_id: 118, role: matrix-server, lan_ip: 192.168.8.242,
|
|
public_host: matrix.hubris.network,
|
|
note: "migrated hubris→strong 2026-07-05"}}
|
|
- {slug: "lxc:sophia", type: lxc, name: sophia,
|
|
attributes: {pve_id: 119, role: workshop, lan_ip: 192.168.8.109}}
|
|
- {slug: "lxc:mule-images", type: lxc, name: mule-images,
|
|
attributes: {pve_id: 120, role: photo-management, lan_ip: 192.168.8.136,
|
|
public_host: photos.hubris.network}}
|
|
- {slug: "lxc:caddy", type: lxc, name: caddy,
|
|
attributes: {pve_id: 121, role: reverse-proxy, lan_ip: 192.168.8.175,
|
|
note: "terminates all *.hubris.network; /etc/caddy is a checkout of dtoro/caddy-conf"}}
|
|
- {slug: "lxc:arriman", type: lxc, name: arriman,
|
|
attributes: {pve_id: 122, role: arr-stack, lan_ip: 192.168.8.245,
|
|
public_hosts: [jellyseerr.hubris.network, qbit.hubris.network, sab.hubris.network],
|
|
note: "homarr/radarr/sonarr/lidarr/sab/qbit/bazarr/flaresolverr/prowlarr/jellyseerr; migrated to strong 2026-07-05"}}
|
|
- {slug: "lxc:trmnl", type: lxc, name: trmnl,
|
|
attributes: {pve_id: 128, role: trmnl-middleware, lan_ip: 192.168.8.211,
|
|
public_host: trmnl.hubris.network,
|
|
note: "not yet mesh/SOPS-enrolled"}}
|
|
- {slug: "lxc:house", type: lxc, name: house,
|
|
attributes: {pve_id: 129, role: family-planner, lan_ip: 192.168.8.244,
|
|
public_host: house.hubris.network,
|
|
age_pubkey: age1s07zs83ehtlg8jtwvr75ltc3c4cdlemfwjuxrwjtwkqxkl9tpggsyrzn2h,
|
|
note: "Yuvomi + WebDAV bridge to paperless; migrated to strong 2026-07-05"}}
|
|
- {slug: "lxc:grimmory", type: lxc, name: grimmory,
|
|
attributes: {pve_id: 130, role: book-library, lan_ip: 192.168.8.247,
|
|
public_host: books.hubris.network,
|
|
age_pubkey: age1uellsemnjrzgfg9fxw4jefpy05laxzggwnwhh6ny3wl7alyp6v8q0muxet}}
|
|
- {slug: "lxc:teddycloud", type: lxc, name: teddycloud,
|
|
attributes: {pve_id: 131, role: teddycloud, lan_ip: 192.168.8.150,
|
|
public_host: teddy.hubris.network,
|
|
note: "drift-caught 2026-07-06; no forward-auth gate on route; not a homelab client"}}
|
|
- {slug: "lxc:rclone", type: lxc, name: rclone,
|
|
attributes: {pve_id: 132, role: backup,
|
|
mesh: {netbird: {fqdn: rclone.netbird.selfhosted}},
|
|
age_pubkey: age1pwtdws2thdh7vzp2dzttl3zxgcs2tgpcsjsqgw3q04nyml4kvuqq467u4x}}
|
|
# verified live on hubris 2026-07-07 (pct list via MCP)
|
|
- {slug: "lxc:seanime", type: lxc, name: seanime,
|
|
attributes: {pve_id: 133, role: anime-media-server, lan_ip: 192.168.8.248,
|
|
public_host: seanime.hubris.network,
|
|
note: "systemd service at /opt/seanime; uses qbittorrent on arriman"}}
|
|
- {slug: "lxc:romm", type: lxc, name: romm,
|
|
attributes: {pve_id: 134, role: rom-manager, lan_ip: 192.168.8.249,
|
|
public_host: roms.hubris.network,
|
|
note: "docker compose + MariaDB sidecar at /opt/romm"}}
|
|
|
|
# ─── VMs ───────────────────────────────────────────────────────────
|
|
- {slug: "vm:zimaos", type: vm, name: zimaos,
|
|
attributes: {pve_id: 100, role: nas-frontend-eval, lan_ip: 192.168.8.195,
|
|
public_host: zimaos.hubris.network}}
|
|
- {slug: "vm:haos", type: vm, name: haos,
|
|
attributes: {pve_id: 108, role: home-automation, lan_ip: 192.168.8.101}}
|
|
|
|
# ─── Storage ───────────────────────────────────────────────────────
|
|
- {slug: "pool:local-lvm-hubris", type: storage-pool, name: local-lvm (hubris),
|
|
attributes: {type: lvm}}
|
|
- {slug: "pool:library-hubris", type: storage-pool, name: library (hubris),
|
|
attributes: {type: lvmthin, capacity_gb: 3700}}
|
|
# 2nd Samsung 990 EVO Plus NVMe; backs /mnt/library ext4 via
|
|
# /dev/mapper/library-library (knowledge/wiki/hosts/hubris.md)
|
|
- {slug: "pool:ludo-lvm", type: storage-pool, name: ludo-lvm (strong),
|
|
attributes: {type: lvm}}
|
|
- {slug: "volume:library", type: volume, name: library,
|
|
attributes: {path: /mnt/library, size_gb: 3700}}
|
|
- {slug: "volume:media-local", type: volume, name: media-local,
|
|
attributes: {path: /mnt/media_local}}
|
|
- {slug: "backup:proton-drive", type: backup-target, name: proton-drive,
|
|
attributes: {provider: proton, encrypted: true,
|
|
path: /mnt/backup,
|
|
note: "rclone stages here before pushing to Proton; freshness is checked on lxc:rclone via the backs-up-to edge"}}
|
|
# The pre-deploy pg_dump written by scripts/deploy.sh on every push to main.
|
|
# It was the lab's only untracked backup: its failure path is `|| echo
|
|
# WARNING` inside the deploy script, so a broken dump was invisible until a
|
|
# rollback needed it.
|
|
- {slug: "backup:oikos-predeploy", type: backup-target, name: oikos-predeploy,
|
|
attributes: {provider: local, encrypted: false,
|
|
path: /opt/oikos/backups,
|
|
note: "pre-deploy pg_dump on the mac-mini; one per deployed SHA"}}
|
|
|
|
# ─── Services ──────────────────────────────────────────────────────
|
|
- {slug: "service:proxmox-ui", type: service, name: proxmox_ui,
|
|
attributes: {url: "https://proxmox.hubris.network", port: 8006,
|
|
doc_page: knowledge/wiki/hosts/hubris.md,
|
|
risk_notes: "hypervisor UI — changes affect every guest on the node"}}
|
|
- {slug: "service:gitea", type: service, name: gitea,
|
|
attributes: {url: "https://git.hubris.network", port: 3000,
|
|
doc_page: knowledge/wiki/containers/104-gitea.md,
|
|
risk_notes: "hosts all config repos + deploy webhooks; outage blocks auto-deploy and sync"}}
|
|
- {slug: "service:caddy", type: service, name: caddy,
|
|
attributes: {doc_page: knowledge/wiki/containers/121-caddy.md,
|
|
risk_notes: "wide blast radius — every *.hubris.network route rides on it"}}
|
|
- {slug: "service:authentik", type: service, name: authentik,
|
|
attributes: {url: "https://auth.hubris.network",
|
|
doc_page: knowledge/wiki/containers/106-auth-outpost.md,
|
|
note: "core on VPS since 2026-05-31; LAN outpost = auth-outpost (LXC 106) 192.168.8.6:9000",
|
|
risk_notes: "SSO provider — outage locks login to OIDC/forward-auth services"}}
|
|
- {slug: "service:dns", type: service, name: dns,
|
|
attributes: {doc_page: knowledge/wiki/containers/107-dns.md,
|
|
risk_notes: "LAN-wide resolver — misconfig breaks name resolution for every client"}}
|
|
- {slug: "service:jellyfin", type: service, name: jellyfin,
|
|
attributes: {url: "https://media.hubris.network",
|
|
doc_page: knowledge/wiki/containers/101-jellyfin.md,
|
|
risk_notes: "native Authentik OIDC (no forward-auth gate); VAAPI depends on GPU passthrough on strong"}}
|
|
- {slug: "service:nextcloud", type: service, name: nextcloud,
|
|
attributes: {url: "https://cloud.hubris.network",
|
|
doc_page: knowledge/wiki/containers/114-nextcloud.md}}
|
|
- {slug: "service:paperless", type: service, name: paperless,
|
|
attributes: {url: "https://paperless.hubris.network",
|
|
doc_page: knowledge/wiki/containers/103-paperless.md,
|
|
risk_notes: "document archive — data irreplaceable; DB operations are destructive-class"}}
|
|
- {slug: "service:matrix", type: service, name: matrix,
|
|
attributes: {url: "https://matrix.hubris.network",
|
|
doc_page: knowledge/wiki/containers/118-elementsynapse.md,
|
|
risk_notes: "alert/approval channel for Oikos — outage silences agent escalation"}}
|
|
- {slug: "service:photos", type: service, name: photos,
|
|
attributes: {url: "https://photos.hubris.network",
|
|
doc_page: knowledge/wiki/containers/120-mule-images.md}}
|
|
- {slug: "service:arr-stack", type: service, name: arr_stack,
|
|
attributes: {doc_page: knowledge/wiki/containers/122-arriman.md,
|
|
note: "jellyseerr / qbit / sab on docker compose"}}
|
|
- {slug: "service:artifacto", type: service, name: artifacto,
|
|
attributes: {url: "https://artifacto.hubris.network",
|
|
doc_page: knowledge/wiki/containers/105-apps.md}}
|
|
- {slug: "service:trmnl", type: service, name: trmnl,
|
|
attributes: {url: "https://trmnl.hubris.network",
|
|
doc_page: knowledge/wiki/containers/128-trmnl.md,
|
|
note: "TRMNL e-ink plugin middleware (polled by TRMNL cloud)"}}
|
|
- {slug: "service:zimaos", type: service, name: zimaos,
|
|
attributes: {url: "https://zimaos.hubris.network",
|
|
doc_page: knowledge/wiki/vms/100-zimaos.md}}
|
|
- {slug: "service:haos", type: service, name: haos,
|
|
attributes: {doc_page: knowledge/wiki/vms/108-haos.md}}
|
|
- {slug: "service:teddycloud", type: service, name: teddycloud,
|
|
attributes: {url: "https://teddy.hubris.network",
|
|
doc_page: knowledge/wiki/containers/131-teddycloud.md,
|
|
risk_notes: "no forward-auth gate — reachable by anyone on LAN/mesh"}}
|
|
# The Go control plane itself: api/scheduler/notifier/web on the mac-mini,
|
|
# and what mcp.hubris.network fronts since the cutover. It existed in the
|
|
# database (created outside the seed) but was never declared here, so a
|
|
# fresh seed could not resolve the routes-to edge below.
|
|
- {slug: "service:oikos", type: service, name: oikos,
|
|
attributes: {url: "https://oikos.hubris.network",
|
|
host: "ws:mac-mini",
|
|
ports: {api: 8090, web: 8091, nomos_gateway: 8092},
|
|
note: "homelab automation platform — api/scheduler/notifier/web on mac-mini docker compose (project name oikos)"}}
|
|
- {slug: "service:homelab-mcp", type: service, name: homelab_mcp,
|
|
attributes: {port: 9810, systemd_unit: homelab-mcp,
|
|
endpoint: "https://mcp.hubris.network/mcp",
|
|
doc_page: knowledge/wiki/infrastructure/homelab-context.md,
|
|
risk_notes: "agents' primary read surface — outage degrades every agent to grepping the clone"}}
|
|
- {slug: "service:secrets-issuance", type: service, name: secrets_issuance,
|
|
attributes: {port: 9820, systemd_unit: secrets-issuance,
|
|
endpoint: "https://secrets.hubris.network/issue",
|
|
doc_page: .agents/operations/agent-enrollment.md,
|
|
risk_notes: "identity issuance — security-sensitive; key operations are destructive-class"}}
|
|
# Services derived from hosts.public_host (no legacy services entry):
|
|
- {slug: "service:house", type: service, name: house,
|
|
attributes: {url: "https://house.hubris.network", note: "Yuvomi family planner (derived)"}}
|
|
- {slug: "service:grimmory", type: service, name: grimmory,
|
|
attributes: {url: "https://books.hubris.network", note: derived}}
|
|
- {slug: "service:seanime", type: service, name: seanime,
|
|
attributes: {url: "https://seanime.hubris.network", port: 43211, note: derived}}
|
|
- {slug: "service:romm", type: service, name: romm,
|
|
attributes: {url: "https://roms.hubris.network", note: derived}}
|
|
- {slug: "service:jellyseerr", type: service, name: jellyseerr,
|
|
attributes: {url: "https://jellyseerr.hubris.network", note: derived (arriman)}}
|
|
- {slug: "service:qbit", type: service, name: qbit,
|
|
attributes: {url: "https://qbit.hubris.network", note: derived (arriman)}}
|
|
- {slug: "service:sab", type: service, name: sab,
|
|
attributes: {url: "https://sab.hubris.network", note: "derived (arriman); forward-auth gated"}}
|
|
|
|
# ─── Config repos ──────────────────────────────────────────────────
|
|
- {slug: "repo:caddy-conf", type: config-repo, name: dtoro/caddy-conf}
|
|
- {slug: "repo:gitea-customizations", type: config-repo, name: dtoro/gitea-customizations}
|
|
- {slug: "repo:mule-image", type: config-repo, name: dtoro/mule-image}
|
|
- {slug: "repo:artifacto", type: config-repo, name: dtoro/Artifacto}
|
|
- {slug: "repo:terminalito", type: config-repo, name: dtoro/terminalito}
|
|
- {slug: "repo:homelab-docs", type: config-repo, name: dtoro/oikos}
|
|
|
|
# ─── Ingress routes (Caddy, *.hubris.network) ──────────────────────
|
|
- {slug: "ingress:proxmox.hubris.network", type: ingress-route, name: proxmox.hubris.network}
|
|
- {slug: "ingress:git.hubris.network", type: ingress-route, name: git.hubris.network}
|
|
- {slug: "ingress:auth.hubris.network", type: ingress-route, name: auth.hubris.network}
|
|
- {slug: "ingress:media.hubris.network", type: ingress-route, name: media.hubris.network}
|
|
- {slug: "ingress:cloud.hubris.network", type: ingress-route, name: cloud.hubris.network}
|
|
- {slug: "ingress:paperless.hubris.network", type: ingress-route, name: paperless.hubris.network,
|
|
attributes: {forward_auth: true}}
|
|
- {slug: "ingress:matrix.hubris.network", type: ingress-route, name: matrix.hubris.network}
|
|
- {slug: "ingress:photos.hubris.network", type: ingress-route, name: photos.hubris.network}
|
|
- {slug: "ingress:artifacto.hubris.network", type: ingress-route, name: artifacto.hubris.network}
|
|
- {slug: "ingress:trmnl.hubris.network", type: ingress-route, name: trmnl.hubris.network}
|
|
- {slug: "ingress:zimaos.hubris.network", type: ingress-route, name: zimaos.hubris.network}
|
|
- {slug: "ingress:teddy.hubris.network", type: ingress-route, name: teddy.hubris.network,
|
|
attributes: {forward_auth: false}}
|
|
- {slug: "ingress:mcp.hubris.network", type: ingress-route, name: mcp.hubris.network}
|
|
- {slug: "ingress:secrets.hubris.network", type: ingress-route, name: secrets.hubris.network}
|
|
- {slug: "ingress:house.hubris.network", type: ingress-route, name: house.hubris.network}
|
|
- {slug: "ingress:books.hubris.network", type: ingress-route, name: books.hubris.network}
|
|
- {slug: "ingress:seanime.hubris.network", type: ingress-route, name: seanime.hubris.network}
|
|
- {slug: "ingress:roms.hubris.network", type: ingress-route, name: roms.hubris.network}
|
|
- {slug: "ingress:jellyseerr.hubris.network", type: ingress-route, name: jellyseerr.hubris.network}
|
|
- {slug: "ingress:qbit.hubris.network", type: ingress-route, name: qbit.hubris.network}
|
|
- {slug: "ingress:sab.hubris.network", type: ingress-route, name: sab.hubris.network,
|
|
attributes: {forward_auth: true}}
|
|
|
|
# ─── Governance ────────────────────────────────────────────────────
|
|
- {slug: "person:dtoro", type: person, name: dtoro,
|
|
attributes: {matrix_id: "@dtoro:avispero"}}
|
|
- {slug: "idp:authentik", type: identity-provider, name: authentik,
|
|
attributes: {issuer: "https://auth.hubris.network", auth_mode: both}}
|
|
- {slug: "agent:nomos", type: agent, name: nomos,
|
|
state: active,
|
|
attributes: {gateway_port: 8092, session_mode: smart_approve, note: "Phase 4 — Docker gateway mode"}}
|
|
- {slug: "agent:oikos", type: agent, name: oikos,
|
|
state: planned,
|
|
attributes: {note: "the OS control loop itself (scheduler/actuator) as an actor"}}
|
|
|
|
# ─── Archaeology (state: destroyed — kept for "what happened to X?") ─
|
|
- {slug: "lxc:claudio-bot", type: lxc, name: claudio-bot, state: destroyed,
|
|
attributes: {pve_id: 123, destroyed: "2026-06-04", reason: "replaced by Nomos Agent on mac-mini"}}
|
|
- {slug: "lxc:plato", type: lxc, name: plato, state: destroyed,
|
|
attributes: {pve_id: 126, destroyed: "2026-06-28", reason: "notes workspace decommissioned; data at /mnt/library/documents/plato"}}
|
|
- {slug: "lxc:mule-photos-new", type: lxc, name: mule-photos-new, state: destroyed,
|
|
attributes: {pve_id: 127, destroyed: "2026-05-22", reason: "PhotoPrism test stack promoted to LXC 120"}}
|
|
- {slug: "lxc:heaper", type: lxc, name: heaper, state: destroyed,
|
|
attributes: {pve_id: 116, destroyed: "2026-05-14", reason: "decommissioned; data at /mnt/library/heaper"}}
|
|
- {slug: "lxc:syncthing", type: lxc, name: syncthing, state: destroyed,
|
|
attributes: {pve_id: 109, destroyed: "2026-05-14", reason: "decommissioned; library subtree was empty"}}
|
|
- {slug: "lxc:seafile", type: lxc, name: seafile, state: destroyed,
|
|
attributes: {pve_id: 125, destroyed: "2026-05-13", reason: "Seafile Pro evaluation rejected"}}
|
|
- {slug: "lxc:arr-yunohost", type: lxc, name: arr-yunohost, state: destroyed,
|
|
attributes: {pve_id: 100, destroyed: "2026-04-28", reason: "migrated to docker stack on arriman (LXC 122)"}}
|
|
- {slug: "lxc:flaresolverr", type: lxc, name: flaresolverr, state: destroyed,
|
|
attributes: {pve_id: 106, destroyed: "2026-04-28", reason: "folded into the arriman docker compose"}}
|
|
- {slug: "lxc:marimo", type: lxc, name: marimo, state: destroyed,
|
|
attributes: {pve_id: 107, destroyed: "2026-04-28", reason: decommissioned}}
|
|
- {slug: "lxc:photoprism", type: lxc, name: photoprism, state: destroyed,
|
|
attributes: {pve_id: 110, destroyed: "2026-04-28", reason: "replaced by mule-images (LXC 120)"}}
|
|
- {slug: "lxc:karakeep", type: lxc, name: karakeep, state: destroyed,
|
|
attributes: {pve_id: 111, destroyed: "2026-04-28", reason: decommissioned}}
|
|
- {slug: "lxc:immich", type: lxc, name: immich, state: destroyed,
|
|
attributes: {pve_id: 112, destroyed: "2026-04-28", reason: "replaced by mule-images (LXC 120)"}}
|
|
- {slug: "lxc:reticulum", type: lxc, name: reticulum, state: destroyed,
|
|
attributes: {pve_id: 115, destroyed: "2026-04-28", reason: decommissioned}}
|
|
|
|
relationships:
|
|
|
|
# ─── Cluster membership ────────────────────────────────────────────
|
|
- {source: "host:hubris", target: "cluster:homelab", type: member-of}
|
|
- {source: "host:strong", target: "cluster:homelab", type: member-of}
|
|
|
|
# ─── Location ──────────────────────────────────────────────────────
|
|
- {source: "host:hubris", target: "site:home", type: located-at}
|
|
- {source: "host:strong", target: "site:home", type: located-at}
|
|
- {source: "ws:mac-mini", target: "site:home", type: located-at}
|
|
- {source: "host:netbird-vps", target: "site:ionos-dc", type: located-at}
|
|
|
|
# ─── Hosting (machine → guest) ─────────────────────────────────────
|
|
- {source: "host:hubris", target: "lxc:nfs-export", type: hosts}
|
|
- {source: "host:hubris", target: "lxc:paperless", type: hosts}
|
|
- {source: "host:hubris", target: "lxc:gitea", type: hosts}
|
|
- {source: "host:hubris", target: "lxc:apps", type: hosts}
|
|
- {source: "host:hubris", target: "lxc:auth-outpost", type: hosts}
|
|
- {source: "host:hubris", target: "lxc:dns", type: hosts}
|
|
- {source: "host:hubris", target: "lxc:nextcloud", type: hosts}
|
|
- {source: "host:hubris", target: "lxc:sophia", type: hosts}
|
|
- {source: "host:hubris", target: "lxc:mule-images", type: hosts}
|
|
- {source: "host:hubris", target: "lxc:caddy", type: hosts}
|
|
- {source: "host:hubris", target: "lxc:trmnl", type: hosts}
|
|
- {source: "host:hubris", target: "lxc:teddycloud", type: hosts}
|
|
- {source: "host:hubris", target: "vm:zimaos", type: hosts}
|
|
- {source: "host:hubris", target: "vm:haos", type: hosts}
|
|
- {source: "host:strong", target: "lxc:jellyfin", type: hosts}
|
|
- {source: "host:strong", target: "lxc:elementsynapse", type: hosts}
|
|
- {source: "host:strong", target: "lxc:arriman", type: hosts}
|
|
- {source: "host:strong", target: "lxc:house", type: hosts}
|
|
- {source: "host:strong", target: "lxc:grimmory", type: hosts}
|
|
- {source: "host:strong", target: "lxc:seanime", type: hosts}
|
|
- {source: "host:strong", target: "lxc:romm", type: hosts}
|
|
- {source: "host:hubris", target: "lxc:rclone", type: hosts}
|
|
|
|
# ─── Service provision (compute → service) ─────────────────────────
|
|
- {source: "host:hubris", target: "service:proxmox-ui", type: provides}
|
|
- {source: "lxc:gitea", target: "service:gitea", type: provides}
|
|
- {source: "lxc:caddy", target: "service:caddy", type: provides}
|
|
- {source: "host:netbird-vps", target: "service:authentik", type: provides}
|
|
- {source: "lxc:dns", target: "service:dns", type: provides}
|
|
- {source: "lxc:jellyfin", target: "service:jellyfin", type: provides}
|
|
- {source: "lxc:nextcloud", target: "service:nextcloud", type: provides}
|
|
- {source: "lxc:paperless", target: "service:paperless", type: provides}
|
|
- {source: "lxc:elementsynapse", target: "service:matrix", type: provides}
|
|
- {source: "lxc:mule-images", target: "service:photos", type: provides}
|
|
- {source: "lxc:arriman", target: "service:arr-stack", type: provides}
|
|
- {source: "lxc:arriman", target: "service:jellyseerr", type: provides}
|
|
- {source: "lxc:arriman", target: "service:qbit", type: provides}
|
|
- {source: "lxc:arriman", target: "service:sab", type: provides}
|
|
- {source: "lxc:apps", target: "service:artifacto", type: provides}
|
|
- {source: "lxc:apps", target: "service:homelab-mcp", type: provides}
|
|
- {source: "lxc:apps", target: "service:secrets-issuance", type: provides}
|
|
- {source: "lxc:trmnl", target: "service:trmnl", type: provides}
|
|
- {source: "vm:zimaos", target: "service:zimaos", type: provides}
|
|
- {source: "vm:haos", target: "service:haos", type: provides}
|
|
- {source: "lxc:teddycloud", target: "service:teddycloud", type: provides}
|
|
- {source: "lxc:house", target: "service:house", type: provides}
|
|
- {source: "lxc:grimmory", target: "service:grimmory", type: provides}
|
|
- {source: "lxc:seanime", target: "service:seanime", type: provides}
|
|
- {source: "lxc:romm", target: "service:romm", type: provides}
|
|
|
|
# ─── Ingress → service ─────────────────────────────────────────────
|
|
- {source: "ingress:proxmox.hubris.network", target: "service:proxmox-ui", type: routes-to}
|
|
- {source: "ingress:git.hubris.network", target: "service:gitea", type: routes-to}
|
|
- {source: "ingress:auth.hubris.network", target: "service:authentik", type: routes-to}
|
|
- {source: "ingress:media.hubris.network", target: "service:jellyfin", type: routes-to}
|
|
- {source: "ingress:cloud.hubris.network", target: "service:nextcloud", type: routes-to}
|
|
- {source: "ingress:paperless.hubris.network", target: "service:paperless", type: routes-to}
|
|
- {source: "ingress:matrix.hubris.network", target: "service:matrix", type: routes-to}
|
|
- {source: "ingress:photos.hubris.network", target: "service:photos", type: routes-to}
|
|
- {source: "ingress:artifacto.hubris.network", target: "service:artifacto", type: routes-to}
|
|
- {source: "ingress:trmnl.hubris.network", target: "service:trmnl", type: routes-to}
|
|
- {source: "ingress:zimaos.hubris.network", target: "service:zimaos", type: routes-to}
|
|
- {source: "ingress:teddy.hubris.network", target: "service:teddycloud", type: routes-to}
|
|
# Re-pointed from service:homelab-mcp, which is deprecated — the Python MCP
|
|
# server on apps/105 was stopped at the Go cutover and mcp.hubris.network now
|
|
# fronts the Go api. Nomos recorded this correctly on 2026-07-12; the seed
|
|
# was the stale one, and re-asserting the old edge alongside it is what made
|
|
# ingress:mcp a cardinality violation.
|
|
- {source: "ws:mac-mini", target: "service:oikos", type: provides}
|
|
- {source: "ingress:mcp.hubris.network", target: "service:oikos", type: routes-to}
|
|
# Every public hostname is terminated by caddy. Without these the
|
|
# reverse proxy — the single widest point of failure in the lab —
|
|
# had a blast radius of one.
|
|
- {source: "ingress:proxmox.hubris.network", target: "service:caddy", type: served-by}
|
|
- {source: "ingress:git.hubris.network", target: "service:caddy", type: served-by}
|
|
- {source: "ingress:auth.hubris.network", target: "service:caddy", type: served-by}
|
|
- {source: "ingress:media.hubris.network", target: "service:caddy", type: served-by}
|
|
- {source: "ingress:cloud.hubris.network", target: "service:caddy", type: served-by}
|
|
- {source: "ingress:paperless.hubris.network", target: "service:caddy", type: served-by}
|
|
- {source: "ingress:matrix.hubris.network", target: "service:caddy", type: served-by}
|
|
- {source: "ingress:photos.hubris.network", target: "service:caddy", type: served-by}
|
|
- {source: "ingress:artifacto.hubris.network", target: "service:caddy", type: served-by}
|
|
- {source: "ingress:trmnl.hubris.network", target: "service:caddy", type: served-by}
|
|
- {source: "ingress:zimaos.hubris.network", target: "service:caddy", type: served-by}
|
|
- {source: "ingress:teddy.hubris.network", target: "service:caddy", type: served-by}
|
|
- {source: "ingress:mcp.hubris.network", target: "service:caddy", type: served-by}
|
|
- {source: "ingress:secrets.hubris.network", target: "service:caddy", type: served-by}
|
|
- {source: "ingress:house.hubris.network", target: "service:caddy", type: served-by}
|
|
- {source: "ingress:books.hubris.network", target: "service:caddy", type: served-by}
|
|
- {source: "ingress:seanime.hubris.network", target: "service:caddy", type: served-by}
|
|
- {source: "ingress:roms.hubris.network", target: "service:caddy", type: served-by}
|
|
- {source: "ingress:jellyseerr.hubris.network", target: "service:caddy", type: served-by}
|
|
- {source: "ingress:qbit.hubris.network", target: "service:caddy", type: served-by}
|
|
- {source: "ingress:sab.hubris.network", target: "service:caddy", type: served-by}
|
|
- {source: "ingress:secrets.hubris.network", target: "service:secrets-issuance", type: routes-to}
|
|
- {source: "ingress:house.hubris.network", target: "service:house", type: routes-to}
|
|
- {source: "ingress:books.hubris.network", target: "service:grimmory", type: routes-to}
|
|
- {source: "ingress:seanime.hubris.network", target: "service:seanime", type: routes-to}
|
|
- {source: "ingress:roms.hubris.network", target: "service:romm", type: routes-to}
|
|
- {source: "ingress:jellyseerr.hubris.network", target: "service:jellyseerr", type: routes-to}
|
|
- {source: "ingress:qbit.hubris.network", target: "service:qbit", type: routes-to}
|
|
- {source: "ingress:sab.hubris.network", target: "service:sab", type: routes-to}
|
|
|
|
# ─── Auth edges ────────────────────────────────────────────────────
|
|
- {source: "ingress:paperless.hubris.network", target: "idp:authentik", type: secured-by}
|
|
- {source: "ingress:sab.hubris.network", target: "idp:authentik", type: secured-by}
|
|
- {source: "service:jellyfin", target: "idp:authentik", type: authenticates-via}
|
|
- {source: "idp:authentik", target: "person:dtoro", type: authenticates}
|
|
|
|
# ─── Config repos ──────────────────────────────────────────────────
|
|
- {source: "service:caddy", target: "repo:caddy-conf", type: configured-by}
|
|
- {source: "service:gitea", target: "repo:gitea-customizations", type: configured-by}
|
|
- {source: "service:photos", target: "repo:mule-image", type: configured-by}
|
|
- {source: "service:artifacto", target: "repo:artifacto", type: configured-by}
|
|
- {source: "service:trmnl", target: "repo:terminalito", type: configured-by}
|
|
- {source: "service:homelab-mcp", target: "repo:homelab-docs", type: configured-by}
|
|
- {source: "service:secrets-issuance", target: "repo:homelab-docs", type: configured-by}
|
|
|
|
# ─── Service dependencies (blast-radius edges; grow over time) ─────
|
|
- {source: "service:paperless", target: "service:authentik", type: depends-on}
|
|
- {source: "service:homelab-mcp", target: "service:gitea", type: depends-on}
|
|
- {source: "service:jellyseerr", target: "service:jellyfin", type: depends-on}
|
|
- {source: "service:seanime", target: "service:qbit", type: depends-on}
|
|
- {source: "service:house", target: "service:paperless", type: depends-on}
|
|
- {source: "service:sab", target: "service:authentik", type: depends-on}
|
|
|
|
# ─── Network membership ────────────────────────────────────────────
|
|
- {source: "host:hubris", target: "lan:lab", type: connects-via}
|
|
- {source: "host:hubris", target: "mesh:netbird", type: connects-via}
|
|
- {source: "host:strong", target: "lan:household", type: connects-via}
|
|
- {source: "ws:mac-mini", target: "lan:household", type: connects-via}
|
|
- {source: "ws:mac-mini", target: "mesh:netbird", type: connects-via}
|
|
- {source: "ws:republic-laptop", target: "mesh:netbird", type: connects-via}
|
|
- {source: "host:netbird-vps", target: "mesh:netbird", type: connects-via}
|
|
- {source: "lxc:rclone", target: "mesh:netbird", type: connects-via}
|
|
- {source: "lxc:jellyfin", target: "lan:lab", type: connects-via}
|
|
- {source: "lxc:nfs-export", target: "lan:lab", type: connects-via}
|
|
- {source: "lxc:paperless", target: "lan:lab", type: connects-via}
|
|
- {source: "lxc:gitea", target: "lan:lab", type: connects-via}
|
|
- {source: "lxc:apps", target: "lan:lab", type: connects-via}
|
|
- {source: "lxc:apps", target: "mesh:tailscale", type: connects-via}
|
|
- {source: "lxc:auth-outpost", target: "lan:lab", type: connects-via}
|
|
- {source: "lxc:dns", target: "lan:lab", type: connects-via}
|
|
- {source: "lxc:nextcloud", target: "lan:lab", type: connects-via}
|
|
- {source: "lxc:elementsynapse", target: "lan:lab", type: connects-via}
|
|
- {source: "lxc:sophia", target: "lan:lab", type: connects-via}
|
|
- {source: "lxc:mule-images", target: "lan:lab", type: connects-via}
|
|
- {source: "lxc:caddy", target: "lan:lab", type: connects-via}
|
|
- {source: "lxc:arriman", target: "lan:lab", type: connects-via}
|
|
- {source: "lxc:trmnl", target: "lan:lab", type: connects-via}
|
|
- {source: "lxc:house", target: "lan:lab", type: connects-via}
|
|
- {source: "lxc:grimmory", target: "lan:lab", type: connects-via}
|
|
- {source: "lxc:teddycloud", target: "lan:lab", type: connects-via}
|
|
- {source: "lxc:seanime", target: "lan:lab", type: connects-via}
|
|
- {source: "lxc:romm", target: "lan:lab", type: connects-via}
|
|
- {source: "vm:zimaos", target: "lan:lab", type: connects-via}
|
|
- {source: "vm:haos", target: "lan:lab", type: connects-via}
|
|
|
|
# ─── Storage ───────────────────────────────────────────────────────
|
|
- {source: "pool:ludo-lvm", target: "volume:media-local", type: contains}
|
|
- {source: "pool:library-hubris", target: "volume:library", type: contains}
|
|
- {source: "host:hubris", target: "volume:library", type: mounts,
|
|
attributes: {mount_point: /mnt/library}}
|
|
- {source: "lxc:jellyfin", target: "volume:media-local", type: mounts,
|
|
attributes: {mount_point: /mnt/media_local}}
|
|
- {source: "lxc:paperless", target: "volume:library", type: mounts,
|
|
attributes: {mount_point: /mnt/library}}
|
|
- {source: "lxc:gitea", target: "volume:library", type: mounts,
|
|
attributes: {mount_point: /mnt/library}}
|
|
- {source: "lxc:apps", target: "volume:library", type: mounts,
|
|
attributes: {mount_point: /mnt/library}}
|
|
- {source: "lxc:nextcloud", target: "volume:library", type: mounts,
|
|
attributes: {mount_point: /mnt/library}}
|
|
- {source: "lxc:sophia", target: "volume:library", type: mounts,
|
|
attributes: {mount_point: /mnt/library}}
|
|
- {source: "lxc:mule-images", target: "volume:library", type: mounts,
|
|
attributes: {mount_point: /mnt/library}}
|
|
- {source: "lxc:arriman", target: "volume:media-local", type: mounts,
|
|
attributes: {mount_point: /mnt/media_local}}
|
|
- {source: "lxc:grimmory", target: "volume:media-local", type: mounts,
|
|
attributes: {mount_point: /mnt/media_local}}
|
|
- {source: "lxc:teddycloud", target: "volume:library", type: mounts,
|
|
attributes: {mount_point: /mnt/library}}
|
|
- {source: "lxc:seanime", target: "volume:media-local", type: mounts,
|
|
attributes: {mount_point: /mnt/media_local/anime}}
|
|
- {source: "lxc:romm", target: "volume:media-local", type: mounts,
|
|
attributes: {mount_point: /mnt/media_local}}
|
|
- {source: "lxc:jellyfin", target: "pool:ludo-lvm", type: stores-on}
|
|
- {source: "lxc:arriman", target: "pool:ludo-lvm", type: stores-on}
|
|
- {source: "lxc:grimmory", target: "pool:ludo-lvm", type: stores-on}
|
|
- {source: "lxc:romm", target: "pool:ludo-lvm", type: stores-on}
|
|
- {source: "lxc:teddycloud", target: "pool:local-lvm-hubris", type: stores-on}
|
|
- {source: "lxc:rclone", target: "backup:proton-drive", type: backs-up-to}
|
|
# The mac-mini writes the pre-deploy dumps, so it is also where the freshness
|
|
# check runs — checkdefaults resolves a backup-target's host by walking this
|
|
# edge backwards.
|
|
- {source: "ws:mac-mini", target: "backup:oikos-predeploy", type: backs-up-to}
|
|
|
|
# ─── Governance ────────────────────────────────────────────────────
|
|
- {source: "person:dtoro", target: "agent:nomos", type: owns}
|
|
- {source: "person:dtoro", target: "agent:oikos", type: owns}
|