v0.119.0 — the host report carries the box's addresses
A managed box's IP was invisible in every operator surface because nothing reported one: HostMetrics carried node/cpu/mem/disk/load/uptime/temp/wrapper-sha and no address of any kind. The hub could not show a host's LAN IP anywhere. Two things that looked like the answer are traps, both checked before writing code: lan_resolver.host_ip is an OPTIONAL config value absent unless that feature is configured, and DeriveHostIP(local_api.listen_addr) returns 169.254.253.1 — since R-50 the local API binds a link-local address identical on every box. Both would have produced a confident wrong answer. New wire field addresses[], one entry per (interface, address). Deliberately iface+cidr rather than a single lan_ip: a Proxmox host legitimately holds several (management bridge, tailnet, WG tunnel) and picking one to call "the" LAN IP is a guess the agent is not entitled to make — silently wrong on a box whose bridge is not vmbr0. The agent reports what exists; the hub does the labelling. The filter is one predicate, chosen by MEASURING both demo hosts rather than by reasoning about interface names. IsGlobalUnicast() alone drops loopback, IPv6 link-local (one per bridge, pure noise) and IPv4 link-local (169.254/16 — exactly the island address above). It needs no veth/fwbr/tap denylist: that per-guest plumbing carries no IP at all and self-excludes, verified on both boxes. No new privilege and no block I/O — net.Interfaces() is a netlink/procfs read, so the sudoers fence is untouched and the health-check rule is honoured. The seam DEFAULTS to the real enumerator, inverting the nil-reporter-means-off convention: this stanza has no config gate, so a forgotten wiring call would have shipped it silently empty — the inert-seam failure recorded four times here. Cross-repo: the golden is duplicated byte-identically in felhom.eu and the contract test fails on top-level key drift, so both goldens moved together and addresses[0]'s key set is asserted bidirectionally. The field marshals as [], never null — the repo's own no-nulls invariant caught that on the first run. Tests +9; three red-proofs (global-unicast filter, down-interface guard, inert collectAddresses) each run, observed failing, and reverted.
This commit is contained in:
@@ -83,6 +83,7 @@ type Collector struct {
|
||||
temp TempReader // slice 9: host CPU/chassis temp (nil-safe → nil temp)
|
||||
capProbe func(ctx context.Context) []capability.Status // v0.44.0: privileged-capability self-check (nil → empty)
|
||||
leafFP string // v0.48.0: served local-API leaf fp (static per process; "" when local API disabled)
|
||||
addrEnum AddressEnumerator // v0.119.0: host interface enumeration; nil => the REAL one (see collectAddresses)
|
||||
wg WireguardReporter // S3: offsite-tunnel status (nil → stanza omitted)
|
||||
pbsdr PBSDRReporter // slice 2: PBS DR tier bridge state (nil → stanza omitted)
|
||||
guestNet GuestNetReporter // R-54: per-guest network watchdog (nil → stanza omitted)
|
||||
@@ -253,6 +254,7 @@ func (c *Collector) Collect(ctx context.Context) (*HostReport, error) {
|
||||
Cloudflared: Cloudflared{Status: c.cloudflaredStatus(ctx)},
|
||||
Capabilities: c.capabilities(ctx),
|
||||
LeafFingerprint: c.leafFP,
|
||||
Addresses: c.collectAddresses(),
|
||||
}
|
||||
// DR recipe host-half — derived from the just-collected guest/storage/PBS facts (no new reads).
|
||||
// Secret-free by construction (identifiers/intents/sizes/coordinates only).
|
||||
|
||||
Reference in New Issue
Block a user