Endpoint GET /api/agent/fleet — Listado de agentes en flota con estado de estabilidad
Descripción
Endpoint REST que expone el estado completo de todos los Local Agents conectados a la organización actual. Retorna array de AgentInstanceOut con telemetría en tiempo real (heartbeat cada 30s).
Firma
GET /api/agent/fleet
Authorization: Bearer <token>
Respuesta 200 OK:
[
{
"agent_id": "abc123",
"hostname": "OFICINA-PC",
"ip_address": "192.168.1.100",
"agent_version": "2.18.0",
"role": "primary",
"status": "online",
"sentinel_active": true,
"last_seen": "2026-07-31T10:15:22Z",
"connected_at": "2026-07-20T08:00:00Z",
"disconnected_at": null,
"disconnects_7d": 1,
"has_battery": false,
"health": {
"cpu_percent": 0.3,
"mem_mb": 48,
"db_mb": 2.1,
"pending_rows": 0,
"last_sync_age_s": 15
}
}
]
Respuesta 403 Forbidden (sin permiso fleet:view):
{ "detail": "Permission denied" }
Ubicación
- Archivo:
terminal/api/fleet.py - Auth: requiere JWT válido + permiso
fleet:view
Campos de respuesta
De siempre (pre-2.18.0)
| Campo | Tipo | Descripción |
|---|---|---|
agent_id | str | UUID único del Agent en el SaaS. |
hostname | str | Nombre del equipo donde corre el Agent. |
ip_address | str | null | IP interna (LAN) del Agent, validada server-side en /api/agent/agent_status. |
agent_version | str | Versión actual del binario (ej: “2.18.0”). |
role | str | “primary” o “secondary” en la flota. |
status | str | “online” u “offline”. |
sentinel_active | bool | ¿Hay un Sentinel monitor conectado? |
last_seen | str | null | ISO 8601 del último heartbeat. |
connected_at | str | null | ISO 8601 de la primera conexión en esta sesión. |
disconnected_at | str | null | ISO 8601 del último desconexión. |
disconnects_7d | int | Contador de desconexiones largas (gap ≥10 min) en los últimos 7 días. |
has_battery | bool | null | ¿El equipo corre en portátil? (reportado por agent_status v2.15+). |
Nuevo en 2.18.0 (v1.65.0 · task #209)
| Campo | Tipo | Descripción |
|---|---|---|
health | dict | null | Snapshot del bloque health del último heartbeat. NULL si Agent < 2.18.0 o sin heartbeat reciente. |
└ cpu_percent | float | Uso de CPU normalizado al total de la máquina (0–100). |
└ mem_mb | int | Memoria residente (RSS) en MB. |
└ db_mb | float | Tamaño de metrics.db en MB. |
└ pending_rows | int | Métricas sin sincronizar al SaaS. |
└ last_sync_age_s | int | Segundos desde el último sync exitoso. |
Casos de uso
Fleet Manager (frontend)
- Columna “Footprint”: renderiza
${health.cpu_percent}% CPU · ${Math.round(health.mem_mb)} MBsihealthexiste ystatus === 'online'. - Fallback: ”—” si no hay telemetría.
Observatory (ficha del agente)
- Variante SaaS-side: muestra CPU, RAM, DB Size, Pending Sync extrayendo de
health. - Variante localhost: sigue consultando
/healthlocal si está disponible (arquitectura simétrica).
Dashboard home (línea marginal)
- Refresco 30s: llama
/api/agent/fleet, busca el primer agente online conhealthy renderiza:Local Agent · <hostname>: <cpu>% CPU · <mb> MB RAM · <synced|metrics buffered>
- Silenciosa: no se muestra si array vacío, sin permiso (error 403) o sin agentes con
health.
Alertas operativas
- Tendencia de DB: si
db_mbcrece sostenidamente (>10 MB en 1h) → indicador de degradación de sync. - Buffer alto: si
pending_rows> 1000 ylast_sync_age_s> 300 → alerta de desconexión del SaaS.
Notas de implementación
Consumer (WebSocket, terminal/consumers.py)
Cuando el Agent envía un heartbeat con bloque health:
- Extraer solo las claves conocidas (
cpu_percent,mem_mb,db_mb,pending_rows,last_sync_age_s). - Validar que todos los valores sean numéricos (float/int).
- Rechazar dicts arbitrarios o nodos de payload malformados.
- Persistir en
AgentInstance.health(JSONField).
Esto implementa fail-closed: un payload roto no corrompe el modelo.
Serialización (Schema AgentInstanceOut, terminal/api/fleet.py)
El bloque health aparece en el schema como Optional[Dict[str, Union[int, float]]].
Retrocompatibilidad
- Agents < 2.18.0: no envían
health→ el campo es NULL. - Clientes viejos: ignoran el nuevo campo si existe (backward-compatible).
- Sin permiso: retorna 403 (igual que antes, ahora efectivamente).
Véase también
- [[entity—terminal—model—agentinstance]] — modelo que persiste
healthen el campo JSONField - [[feature—terminal—agent-health-heartbeat]] — feature padre que introduce la telemetría
- [[concept—saas—observability]] — principios de observabilidad del SaaS
- [[entity—terminal—endpoint—agent-status]] — endpoint hermano que reporta estado del Agent local