fix(mesh): treat local-node aliases as always reachable in diagnostics (#994)

MeshService.getRouteDiagnostic called PilotTunnelManager.hasActiveTunnel
unconditionally for the alias's target node. Local nodes never establish
pilot tunnels because they do not need them (mesh same-node traffic uses
the localhost fast path), so the call always returned false and the
diagnostic flipped to state: 'tunnel down' even on a working local
route. The route detail sheet and node card consequently rendered every
local mesh alias as a destructive 'tunnel down' pill.

Added a private isMeshReachable helper that returns true for any
local-type node and otherwise delegates to hasActiveTunnel. Used it in
both getRouteDiagnostic and getStatus, replacing the inline ternary in
getStatus that already had the right shape but lived as duplicated
logic. The helper falls back to false when the node row is missing
(orphaned alias from a partial cascade), matching the pre-existing
conservative behavior.

Verified live: GET /api/mesh/aliases/echo.audit-mesh-prod.Local.sencho/diagnostic
previously returned state='tunnel down' on a healthy local route.
This commit is contained in:
Anso
2026-05-08 11:38:35 -04:00
committed by GitHub
parent f7ff7955ee
commit 34581e55e7
2 changed files with 161 additions and 4 deletions
+18 -4
View File
@@ -669,6 +669,22 @@ export class MeshService extends EventEmitter {
// --- Diagnostics ---
/**
* Whether mesh traffic to this node can flow. Local nodes are always
* reachable because mesh uses the same-node fast path on localhost.
* Remote nodes are reachable only when a pilot tunnel is registered. The
* literal `hasActiveTunnel(localNodeId)` would always be false (local
* nodes do not establish tunnels to themselves), so a direct call would
* render every local alias as `tunnel down` in the UI even on a working
* route.
*/
private isMeshReachable(nodeId: number): boolean {
const node = DatabaseService.getInstance().getNode(nodeId);
if (!node) return false;
if (node.type !== 'remote') return true;
return PilotTunnelManager.getInstance().hasActiveTunnel(nodeId);
}
public async getRouteDiagnostic(alias: string): Promise<MeshRouteDiagnostic> {
const target = this.lookupAliasGlobal(alias);
const lastError = this.routeErrorMap.get(alias) || null;
@@ -678,8 +694,7 @@ export class MeshService extends EventEmitter {
return { alias, target: null, pilot: { connected: false, lastSeen: null }, lastError, lastProbeMs, state: 'not authorized' };
}
const ptm = PilotTunnelManager.getInstance();
const pilotConnected = ptm.hasActiveTunnel(target.nodeId);
const pilotConnected = this.isMeshReachable(target.nodeId);
const node = DatabaseService.getInstance().getNode(target.nodeId);
const lastSeen = node?.pilot_last_seen ?? null;
const optedIn = DatabaseService.getInstance().isMeshStackEnabled(target.nodeId, target.stackName);
@@ -739,7 +754,6 @@ export class MeshService extends EventEmitter {
public async getStatus(): Promise<MeshNodeStatus[]> {
const db = DatabaseService.getInstance();
const ptm = PilotTunnelManager.getInstance();
const nodes = db.getNodes();
const out: MeshNodeStatus[] = [];
for (const node of nodes) {
@@ -749,7 +763,7 @@ export class MeshService extends EventEmitter {
nodeName: node.name,
enabled: db.getNodeMeshEnabled(node.id),
sidecarRunning: await this.isSidecarRunning(node.id),
pilotConnected: node.type === 'remote' ? ptm.hasActiveTunnel(node.id) : true,
pilotConnected: this.isMeshReachable(node.id),
optedInStacks,
activeStreamCount: Array.from(this.activeStreams.values()).length,
});