fix(mesh): surface data-plane failures in health, meta, and Routing tab (#1088)

The three previously-silent console.warn paths in MeshService.setupMeshNetwork
now route through a typed recordSetupFailure helper that classifies the
failure (subnet_invalid, subnet_overlap, subnet_mismatch, ip_in_use,
attach_failed, not_in_docker), emits a mesh.disable activity entry at the
matching level (error for real failures, warn for the expected dev-mode
not_in_docker case), and strips mesh_proxy_callback_bootstrap from
advertised capabilities via CapabilityRegistry.

/api/health gains a mesh.dataPlane block carrying the typed status.
/api/mesh/status carries localDataPlane at the top level so the Routing tab
renders a red banner with an operator-actionable recovery hint (set
SENCHO_MESH_SUBNET to a free /24 and recreate the container) when the data
plane is down. The success path re-enables the capability and flips the
status to ok.

Generalizes the previously-documented F-0 failure mode (IP-in-subnet
collision) to also cover the subnet-pool-overlap case where another Docker
bridge on the host already owns the requested CIDR.
This commit is contained in:
Anso
2026-05-17 17:31:57 -04:00
committed by GitHub
parent f11e5ef58c
commit 578cac89da
9 changed files with 497 additions and 36 deletions
+3 -2
View File
@@ -16,8 +16,9 @@ function actorFor(req: Request): string {
meshRouter.get('/status', async (_req: Request, res: Response): Promise<void> => {
if (!requireAdmiral(_req, res)) return;
try {
const status = await MeshService.getInstance().getStatus();
res.json({ nodes: status });
const mesh = MeshService.getInstance();
const status = await mesh.getStatus();
res.json({ nodes: status, localDataPlane: mesh.getDataPlaneStatus() });
} catch (err) {
console.warn('[mesh] /status failed:', sanitizeForLog((err as Error).message));
res.status(500).json({ error: 'Failed to load mesh status' });
+13 -1
View File
@@ -1,5 +1,6 @@
import { Router, type Request, type Response } from 'express';
import { getActiveCapabilities, getSenchoVersion } from '../services/CapabilityRegistry';
import { MeshService } from '../services/MeshService';
import SelfUpdateService from '../services/SelfUpdateService';
// Captured at boot. Exposed via /api/health and /api/meta so the Fleet update
@@ -9,8 +10,19 @@ const processStartedAt = Date.now();
export const metaRouter = Router();
// Public health endpoint (no auth). Used by Docker HEALTHCHECK and uptime monitors.
// The `mesh.dataPlane` block reports whether `MeshService.setupMeshNetwork`
// completed successfully; an `ok: false` value means cross-node mesh routing
// is disabled on this node and the operator should consult the activity log
// or set `SENCHO_MESH_SUBNET` to a free /24 and restart the container.
metaRouter.get('/health', (_req: Request, res: Response): void => {
res.json({ status: 'ok', uptime: process.uptime(), startedAt: processStartedAt });
res.json({
status: 'ok',
uptime: process.uptime(),
startedAt: processStartedAt,
mesh: {
dataPlane: MeshService.getInstance().getDataPlaneStatus(),
},
});
});
// Public meta endpoint. Returns this instance's version and supported