inventory: real mesh state per host (no more placeholder netbird FQDNs)

Audit against actual netbird+tailscale peer lists:
  - hubris is the only LXC-host on Netbird; only workstations + hubris
    have netbird entries
  - 10 LXCs+VMs have real Tailscale FQDNs: apps, jellyfin, paperless,
    gitea, nextcloud, elementsynapse, sophia, mule-images→muleimage,
    arriman→arr, haos→homeassistant
  - 7 hosts are LAN-only (no mesh block): nfs-export, caddy, claudio-bot,
    authentik, plato, mule-photos-new, zimaos
  - mac-mini's netbird FQDN corrected to the actual peer name
    (mac-mini-234-17.netbird.selfhosted)

Also: bin/homelab host_address() now prefers lan_ip first — universally
reachable from any LAN client and from any Netbird peer via the
192.168.8.0/24 network resource routed through hubris. Mesh FQDNs are
fallbacks for roaming workstations without a fixed lan_ip.

This makes 'homelab status' from republic show all backends 'ok' instead
of falsely reporting them 'down' against unresolvable netbird FQDNs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
root
2026-05-20 21:28:03 +02:00
parent e7a74f795d
commit 36a686d953
21 changed files with 56 additions and 103 deletions

View File

@@ -55,18 +55,25 @@ def host(name: str) -> dict:
def host_address(name: str, prefer_lan: bool = False) -> str: def host_address(name: str, prefer_lan: bool = False) -> str:
"""Best-effort single address for `name`.
Preference order is lan_ip first because:
- From any LAN client it's a direct route.
- From any Netbird peer it's routed through hubris's
192.168.8.0/24 network resource.
- From any Tailscale peer with subnet routing it works too.
Mesh FQDNs are fallbacks for hosts without a lan_ip (e.g. roaming
workstations).
"""
h = host(name) h = host(name)
mesh = h.get("mesh", {}) mesh = h.get("mesh", {})
candidates = [] nb = mesh.get("netbird") if isinstance(mesh.get("netbird"), dict) else {}
if prefer_lan and h.get("lan_ip"): ts = mesh.get("tailscale") if isinstance(mesh.get("tailscale"), dict) else {}
candidates.append(h["lan_ip"]) candidates = [
candidates.extend([
mesh.get("netbird", {}).get("fqdn") if isinstance(mesh.get("netbird"), dict) else None,
mesh.get("netbird", {}).get("ip") if isinstance(mesh.get("netbird"), dict) else None,
h.get("lan_ip"), h.get("lan_ip"),
mesh.get("tailscale", {}).get("fqdn") if isinstance(mesh.get("tailscale"), dict) else None, nb.get("fqdn"), nb.get("ip"),
mesh.get("tailscale", {}).get("ip") if isinstance(mesh.get("tailscale"), dict) else None, ts.get("fqdn"), ts.get("ip"),
]) ]
for c in candidates: for c in candidates:
if c: if c:
return c return c

View File

@@ -9,10 +9,9 @@ host: hubris
pve_id: 105 pve_id: 105
lan_ip: 192.168.8.205 lan_ip: 192.168.8.205
mesh: mesh:
netbird:
fqdn: apps.netbird.selfhosted
tailscale: tailscale:
ip: 100.121.171.122 ip: 100.121.171.122
fqdn: apps
mesh_globals: mesh_globals:
primary: netbird primary: netbird
accepted: accepted:

View File

@@ -9,8 +9,8 @@ host: hubris
pve_id: 122 pve_id: 122
lan_ip: 192.168.8.132 lan_ip: 192.168.8.132
mesh: mesh:
netbird: tailscale:
fqdn: arriman.netbird.selfhosted fqdn: arr
mesh_globals: mesh_globals:
primary: netbird primary: netbird
accepted: accepted:

View File

@@ -8,9 +8,6 @@ role: idp
host: hubris host: hubris
pve_id: 124 pve_id: 124
lan_ip: 192.168.8.180 lan_ip: 192.168.8.180
mesh:
netbird:
fqdn: authentik.netbird.selfhosted
mesh_globals: mesh_globals:
primary: netbird primary: netbird
accepted: accepted:

View File

@@ -8,9 +8,6 @@ role: reverse-proxy
host: hubris host: hubris
pve_id: 121 pve_id: 121
lan_ip: 192.168.8.175 lan_ip: 192.168.8.175
mesh:
netbird:
fqdn: caddy.netbird.selfhosted
mesh_globals: mesh_globals:
primary: netbird primary: netbird
accepted: accepted:

View File

@@ -8,9 +8,6 @@ role: matrix-agent
host: hubris host: hubris
pve_id: 123 pve_id: 123
lan_ip: 192.168.8.230 lan_ip: 192.168.8.230
mesh:
netbird:
fqdn: claudio-bot.netbird.selfhosted
mesh_globals: mesh_globals:
primary: netbird primary: netbird
accepted: accepted:

View File

@@ -9,8 +9,8 @@ host: hubris
pve_id: 118 pve_id: 118
lan_ip: 192.168.8.239 lan_ip: 192.168.8.239
mesh: mesh:
netbird: tailscale:
fqdn: elementsynapse.netbird.selfhosted fqdn: elementsynapse
mesh_globals: mesh_globals:
primary: netbird primary: netbird
accepted: accepted:

View File

@@ -9,8 +9,8 @@ host: hubris
pve_id: 104 pve_id: 104
lan_ip: 192.168.8.121 lan_ip: 192.168.8.121
mesh: mesh:
netbird: tailscale:
fqdn: gitea.netbird.selfhosted fqdn: gitea
mesh_globals: mesh_globals:
primary: netbird primary: netbird
accepted: accepted:

View File

@@ -8,8 +8,8 @@ role: home-automation
host: hubris host: hubris
pve_id: 108 pve_id: 108
mesh: mesh:
netbird: tailscale:
fqdn: haos.netbird.selfhosted fqdn: homeassistant
mesh_globals: mesh_globals:
primary: netbird primary: netbird
accepted: accepted:

View File

@@ -9,8 +9,8 @@ host: hubris
pve_id: 101 pve_id: 101
lan_ip: 192.168.8.206 lan_ip: 192.168.8.206
mesh: mesh:
netbird: tailscale:
fqdn: jellyfin.netbird.selfhosted fqdn: jellyfin
mesh_globals: mesh_globals:
primary: netbird primary: netbird
accepted: accepted:

View File

@@ -7,7 +7,7 @@ os: macos
role: dev role: dev
mesh: mesh:
netbird: netbird:
fqdn: mac-mini.netbird.selfhosted fqdn: mac-mini-234-17.netbird.selfhosted
mesh_globals: mesh_globals:
primary: netbird primary: netbird
accepted: accepted:

View File

@@ -9,8 +9,8 @@ host: hubris
pve_id: 120 pve_id: 120
lan_ip: 192.168.8.136 lan_ip: 192.168.8.136
mesh: mesh:
netbird: tailscale:
fqdn: mule-images.netbird.selfhosted fqdn: muleimage
mesh_globals: mesh_globals:
primary: netbird primary: netbird
accepted: accepted:

View File

@@ -8,9 +8,6 @@ role: photo-management
host: hubris host: hubris
pve_id: 127 pve_id: 127
lan_ip: 192.168.8.181 lan_ip: 192.168.8.181
mesh:
netbird:
fqdn: mule-photos-new.netbird.selfhosted
mesh_globals: mesh_globals:
primary: netbird primary: netbird
accepted: accepted:

View File

@@ -9,8 +9,8 @@ host: hubris
pve_id: 114 pve_id: 114
lan_ip: 192.168.8.224 lan_ip: 192.168.8.224
mesh: mesh:
netbird: tailscale:
fqdn: nextcloud.netbird.selfhosted fqdn: nextcloud
mesh_globals: mesh_globals:
primary: netbird primary: netbird
accepted: accepted:

View File

@@ -8,9 +8,6 @@ role: storage-export
host: hubris host: hubris
pve_id: 102 pve_id: 102
lan_ip: 192.168.8.200 lan_ip: 192.168.8.200
mesh:
netbird:
fqdn: nfs-export.netbird.selfhosted
mesh_globals: mesh_globals:
primary: netbird primary: netbird
accepted: accepted:

View File

@@ -9,8 +9,8 @@ host: hubris
pve_id: 103 pve_id: 103
lan_ip: 192.168.8.130 lan_ip: 192.168.8.130
mesh: mesh:
netbird: tailscale:
fqdn: paperless.netbird.selfhosted fqdn: paperless
mesh_globals: mesh_globals:
primary: netbird primary: netbird
accepted: accepted:

View File

@@ -8,9 +8,6 @@ role: app
host: hubris host: hubris
pve_id: 126 pve_id: 126
lan_ip: 192.168.8.190 lan_ip: 192.168.8.190
mesh:
netbird:
fqdn: plato.netbird.selfhosted
mesh_globals: mesh_globals:
primary: netbird primary: netbird
accepted: accepted:

View File

@@ -13,6 +13,5 @@ mesh_globals:
accepted: accepted:
- netbird - netbird
- tailscale - tailscale
age_pubkey: age1vf8h7s8mqsn2q5eadgpdupsj4mwn8zguc77d85ws3xj40sl9rgksx2rxw6
mcp_endpoint: https://mcp.hubris.network/sse mcp_endpoint: https://mcp.hubris.network/sse
secrets_issuance_endpoint: https://secrets.hubris.network/issue secrets_issuance_endpoint: https://secrets.hubris.network/issue

View File

@@ -9,8 +9,8 @@ host: hubris
pve_id: 119 pve_id: 119
lan_ip: 192.168.8.157 lan_ip: 192.168.8.157
mesh: mesh:
netbird: tailscale:
fqdn: sophia.netbird.selfhosted fqdn: sophia
mesh_globals: mesh_globals:
primary: netbird primary: netbird
accepted: accepted:

View File

@@ -7,9 +7,6 @@ os: linux
role: nas-frontend-eval role: nas-frontend-eval
host: hubris host: hubris
pve_id: 100 pve_id: 100
mesh:
netbird:
fqdn: zimaos.netbird.selfhosted
mesh_globals: mesh_globals:
primary: netbird primary: netbird
accepted: accepted:

View File

@@ -119,8 +119,8 @@ hosts:
lan_ip: 192.168.8.206 lan_ip: 192.168.8.206
public_host: media.hubris.network public_host: media.hubris.network
mesh: mesh:
netbird: tailscale:
fqdn: jellyfin.netbird.selfhosted fqdn: jellyfin
mounts: mounts:
- /mnt/library - /mnt/library
age_pubkey: '' age_pubkey: ''
@@ -131,10 +131,6 @@ hosts:
os: linux os: linux
role: storage-export role: storage-export
lan_ip: 192.168.8.200 lan_ip: 192.168.8.200
mesh:
netbird:
fqdn: nfs-export.netbird.selfhosted
age_pubkey: ''
paperless: paperless:
kind: lxc kind: lxc
pve_id: 103 pve_id: 103
@@ -144,8 +140,8 @@ hosts:
lan_ip: 192.168.8.130 lan_ip: 192.168.8.130
public_host: paperless.hubris.network public_host: paperless.hubris.network
mesh: mesh:
netbird: tailscale:
fqdn: paperless.netbird.selfhosted fqdn: paperless
mounts: mounts:
- /mnt/library - /mnt/library
age_pubkey: '' age_pubkey: ''
@@ -159,8 +155,8 @@ hosts:
public_host: git.hubris.network public_host: git.hubris.network
backend_port: 3000 backend_port: 3000
mesh: mesh:
netbird: tailscale:
fqdn: gitea.netbird.selfhosted fqdn: gitea
mounts: mounts:
- /mnt/library - /mnt/library
notes: notes:
@@ -176,10 +172,9 @@ hosts:
public_hosts: public_hosts:
- artifacto.hubris.network - artifacto.hubris.network
mesh: mesh:
netbird:
fqdn: apps.netbird.selfhosted
tailscale: tailscale:
ip: 100.121.171.122 ip: 100.121.171.122
fqdn: apps
mounts: mounts:
- /mnt/library - /mnt/library
runs: runs:
@@ -197,8 +192,8 @@ hosts:
lan_ip: 192.168.8.224 lan_ip: 192.168.8.224
public_host: cloud.hubris.network public_host: cloud.hubris.network
mesh: mesh:
netbird: tailscale:
fqdn: nextcloud.netbird.selfhosted fqdn: nextcloud
mounts: mounts:
- /mnt/library - /mnt/library
age_pubkey: '' age_pubkey: ''
@@ -211,9 +206,8 @@ hosts:
lan_ip: 192.168.8.239 lan_ip: 192.168.8.239
public_host: matrix.hubris.network public_host: matrix.hubris.network
mesh: mesh:
netbird: tailscale:
fqdn: elementsynapse.netbird.selfhosted fqdn: elementsynapse
age_pubkey: ''
sophia: sophia:
kind: lxc kind: lxc
pve_id: 119 pve_id: 119
@@ -222,8 +216,8 @@ hosts:
role: workshop role: workshop
lan_ip: 192.168.8.157 lan_ip: 192.168.8.157
mesh: mesh:
netbird: tailscale:
fqdn: sophia.netbird.selfhosted fqdn: sophia
mounts: mounts:
- /mnt/library - /mnt/library
age_pubkey: '' age_pubkey: ''
@@ -236,8 +230,8 @@ hosts:
lan_ip: 192.168.8.136 lan_ip: 192.168.8.136
public_host: photos.hubris.network public_host: photos.hubris.network
mesh: mesh:
netbird: tailscale:
fqdn: mule-images.netbird.selfhosted fqdn: muleimage
mounts: mounts:
- /mnt/library - /mnt/library
age_pubkey: '' age_pubkey: ''
@@ -254,10 +248,6 @@ hosts:
peers: peers:
- authentik - authentik
- gitea - gitea
mesh:
netbird:
fqdn: caddy.netbird.selfhosted
age_pubkey: ''
arriman: arriman:
kind: lxc kind: lxc
pve_id: 122 pve_id: 122
@@ -270,8 +260,8 @@ hosts:
- qbit.hubris.network - qbit.hubris.network
- sab.hubris.network - sab.hubris.network
mesh: mesh:
netbird: tailscale:
fqdn: arriman.netbird.selfhosted fqdn: arr
mounts: mounts:
- /mnt/library - /mnt/library
age_pubkey: '' age_pubkey: ''
@@ -282,9 +272,6 @@ hosts:
os: linux os: linux
role: matrix-agent role: matrix-agent
lan_ip: 192.168.8.230 lan_ip: 192.168.8.230
mesh:
netbird:
fqdn: claudio-bot.netbird.selfhosted
notes: notes:
- Reads /opt/homelab-context/ on startup - Reads /opt/homelab-context/ on startup
age_pubkey: age1xmkeq968areza2necqyq0065dpeegngzyr6dhagh0n6pl33lccfqe5mqn9 age_pubkey: age1xmkeq968areza2necqyq0065dpeegngzyr6dhagh0n6pl33lccfqe5mqn9
@@ -298,10 +285,6 @@ hosts:
public_host: auth.hubris.network public_host: auth.hubris.network
notes: notes:
- 'Also hosts split-horizon dnsmasq: /etc/dnsmasq.d/hubris-split.conf' - 'Also hosts split-horizon dnsmasq: /etc/dnsmasq.d/hubris-split.conf'
mesh:
netbird:
fqdn: authentik.netbird.selfhosted
age_pubkey: ''
plato: plato:
kind: lxc kind: lxc
pve_id: 126 pve_id: 126
@@ -310,9 +293,6 @@ hosts:
role: app role: app
lan_ip: 192.168.8.190 lan_ip: 192.168.8.190
public_host: plato.hubris.network public_host: plato.hubris.network
mesh:
netbird:
fqdn: plato.netbird.selfhosted
mounts: mounts:
- /mnt/library/documents/plato - /mnt/library/documents/plato
age_pubkey: '' age_pubkey: ''
@@ -324,10 +304,6 @@ hosts:
role: photo-management role: photo-management
lan_ip: 192.168.8.181 lan_ip: 192.168.8.181
public_host: photos-new.hubris.network public_host: photos-new.hubris.network
mesh:
netbird:
fqdn: mule-photos-new.netbird.selfhosted
age_pubkey: ''
zimaos: zimaos:
kind: vm kind: vm
pve_id: 100 pve_id: 100
@@ -335,10 +311,6 @@ hosts:
os: linux os: linux
role: nas-frontend-eval role: nas-frontend-eval
public_host: zimaos.hubris.network public_host: zimaos.hubris.network
mesh:
netbird:
fqdn: zimaos.netbird.selfhosted
age_pubkey: ''
haos: haos:
kind: vm kind: vm
pve_id: 108 pve_id: 108
@@ -346,9 +318,8 @@ hosts:
os: linux os: linux
role: home-automation role: home-automation
mesh: mesh:
netbird: tailscale:
fqdn: haos.netbird.selfhosted fqdn: homeassistant
age_pubkey: ''
republic-laptop: republic-laptop:
kind: workstation kind: workstation
os: linux os: linux
@@ -356,14 +327,13 @@ hosts:
mesh: mesh:
netbird: netbird:
fqdn: republic-laptop.netbird.selfhosted fqdn: republic-laptop.netbird.selfhosted
age_pubkey: age1vf8h7s8mqsn2q5eadgpdupsj4mwn8zguc77d85ws3xj40sl9rgksx2rxw6
mac-mini: mac-mini:
kind: workstation kind: workstation
os: macos os: macos
role: dev role: dev
mesh: mesh:
netbird: netbird:
fqdn: mac-mini.netbird.selfhosted fqdn: mac-mini-234-17.netbird.selfhosted
notes: notes:
- Only macOS in the fleet. Bootstrap uses launchd. - Only macOS in the fleet. Bootstrap uses launchd.
age_pubkey: '' age_pubkey: ''
@@ -374,4 +344,3 @@ hosts:
mesh: mesh:
netbird: netbird:
fqdn: ludo-mini.netbird.selfhosted fqdn: ludo-mini.netbird.selfhosted
age_pubkey: ''