Docs / Plano de control

Plano de Control :: Arquitectura del Gateway de Gobernanza (jg_dashboard)

"Un agente autónomo en producción es tan seguro como el plano que controla sus estímulos y autoriza sus mutaciones. Si la pasarela de control no aísla la persistencia, no normaliza la entrada ni gestiona la concurrencia humana con exclusión mutua pesimista, el sistema colapsará ante la primera condición de carrera en el centro de operaciones."

Código fuente e histórico de auditoría disponible en el repositorio oficial:
🔗 GitLab: gitlab.com/javiergalvez-ia/jg_dashboard


1. Visión y Principios del Plano de Control

El Plano de Control (jg_dashboard) es la pasarela de observabilidad, autorización y blindaje perimetral del ecosistema. Construido sobre Laravel 12, Filament v3 y Livewire 3, actúa como el único punto de contacto expuesto hacia el exterior (operadores humanos, pasarelas de telefonía, bots y llamadas de sistemas heterogéneos M2M).

Su misión fundamental es desacoplar de forma estricta los entornos de presentación y gestión del motor de razonamiento (jg_agente), garantizando que ninguna petición externa interactúe directamente con el runtime sin antes haber sido autenticada, normalizada, autorizada y registrada inmutablemente.

Principios Rectores de Diseño:

  1. Escudo Perimetral y Aislamiento de Red: Es el único componente accesible desde redes públicas o corporativas. Los agentes cognitivos residen de forma estricta en redes privadas aisladas, recibiendo tráfico exclusivamente a través de este plano mediante firmas criptográficas.
  2. Segregación Estricta de Persistencia: Opera sobre su propia base de datos relacional PostgreSQL independiente (dashboard_postgres, puerto 5434). Bajo ningún concepto el Control Plane accede ni ejecuta consultas contra la base de datos interna de los agentes (core_checkpoints, core_messages). El estado se sincroniza exclusivamente mediante APIs tipadas.
  3. Normalización Perimetral de Entrada/Salida (App\Adapters): Los clientes externos pueden enviar JSON arbitrarios, payloads de telefonía (Asterisk PBX) o webhooks heterogéneos. La capa de adaptadores normaliza cualquier estímulo al contrato canónico AgentInput antes del despacho, y formatea la respuesta final al dialecto específico del cliente.
  4. Gobernanza Human-in-the-Loop (HITL) sin Condiciones de Carrera: Las aprobaciones críticas de operadores se resuelven mediante bloqueo pesimista a nivel de fila (lockForUpdate()), neutralizando el riesgo de que dos personas en el centro de control autoricen o rechacen una misma acción simultáneamente.
  5. Resiliencia SRE y Aislamiento de Fallos en Cascada: Incorpora interruptores de circuito (Circuit Breakers) por servicio destino, mapas dinámicos de topología de infraestructura y deduplicación inteligente de incidentes operacionales.

2. Arquitectura de Separación de Planos y Gateway de Interfaces

El siguiente diagrama ilustra la segregación de responsabilidades entre el mundo exterior, el Control Plane y el Execution Plane:

   USUARIOS, SISTEMAS HETEROGÉNEOS (CI/CD, Asterisk, Bots) & OPERADORES
                                │
                                │ HTTPS / REST / Webhooks
                                ▼
┌────────────────────────────────────────────────────────────────────────┐
│                      jg_dashboard (Control Plane)                      │
│                                                                        │
│  ┌───────────────────────┐         ┌────────────────────────────────┐  │
│  │    Web UI & Console   │         │ REST API & Adapters Subsystem  │  │
│  │   (Filament / Blade)  │         │   (/api/v1/adapters/*)         │  │
│  └───────────┬───────────┘         └───────────────┬────────────────┘  │
│              │                                     │                   │
│              ▼                                     ▼                   │
│  ┌──────────────────────────────────────────────────────────────────┐  │
│  │                     Servicios del Dashboard                      │  │
│  │  - Auth & RBAC (Admin / Operator / Viewer) + JwtService          │  │
│  │  - Capa App\Adapters: InputNormalizer + ClientOutputFormatter   │  │
│  │  - Adaptadores: HttpWebhookAdapter & MockAsteriskChatAdapter     │  │
│  │  - Agent Registration Service (Bootstrap Token 24h + Dual Secret)│  │
│  │  - HitlApprovalService (lockForUpdate + Reconciliación 404/409)  │  │
│  │  - AgentHealthMonitorService (Schedule everyMinute)              │  │
│  │  - AgentHttpClient (Firmas canónicas HMAC-SHA256 y Retries)      │  │
│  │  - Observabilidad: CircuitBreakerState, InfraTopology, Incidentes│  │
│  └──────────────────────────────────┬───────────────────────────────┘  │
│                                     │                                  │
│                                     ▼                                  │
│                    ┌─────────────────────────────────┐                 │
│                    │     PostgreSQL del Dashboard    │                 │
│                    │  (users, agents, tasks, audit,  │                 │
│                    │   circuit_breaker, topology,    │                 │
│                    │   notified_incidents)           │                 │
│                    └─────────────────────────────────┘                 │
└──────────────────────────────────────┬─────────────────────────────────┘
                                       │ HTTP / HMAC-SHA256 (Red Privada)
                                       ▼
┌────────────────────────────────────────────────────────────────────────┐
│                      jg_agente (Execution Plane)                       │
│                         (FastAPI / LangGraph)                          │
└────────────────────────────────────────────────────────────────────────┘

3. Gobernanza HITL sin Condiciones de Carrera (lockForUpdate)

Cuando un agente cognitivo alcanza un paso de alto riesgo (RiskLevel >= HIGH), suspende su ejecución emitiendo un estado WAITING_APPROVAL que genera una solicitud en el Control Plane (ApprovalRequest).

En salas de control u operaciones donde múltiples ingenieros revisan simultáneamente la bandeja de aprobaciones, existe el riesgo crítico de doble resolución concurrente: dos operadores pulsando "Aprobar" al mismo milisegundo sobre la misma tarea.

Para erradicar este riesgo, HitlApprovalService encapsula la resolución en una máquina de estados con bloqueo transaccional pesimista:

               ┌───────────────────────┐
               │        PENDING        │◄─────────────────────────┐
               └───────────┬───────────┘                          │
                           │                                      │
            (lockForUpdate: DB::transaction)                      │
                           │                                      │
                           ▼                                      │
               ┌───────────────────────┐                          │
               │      PROCESSING       │                          │
               └───────────┬───────────┘                          │
                           │                                      │
               ┌───────────┴───────────┐                          │
               │                       │                          │
         (HTTP 200 Agente)      (Error HTTP / Timeout) ───────────┘
               │                (Reversión atómica a PENDING)
    ┌──────────┴──────────┐
    │                     │
(Aprobación)          (Rechazo)
    ▼                     ▼
┌─────────────────┐ ┌─────────────────┐
│    APPROVED     │ │    REJECTED     │
└─────────────────┘ └─────────────────┘

Protocolo de Reserva Atómica en Dos Fases:

  1. Fase 1: Adquisición Pesimista del Cerrojo: Dentro de una transacción DB::transaction(), el servicio ejecuta ApprovalRequest::where('id', $id)->lockForUpdate()->first(). Si el registro no se encuentra en estado PENDING (ya fue tomado por otro operador o resuelto), se aborta inmediatamente con código HTTP 409 Conflict, impidiendo la duplicidad.
  2. Fase 2: Transición a PROCESSING y Liberación de Cerrojo: El registro transiciona a PROCESSING y la transacción de base de datos se confirma (commit) antes de despachar la petición de red HTTP al agente. Esta decisión de diseño es crucial: jamás se debe mantener una conexión a base de datos bloqueada mientras se espera una respuesta de red externa.
  3. Resolución o Reversión Tolerante a Fallos: Si la llamada al agente (POST /executions/{id}/approve con firma HMAC) retorna HTTP 200, el registro se consolida como APPROVED. Si se produce un timeout o error de red, el registro revierte atómicamente a PENDING, permitiendo que el operador reintente sin dejar la tarea bloqueada de por vida.
// app/Services/Hitl/HitlApprovalService.php (Implementación canónica)
$lockResult = DB::transaction(function () use ($approvalId) {
    $record = ApprovalRequest::where('id', $approvalId)
        ->lockForUpdate()
        ->first();

    if (!$record || !$record->isPending()) {
        return ['error' => 'Solicitud ya tomada o inexistente', 'code' => 409];
    }

    $record->update(['status' => ApprovalStatus::PROCESSING]);
    return ['record' => $record, 'code' => 200];
});

4. Reconciliador Automático Reaper y Autosanación SRE

Los sistemas distribuidos reales sufren caídas de red, reinicios de procesos y abandonos de sesión. Si un operador cierra el navegador mientras una solicitud está en estado transitorio PROCESSING, la solicitud podría quedar congelada indefinidamente.

Para resolverlo sin intervención humana, jg_dashboard incluye un Reconciliador Reaper periódico programado en el planificador de Laravel (app/Console/Commands/ReconcileHitlApprovalsCommand.php):

  • Se ejecuta cada minuto mediante cron.
  • Detecta registros en PROCESSING que han superado el TTL de seguridad (por defecto 120 segundos).
  • Comprueba el estado real del hilo en jg_agente mediante consulta HTTP firmada.
  • Si el hilo sigue suspendido en WAITING_APPROVAL, revierte la solicitud en base de datos a PENDING y emite una alerta en los logs de auditoría, restableciendo la capacidad de supervisión.

5. Protocolo Criptográfico Inter-Servicio: HMAC-SHA256 y Anti-Replay

La comunicación entre el Control Plane y los agentes se autentica de forma simétrica mediante firmas digitales HMAC-SHA256. A diferencia de modelos frágiles basados en tokens estáticos en cabeceras (Bearer tokens expuestos), este protocolo blinda la integridad de los datos en tránsito:

Payload JSON + Método HTTP + Path + Timestamp + X-Nonce
                         │
                         ▼
             [ Hash HMAC-SHA256(Secreto) ]
                         │
                         ▼
            Cabecera: X-Signature-SHA256

Mitigación Determinista de Ataques de Repetición (Anti-Replay):

  1. Marca de Tiempo con Ventana de Deriva Máxima (Skew): La cabecera X-Timestamp debe encontrarse dentro de una ventana de tolerancia estricta de ±300 segundos respecto a la hora UTC del servidor.
  2. Caché Atómica de Nonces (X-Nonce): Cada petición genera un identificador criptográfico único UUIDv4 (X-Nonce). El receptor almacena el nonce en su caché distribuida (Redis / PostgreSQL) con el método atómico Cache::add($nonce, true, $ttl). Si el nonce ya existía, la petición es rechazada de inmediato con HTTP 401 Unauthorized / Replay Detected, impidiendo que un atacante que intercepte el tráfico pueda reenviar una orden de mutación o aprobación legítima.

6. Interruptores de Circuito (Circuit Breakers) y Mapa de Topología

Para evitar el temido efecto de "cascada de fallos" en infraestructuras distribuidas, el Dashboard gestiona el estado de salud de cada agente registrado a través del patrón Circuit Breaker (app/Services/Resilience/CircuitBreakerService.php):

  • Estado CLOSED (Operación Normal): Las peticiones fluyen con normalidad hacia el agente satélite.
  • Estado OPEN (Aislamiento Activo): Si el agente acumula fallos consecutivos por encima del umbral configurado (ej. 5 fallos en 60 segundos), el circuito se abre. Todas las peticiones entrantes destinadas a ese agente son rechazadas de inmediato sin sobrecargar la red ni degradar la experiencia de usuario.
  • Estado HALF_OPEN (Sondeo de Recuperación): Tras expirar el tiempo de enfriamiento (cooldown de 30 segundos), el sistema permite el paso de una única petición de prueba (canary request). Si tiene éxito, el circuito vuelve a cerrarse; si falla, el periodo de aislamiento se renueva con penalización.

Toda esta topología se visualiza en tiempo real en el panel administrativo Filament (TopologyMap), permitiendo al operador auditar la salud de cada enlace de la malla de un vistazo.


7. Doble Capa de Interfaz: Filament v3 vs Consola Interactiva

El Plano de Control proporciona dos experiencias de usuario complementarias según el perfil operativo:

  1. Panel Administrativo Filament v3 (/admin): Diseñado para ingeniería de operaciones, auditoría de cumplimiento y gestión de recursos del sistema (usuarios RBAC, agentes registrados, incidentes deduplicados, cuotas de tokens e historial forense de ejecuciones).
  2. Consola Web Interactiva (/console — WebConsoleController): Diseñada para monitorización ágil en tiempo real, disparo directo de prompts cognitivos, inspección en vivo de árboles de razonamiento LangGraph y aprobación ágil de compuertas HITL con feedback visual inmediato.

8. Referencia de Repositorio y Especificación

El código completo del Plano de Control, sus migraciones de base de datos, políticas de autorización RBAC y suite de pruebas (Pest / PHPUnit) se encuentra disponible para auditoría y evaluación técnica en el repositorio oficial:

Metadatos del documento
Título Plano de Control :: Arquitectura del Gateway de Gobernanza jg_dashboard
Archivo docs/02_jg_agent/control_plane.md
Descripción Arquitectura interna, escudo perimetral, gobernanza HITL con concurrencia pesimista lockForUpdate, adaptadores de E/S, resiliencia SRE y protocolo criptográfico en jg_dashboard.
Autor Javier Gálvez
Fecha 2026-10-17
Etiquetas
#Control Plane #jg_dashboard #Laravel 12 #Filament v3 #HITL #Pessimistic Lock #lockForUpdate #HMAC-SHA256 #Circuit Breakers #Observabilidad