El escudo perimetral: jg_dashboard como pasarela de interfaces heterogéneas y aislador del runtime
Cómo proteger al agente del ruido de Internet: normalización de estímulos arbitrarios, aislamiento estricto de base de datos y orquestación con Laravel 11 y Filament v3.
"En la arquitectura de sistemas distribuidos y telecomunicaciones, ningún motor de conmutación o procesamiento crítico se expone directamente a la frontera no confiable de la red. Delegar en un runtime cognitivo la gestión de sockets de telefonía, tramas de mensajería, autenticación de usuarios y exposición perimetral HTTP es un error de diseño que compromete tanto la disponibilidad como la seguridad del sistema. El agente debe residir en un plano de ejecución puramente privado, resguardado por un Control Plane perimetral que normalice los estímulos arbitrarios, gobierne el acceso y preserve la integridad de los datos."
1. El Dilema Arquitectónico: Exposición Directa vs. Control Plane Perimetral
Uno de los errores más frecuentes al llevar agentes de IA a producción es la exposición directa del runtime a redes públicas. Bajo esta aproximación simplista, el mismo proceso que orquesta el grafo cognitivo o invoca al modelo de lenguaje se encarga de escuchar peticiones HTTP entrantes, recibir tramas de audio PBX, parsear webhooks de terceros y gestionar la autenticación de usuarios.
+---------------------------------------------------------------------------------+
| Antipatrón: Runtime Cognitivo Expuesto Directamente al Perímetro |
| |
| Internet / Clientes ---> [Puerto 8000 Público: jg_agente] |
| ├── Gestión de TLS y certificados perimetrales |
| ├── Parseo de payloads arbitrarios (Telegram, PBX...) |
| ├── Autenticación de usuarios y RBAC disperso |
| └── Ejecución de LangGraph + LLM + Checkpoints |
| (Vulnerable a DoS por agotamiento de tokens y socket) |
+---------------------------------------------------------------------------------+
+---------------------------------------------------------------------------------+
| Patrón jg_ia: Aislamiento de Runtime mediante Control Plane Perimetral |
| |
| Internet / Clientes ---> [Escudo Perimetral: jg_dashboard (Laravel 11 / PHP)] |
| ├── Terminación TLS, WAF y autenticación de identidad |
| ├── Normalización canónica (InputNormalizer) |
| ├── Auditoría inmutable y gobierno de permisos RBAC |
| └── Despacho firmado criptográficamente (HMAC-SHA256) |
| │ |
| VPC / Red Privada Docker |
| ▼ |
| [Plano Privado Aislado: jg_agente (Python / LangGraph)]|
| ├── Escucha exclusiva en http://jg_agente:8000 |
| ├── Cero IP pública / Cero exposición perimetral |
| └── Ejecución determinista del ciclo cognitivo |
+---------------------------------------------------------------------------------+
Este diseño desacoplado responde al Principio de Proceso Aislado:
- Contención del Ruido de Red: Peticiones malformadas, escaneos automáticos de vulnerabilidades, intentos de inyección y ráfagas de tráfico no autenticado son filtrados en el perímetro por
jg_dashboardantes de consumir ciclos de CPU en el motor cognitivo. - Defensa en Profundidad: El contenedor
jg_agenteno tiene IP pública asignada ni expone puertos al host en despliegues productivos. Escucha únicamente dentro de la red privada interna de Docker o VPC (http://jg_agente:8000). Si un atacante comprometiera un servicio perimetral, la barrera criptográfica inter-servicio impide invocar al agente sin una firma válida. - Especialización de Tecnologías: Laravel 11 y PHP proveen un ecosistema maduro para la gestión de interfaces web, sesiones, pasarelas de eventos, autenticación multi-factor y paneles administrativos (Filament v3), mientras que Python y FastAPI permanecen concentrados en el grafo cognitivo, la inferencia de modelos y la orquestación de herramientas.
2. La Pasarela de Adaptadores (I/O Gateway)
En un ecosistema corporativo real, los estímulos que disparan la acción de un agente provienen de canales completamente heterogéneos:
- Webhooks de Integración Continua: Eventos JSON de GitLab o GitHub desencadenados por commits, fallos de pipeline o aperturas de Merge Requests.
- Telefonía y Voz PBX: Centralitas Asterisk o sistemas IVR que emiten tramas asíncronas de eventos telefónicos y requieren respuestas en formato SSML o texto para síntesis de voz (TTS).
- Mensajería Instantánea: Bots en Telegram o Slack con estructuras anidadas de conversación, botones interactivos y restricciones de formato Markdown/HTML.
Para evitar que el agente deba conocer las especificaciones de cada protocolo externo, jg_dashboard implementa el subsistema de adaptadores perimetrales (app/Adapters/).
El Rol de InputNormalizer
La clase InputNormalizer (app/Adapters/Normalizers/InputNormalizer.php) implementa InputNormalizerInterface. Recibe un estímulo arbitrario (Request o array crudo) y ejecuta un pipeline determinista de seis etapas:
Payload Arbitrario (JSON GitLab / Telegram / PBX)
│
▼
┌───────────────────────────┐
│ 1. detectSourceType() │ (Identifica: gitlab, github, telegram, pbx, chatbot)
└─────────────┬─────────────┘
▼
┌───────────────────────────┐
│ 2. extractPrompt() │ (Sintetiza la intención textual limpia)
└─────────────┬─────────────┘
▼
┌───────────────────────────┐
│ 3. Identificadores Únicos │ (Asigna execution_id, thread_id y correlation_id)
└─────────────┬─────────────┘
▼
┌───────────────────────────┐
│ 4. extractParameters() │ (Extrae variables de ejecución y flags async)
└─────────────┬─────────────┘
▼
┌───────────────────────────┐
│ 5. buildUserContext() │ (Inyecta identidad, roles RBAC y origen)
└─────────────┬─────────────┘
▼
┌───────────────────────────┐
│ 6. buildMetadata() │ (Registra timestamps, IP cliente y provenance)
└─────────────┬─────────────┘
│
▼
Contrato Canónico: AgentInput
El resultado de esta transformación es un contrato tipificado AgentInput:
{
"execution_id": "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d",
"thread_id": "c1f2e3d4-0000-0000-0000-000000000001",
"correlation_id": "corr-550e8400-e29b-41d4-a716-446655440000",
"prompt": "Reiniciar servicio de producción tras fallo en pipeline #4021",
"user_prompt": "Reiniciar servicio de producción tras fallo en pipeline #4021",
"parameters": {
"async": false,
"timeout": 30
},
"user_context": {
"user_id": "usr_sre_lead",
"roles": ["admin", "sre"],
"channel": "gitlab_webhook"
},
"metadata": {
"source": "gitlab",
"ip": "192.168.10.45",
"user_agent": "GitLab/16.8"
}
}
El Rol de ClientOutputFormatter
Cuando el agente concluye su trabajo, emite un contrato tipificado AgentOutput. Sin embargo, el cliente emisor espera un formato adaptado a su canal: Telegram espera un bloque de texto formateado; una centralita PBX requiere un campo semántico interpretable; un pipeline CI/CD espera un código de estado de salida con metadatos de telemetría.
ClientOutputFormatter (app/Adapters/Formatters/ClientOutputFormatter.php) transforma el contrato canónico saliente en el sabor requerido (generic, chatbot, ci_cd):
// app/Adapters/Formatters/ClientOutputFormatter.php
public function format(array $agentResponse, string $format = 'generic', array $metadata = []): array
{
$success = (bool) ($agentResponse['success'] ?? false);
$data = $agentResponse['data'] ?? [];
$outputText = $this->extractOutputText($data, $agentResponse);
return match (strtolower($format)) {
'chatbot', 'chat', 'messaging' => $this->formatForChatbot($success, $status, $outputText, $executionId, $threadId, $data, $metadata),
'ci_cd', 'cicd', 'pipeline', 'webhook' => $this->formatForCiCd($success, $status, $outputText, $executionId, $threadId, $data, $metadata),
default => $this->formatGeneric($success, $status, $outputText, $executionId, $threadId, $data, $metadata),
};
}
3. Aislamiento Estricto de Datos (Storage Segregation)
Un pilar irrenunciable del diseño es la segregación física y lógica del almacenamiento. El Control Plane (jg_dashboard) y el Execution Plane (jg_agente) operan sobre motores de base de datos PostgreSQL independientes ejecutándose en contenedores y puertos separados:
| Parámetro | Base de Datos del Control Plane | Base de Datos del Execution Plane |
|---|---|---|
| Contenedor | jg_dashboard_postgres |
jg_agente_postgres |
| Puerto Host | 5434 (interno 5432) |
5433 (interno 5432) |
| Tecnología | PostgreSQL 16 Alpine | PostgreSQL 16 Alpine |
| ORM / Acceso | Laravel Eloquent / PDO PHP | LangGraph Saver / Asyncpg / SQLAlchemy |
| Responsabilidad | Identidad, auditoría, gobernanza y HITL | Checkpoints, estados de nodos y grafos cognitivos |
| Acceso Cruzado | PROHIBIDO: Dashboard jamás lee puerto 5433 | PROHIBIDO: Agente jamás lee puerto 5434 |
+-----------------------------------+ +-----------------------------------+
| CONTROL PLANE (Laravel) | | EXECUTION PLANE (Python) |
| | | |
| dashboard_users | | checkpoints |
| dashboard_agents | | checkpoint_blobs |
| dashboard_audit_events | | checkpoint_writes |
| dashboard_approval_requests | | core_thread_states |
| | | |
| PostgreSQL Dedicado (:5434) | | PostgreSQL Dedicado (:5433) |
+-----------------------------------+ +-----------------------------------+
▲ ▲
│ │
└────────────── [PROHIBIDO] ──────────────────┘
(Sin conexiones directas entre bases)
Toda interacción es vía API REST firmada
Qué Reside en la Base de Datos del Dashboard
- Credenciales y Control de Acceso: Usuarios, contraseñas hash con algoritmos robustos (
bcrypt/argon2id) y sesiones web. - Catálogo de Agentes Registrados (
dashboard_agents): Registro de agentes activos en la plataforma. Los secretos criptográficos compartidos nunca se almacenan en texto claro: se persisten cifrados simétricamente medianteCrypt::encryptString()(AES-256-CBC gobernado por laAPP_KEYde Laravel) y se verifica su integridad conpermanent_secret_hash. - Pistas de Auditoría Inmutables (
dashboard_audit_events): Registro histórico append-only de cada evento externo, estímulo procesado y decisión de enrutamiento. Para asegurar la inmutabilidad física en la capa de modelo Eloquent, la entidad define de forma explícita:
Cualquier intento de modificar un registro existente es rechazado a nivel de arquitectura.const UPDATED_AT = null; - Solicitudes de Gobernanza HITL (
dashboard_approval_requests): Peticiones de autorización humana pendientes de resolución por operadores, gestionadas con bloqueos pesimistas (lockForUpdate).
4. Ciclo de Vida de una Petición a Través de la Pasarela
El flujo completo desde que un estímulo externo impacta en el perímetro hasta que la respuesta es entregada se detalla a continuación:
Cliente Externo (GitLab / Telegram / PBX)
│
│ 1. POST /api/v1/webhook (HTTP no confiable)
▼
+-------------------------------------------------------------------------------+
| jg_dashboard (Control Plane Gateway) |
| |
| 2. verifyAuthentication(): Valida token o firma de origen |
| 3. InputNormalizer::normalize(): Convierte estímulo a contrato AgentInput |
| 4. AuditEvent::record(): Persiste recepción inmutable en BD (:5434) |
| 5. Agent::find(): Resuelve agente destino y descifra secreto simétrico |
| 6. AgentHttpClient::triggerExecution(): |
| Construye firma canónica HMAC-SHA256 con X-Nonce y X-Timestamp |
+-------------------------------------------------------------------------------+
│
│ 7. POST http://jg_agente:8000/api/v1/execute (Red Privada Docker)
│ Headers: X-Agent-ID, X-Signature, X-Timestamp, X-Nonce
▼
+-------------------------------------------------------------------------------+
| jg_agente (Execution Plane) |
| |
| 8. VerifyHmacSignature: Verifica firma criptográfica y anti-replay en caché |
| 9. LangGraph StateGraph: Ejecuta ciclo de estados (Observe, Reason, Exec...) |
| 10. PostgreSQL (:5433): Persiste checkpoints de grafo atómicamente |
| 11. Emite contrato canónico: AgentOutput |
+-------------------------------------------------------------------------------+
│
│ 12. HTTP 200 OK con payload canónico AgentOutput
▼
+-------------------------------------------------------------------------------+
| jg_dashboard |
| |
| 13. ClientOutputFormatter::format(): Transforma según canal cliente |
| 14. AuditEvent::record(): Registra finalización y consumo |
+-------------------------------------------------------------------------------+
│
│ 15. Respuesta formateada (JSON / SSML / Telegram Markdown)
▼
Cliente Externo
5. Observabilidad de Plataforma y Topología Dinámica
El Control Plane no actúa únicamente como pasarela pasiva de E/S; monitoriza activamente la salud y disponibilidad de los nodos del Execution Plane mediante tres mecanismos:
1. Latidos de Salud Periódicos (Heartbeats)
Los agentes en ejecución emiten latidos periódicos cada 30 segundos invocando:
POST /api/v1/agents/heartbeat
Headers:
X-Agent-ID: a0000000-0000-0000-0000-000000000002
X-Signature: [Firma HMAC-SHA256 del payload]
X-Timestamp: 1729000000
X-Nonce: 4f8b2c1a-9e3d...
El controlador AgentHeartbeatController valida criptográficamente la firma con el secreto registrado en dashboard_agents, actualiza la marca temporal last_heartbeat_at y registra las métricas de carga reportadas por el agente.
De forma complementaria, el comando programado CheckAgentHealthCommand (php artisan agents:check-health) evalúa periódicamente el catálogo: si un agente activo no reporta heartbeat en más de 90 segundos, transiciona automáticamente su estado a OFFLINE, notificando la anomalía y evitando que la pasarela le derive nuevo tráfico.
2. Mapa Topológico en Tiempo Real (TopologyMap en Filament v3)
El panel administrativo incorpora un visualizador topológico interactivo (TopologyMap) montado sobre Filament v3 y Livewire. Este componente representa gráficamente la infraestructura:
- Conexiones activas entre el Control Plane y los agentes registrados.
- Estado operativo de cada nodo (
ACTIVE,PENDING,OFFLINE). - Métricas de latencia de red en milisegundos y tasa de ejecuciones concurrentes.
- Detección visual inmediata ante desconexiones de satélites o degradación de servicios.
3. Circuit Breakers Perimetrales (CircuitBreakerService)
Para evitar la saturación de workers y la acumulación de conexiones en espera cuando un agente satélite o una API upstream sufre una caída, jg_dashboard interpone interruptores de circuito gobernados por el modelo CircuitBreakerState:
- Umbral de Fallos (
failureThreshold): 3 fallos consecutivos disparan la apertura del circuito. - Ventana de Enfriamiento (
cooldownSeconds): 30 segundos. - Estados del Circuito:
CLOSED: Operación normal; el tráfico fluye hacia el agente.OPEN: Tráfico cortado de inmediato en el perímetro. La petición es rechazada localmente lanzandoCircuitBreakerOpenExceptiono derivándose a un fallback sin saturar la red interna.HALF_OPEN: Tras expirar la ventana de enfriamiento, se permite una petición de prueba para verificar si el agente upstream ha recuperado la estabilidad operativa.
6. Consideraciones de Producción, Trade-offs y Límites
-
Sobrecarga de Latencia del Proxy Perimetral: Interponer un intermediario introduce una penalización temporal medible frente a la invocación directa. En pruebas de laboratorio, el coste de parseo en
InputNormalizer, resolución en base de datos, cálculo de firma HMAC-SHA256 y reformateo enClientOutputFormatterañade un overhead promedio inferior a 5 milisegundos. Para flujos agénticos donde la inferencia de un modelo de lenguaje oscila entre 800 ms y 4.000 ms, esta penalización representa menos del 0,5% del tiempo total y compensa ampliamente las garantías de seguridad y aislamiento obtenidas. -
Desfase de Timeouts HTTP (Síncrono vs. Asíncrono): Un proxy inverso perimetral o un servidor PHP-FPM suele tener configurado un tiempo límite de ejecución duro (habitualmente 30 o 60 segundos). Si un agente ejecuta un grafo cognitivo complejo con múltiples pasos de herramientas y verificación que excede ese umbral, la conexión perimetral colapsará con un error
504 Gateway Timeoutaunque el agente continúe ejecutándose internamente.- Solución Arquitectónica: Para operaciones que superen los 20 segundos, la pasarela admite ejecución asíncrona mediante el parámetro
parameters.async = truey una cabeceracallback_url. El Dashboard registra la tarea en estadoPENDING, devuelve un código202 Acceptedinmediato con elexecution_id, y el agente notifica el resultado final de forma asíncrona al Control Plane al concluir su ciclo.
- Solución Arquitectónica: Para operaciones que superen los 20 segundos, la pasarela admite ejecución asíncrona mediante el parámetro
-
Gobernanza de Conexiones a Base de Datos: Dado que
jg_dashboardexperimenta picos de peticiones externas concurrentes, es indispensable configurar un pool de conexiones adecuado en PostgreSQL (pgbouncero límites estrictos enconfig/database.php) para evitar agotar las conexiones disponibles del host en el puerto 5434.
7. Reflexión de Arquitectura: La Autonomía de la Periferia
Uno de los mayores errores en el diseño de plataformas de agentes es transferir la complejidad de los canales de comunicación y las interfaces de usuario hacia el núcleo de razonamiento. La tentación de añadir un parche en el runtime para parsear el JSON de un webhook concreto o gestionar el socket de una centralita telefónica rompe inmediatamente el principio de responsabilidad única y acopla el ciclo cognitivo a la volatilidad del mundo exterior.
La verdadera madurez de un sistema distribuido radica en otorgar plena autonomía y soberanía a la periferia: un Control Plane perimetral (jg_dashboard) que asume el desgaste de Internet, normaliza los estímulos arbitrarios mediante contratos estrictos y aísla los datos sensibles. Al confinar el ruido de la red en la frontera y mantener el motor de ejecución en una red privada hermética, el agente puede dedicarse exclusivamente a lo que mejor sabe hacer: razonar y operar de forma determinista sobre representaciones canónicas.
8. Enlaces y Referencias en el Repositorio
- Pasarela de Adaptadores e Interfaces Normalizadoras:
jg_dashboard/app/Adapters/ - Cliente HTTP Perimetral y Despacho Criptográfico:
jg_dashboard/app/Services/Agents/AgentHttpClient.php - Modelo de Agentes Registrados y Gestión de Secretos:
jg_dashboard/app/Models/Agent.php - Servicio de Interrupción de Circuito (Circuit Breakers):
jg_dashboard/app/Services/Resilience/CircuitBreakerService.php - Controlador de Recepción de Heartbeats:
jg_dashboard/app/Http/Controllers/Api/AgentHeartbeatController.php
| Título | El escudo perimetral: jg_dashboard como pasarela de interfaces heterogéneas y aislador del runtime |
|---|---|
| Archivo | docs/02_jg_agent/articulos/04_dashboard_control_plane_gateway.md |
| Descripción | Cómo proteger al agente del ruido de Internet: normalización de estímulos arbitrarios, aislamiento estricto de base de datos y orquestación con Laravel 11 y Filament v3. |
| Autor | Javier Gálvez |
| Fecha | 2026-10-17 |
| Etiquetas |
#Artículos
#Arquitectura
#Control Plane
#Dashboard
#Perímetro
#Normalización
#Aislamiento
#Filament
|