Include self in dashboard-v2 fleet aggregation
Build with clawstor cache / Cargo build (clawstor-cached) (pull_request) Failing after 3s

V2State::from_config built its peer fan-out list solely from
[[cluster.peers]], which by definition never includes the local
node. Result: hitting a given node's /api/v2/fleet directly always
omitted that node from its own fleet view, even when perfectly
healthy -- looked like "node X is missing" from the dashboard when
X just never queried itself.

Fix: synthesize a self PeerEntry from the node's own gossip bind
address and include it in the fan-out, same as any other peer.
peer_rpc_addr()'s existing +1 port convention resolves it to the
same bind_rpc_lan/bind_rpc_tailscale the daemon already listens on.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
osobh
2026-07-31 13:01:02 -07:00
co-authored by Claude Sonnet 5
parent 75d822d0f2
commit bb24c77676
+22 -1
View File
@@ -91,9 +91,30 @@ impl V2State {
.join("aggregator-sessions.json");
let sessions = SessionStore::load(sessions_path)
.map_err(|e| anyhow::anyhow!("loading session store: {e}"))?;
// Bug fix 2026-07-31: the fleet view previously never included
// the node actually serving the dashboard — `cluster.peers` is
// by definition every *other* node, so hitting a given node's
// `/api/v2/fleet` directly silently dropped that node from its
// own view (looked like "node X is missing" from the UI, even
// though X was perfectly healthy — it just never queried
// itself). Fix: synthesize a self `PeerEntry` from our own
// gossip bind address and include it in the fan-out list, same
// as any other peer. `peer_rpc_addr` derives the RPC port from
// `lan_addr`/`tailscale_addr` via the fleet's +1 convention, so
// this resolves to the same `bind_rpc_lan`/`bind_rpc_tailscale`
// the daemon actually listens on.
let self_peer = PeerEntry {
name: cfg.node.name.clone(),
zone: cluster.zone.clone(),
lan_addr: cluster.bind_lan,
tailscale_addr: cluster.bind_tailscale,
};
let mut peers = cluster.peers.clone();
peers.push(self_peer);
Ok(Self {
aggregator_name: cfg.node.name.clone(),
peers: cluster.peers.clone(),
peers,
client: std::sync::Arc::new(client),
default_rpc_port_offset: 1,
api_token: cfg.api_token.clone(),