Saltar al contenido principal

Panel de Control — Monitorización de Infraestructura

Destacado

Dashboard centralizado con health endpoints, métricas de sistema, API REST + WebSocket y estado de agentes en tiempo real para la infraestructura del equipo.

Node.js TypeScript Docker REST API WebSocket Pino

El problema

Tres servidores (dos físicos en casa, un VPS en Oracle Cloud) y tres agentes IA significa que cuando algo falla, no sabes qué ha sido hasta que entras a mirar. Con servicios repartidos entre nodos y cada máquina con sus propios logs, diagnosticar un problema requiere hacer SSH a varias máquinas y revisar cada servicio por separado.

Necesitaba un panel que respondiera instantáneamente a: ¿Los servicios están UP o DOWN? ¿Qué CPU y RAM consume cada cosa? ¿Qué está haciendo cada agente ahora?

La decisión — dashboard unificado con health checks automatizados

En lugar de soluciones de monitorización pesadas (Nagios, Zabbix), opté por un dashboard ligero en Node.js que consulta periódicamente los health endpoints de cada servicio y muestra el estado en un solo lugar.

La clave: el panel no solo muestra datos de infraestructura — también consume los heartbeats de los agentes IA, integrando monitorización de sistema y visibilidad del equipo en un mismo sitio.

Arquitectura — fusión de dos sistemas

El proyecto nació de la fusión de dos sistemas que antes funcionaban por separado: el Agente Conectora (un broker FastAPI con oficina pixel-art) y el Panel de Control original (Miniverse, un mundo pixel con heartbeats REST). En lugar de mantener ambos, los unifiqué en un solo backend Node.js en el puerto 4321, con un frontend React SPA en el 3001.

ServicioPuertoRol
OpenClaw Gateway18789Gestión de agentes — no se toca
Hermes Broker18792Bridge interno: conecta OpenClaw con Miniverse
Miniverse Server4321API REST + WebSocket unificada del panel
Panel Frontend3001React SPA con tres rutas

El frontend tiene tres rutas: / (mundo pixel-art con sprites de agentes), /oficina (oficina pixel-art migrada del antiguo sistema HTML), y /dashboard (métricas y salud del sistema).

Implementación

Backend — Node.js raw HTTP

El backend corre en Node.js + TypeScript sin Express (deliberadamente raw HTTP, para mantener la huella mínima). Pino como logger estructurado. WebSocket con la librería ws para tiempo real.

Servicios monitorizados

ServicioPuertoFunción
Miniverse Server4321Backend API del panel con health endpoint
OpenClaw3000Sistema de control de agentes
Hermes Broker18789Broker de mensajería entre agentes

API completa (puerto 4321)

MétodoRutaFunción
GET/api/service-healthHealth checks de todos los servicios con latencia
GET/api/metricsCPU load, RAM usada, uptime, conexiones activas
GET/api/agentsLista de agentes con estado y heartbeat
GET/api/agents/:idDetalle de un agente
GET/api/infoInformación general del servidor, versión, subsistemas
POST/api/heartbeatRecepción de heartbeat de agentes
POST/api/agents/removeEliminar un agente del registro
GET/api/observeSnapshot del mundo y eventos recientes
POST/api/observeRecibe eventos de Hermes y los retransmite por WebSocket
POST/api/actEnviar acción de un agente (move, speak, emote, status…)
GET/api/inboxMensajes pendientes de un agente
GET/api/channelsCanales y sus miembros
GET/api/eventsEventos recientes
POST/api/generateGenerar assets gráficos con IA (FAL)
POST/api/webhookRegistrar callback para mensajes de agente
DELETE/api/webhookEliminar webhook
POST/api/save-worldGuardar estado del mundo
POST/api/hooks/claude-codeHook para eventos de Claude Code

Sistema de heartbeats

Cada agente escribe su estado en un archivo JSON en el NAS, y Miniverse los lee por polling cada 30 segundos. El formato unificado incluye:

{
  "agent": "codex",
  "state": "working",
  "task": "Refactorizando módulo de sync",
  "position": "desk_2_0",
  "timestamp": 1751326200,
  "energy": 1,
  "sensors": {
    "cpu_load": 0.45,
    "session_age_min": 3,
    "nas_messages_pending": 1
  }
}

Estados soportados: working, idle, thinking, error, waiting, collaborating, sleeping, listening, speaking, resting, descanso_corto, descanso_largo, offline.

Health checks

El endpoint GET /api/service-health devuelve estado del proceso y de cada subsistema:

{
  "status": "ok",
  "processUptime": 12345,
  "memory": { "rss": 52428800, "heapUsed": 25165824 },
  "activeConnections": 3,
  "totalRequests": 847,
  "subsystems": {
    "Miniverse Server": { "up": true, "latencyMs": 0 },
    "OpenClaw": { "up": true, "latencyMs": 0 },
    "Hermes Broker": { "up": true, "latencyMs": 0 }
  }
}

Funcionalidades

  • Health checks automáticos: cada servicio tiene un endpoint /health que el panel consulta periódicamente y muestra UP/DOWN con tiempo de respuesta
  • Métricas en tiempo real: CPU load, memoria RAM, uptime del sistema, conexiones activas
  • Estado de agentes: Codex, Palomino y Lia con heartbeat cada 30 segundos y estado visual
  • Rate limiting: sliding window de 100 req/min para IPs externas, ilimitado para localhost y red local (192.168.50.63)
  • Logging centralizado: Pino como logger con salida estructurada a stdout
  • CORS hardening: origen específico para el frontend (192.168.50.63:3001), wildcard para el resto. Methods: GET, POST, OPTIONS, DELETE. Headers: Content-Type, Authorization
  • WebSocket messaging: canales (general, dev), mensajes directos entre agentes, broadcasting de eventos
  • Manejo de errores centralizado: try/catch global, respuestas 400/404/429/500 con formato consistente

Testing

12 tests con el framework node:test (sin dependencias externas) que cubren rate limiting, forma de respuestas de todos los endpoints, CORS, y errores.

Métricas

MétricaValor
Servicios monitorizados3
Agentes con heartbeat3 (Codex, Palomino, Lia)
Frecuencia de heartbeatCada 30 segundos
Rate limit externo100 req/min por IP
Endpoints REST18
Tests12/12 pasando
FrontendPuerto 3001

Qué cambiaría

Coordinación de URLs desde el principio. Los health checks cruzados entre servicios requieren que cada URL esté bien mapeada. Un error de mapeo muestra todos los servicios como caídos aunque respondan correctamente. Perdí tiempo depurando falsos positivos.

Heartbeat robusto desde el día uno. Si un agente deja de reportar, el dashboard debe mostrar el estado como offline sin bloquear el resto del sistema. La primera versión trataba la ausencia de heartbeat como error general.

El rate limiting no es solo para producción. Incluso en red local, un bucle accidental de polling puede saturar el panel. El rate limiting por IP debería haber estado desde la primera versión, no después del primer incidente.

La fusión de los dos frontends (oficina HTML + panel React) debería haber sido planeada desde el inicio. Mantener dos sistemas separados retrasó la unificación y generó duplicidad de código.