CreaRack-SL

Auditoría Suprema Terminal — Fase A Backend: Correctness/Honestidad

Resumen

Tanda de correcciones internas del backend del Agente Terminal (sesión s123, 2026-06-10). Fixes de honestidad y robustez en cuatro áreas clave:

  1. Endpoint /api/agent/alert reparado — dejaba de devolver HTTP 500 perpetuo.
  2. Promoción de Primary atómica — evita inconsistencias en la flota.
  3. Endurecimiento de autenticación — refresh tokens rechazados para peticiones de datos.
  4. Retiro de código muerto — módulo terminal/api/scripts.py eliminado.

Detalles por área

1. Alerta de Agente (receive_agent_alert)

Archivo: terminal/api/sentinel.py::receive_agent_alert, terminal/consumers.py::_handle_alert

Problema: Ambos endpoints intentaban crear un MonitoringAlert (modelo de configuración de reglas) con campos que no existen en el esquema:

  • alert_type — no es campo del modelo
  • message — no es campo del modelo
  • severity — tentativa con valor fuera de choices

Resultado: TypeError no capturado → HTTP 500 siempre, alerta perdida silenciosamente (Reglas 13/15 de honestidad).

Comprobación: El Agente actual nunca invoca este canal (send_alert existe en el código pero no se llama). Las alertas reales se derivan en el SaaS de las métricas que el Agente empuja (ver monitoring.api.evaluate_and_trigger_alerts).

Fix: Ambos receptores ahora registran un log de advertencia y responden honestamente:

  • HTTP REST: 200 {"acknowledged": true}
  • WebSocket: log warning, sin intento de crear modelo

Nota: Si en el futuro el Agente emite alertas propias, se enrutarán por el motor real de monitorización, no por este extremo.

2. Promoción de Primary (promote_agent)

Archivo: terminal/api/fleet.py::promote_agent

Problema: La secuencia demote (Primary anterior) + promote (nuevo) se ejecutaba sin transacción:

# ANTES (sin transacción):
current_primary.role = "secondary"
current_primary.save()

agent.role = "primary"
agent.save()

Si dos requests de promoción llegaban concurrentemente, la ventana entre reads y writes podía dejar:

  • 0 Primary (ambos demotaban y ninguno se elevaba)
  • 2 Primary (ambos se promovían sin ver el cambio del otro)

Fix: Serialización con transaction.atomic() + select_for_update():

with transaction.atomic():
    agent = AgentInstance.objects.select_for_update().filter(...).first()
    current_primary = AgentInstance.objects.select_for_update().filter(...).first()
    # demote + promote bajo el lock
    ...
# Notificaciones WS fuera del lock (no retener la transacción durante I/O de red)
_send_role_change_bg(current_primary.agent_id, "secondary")
_send_role_change_bg(agent.agent_id, "primary")

Ahora la reasignación es atómica dentro de la organización.

3. Endurecimiento de Autenticación

Archivo: terminal/api/auth.py::get_agent_from_request

Problema: Un refresh token (larga vida, 180 días) podía autenticar peticiones de datos del Agent.

# ANTES:
payload = verify_agent_token(token)
return payload  # sin revisar tipo

Un refresh token tiene type="refresh" en su payload. Un agente malintencionado con acceso a un refresh token podía usarlo para solicitar datos hasta 180 días después (vuln SA2 BAJA).

Fix: Ahora verifica explícitamente type="access":

payload = verify_agent_token(token)
if not payload or payload.get("type") != "access":
    return None
return payload

Access tokens (corta vida, ~15 min) son los únicos válidos. Los genera generate_agent_token().

4. Código Muerto: terminal/api/scripts.py

Archivo: Eliminado terminal/api/scripts.py + su re-exportación en __init__.py

Contexto: El router de Scripts SSH estaba expuesto en terminal/api/__init__.py pero nunca se montaba en las URLs. El punto de entrada real es network.api.scripts_router en /api/scripts.

Impacto: El modelo terminal.models.Script se conserva (lo usan backup/restore). Solo se retira el endpoint fantasma y el módulo redundante.

Cambios Operacionales

  • Migraciones: ninguna.
  • Tests: 6 nuevos en tests/api/test_terminal_agent_api.py (auditoría s122 → rectificación s123).
  • Diferido:
    • Carrera de doble-Primary en consumers._register_and_assign_role requiere migración + dedup.
    • UniqueConstraint parcial de Primary es endurecimiento a considerar.

Reglas de Honestidad Tocadas

  • Regla 13 (sin silencio): alerta registrada, no perdida.
  • Regla 15 (confirmación clara): endpoint responde 200 {“acknowledged”: true}, no miente con 500.

Véase también

  • [[entity—terminal—model—agent-instance]]
  • [[concept—saas—multi-tenancy]]
  • [[entity—monitoring—endpoint—evaluate-and-trigger-alerts]]
  • [[feature—terminal—agent-authentication]]
  • [[entity—terminal—endpoint—receive-agent-metrics]]
  • [[concept—reliability—atomicity]]