Resumen
Sesión 125 (2026-06-10): primer paso del cierre de la Raíz 3 de la Auditoría Suprema. Implementación backend (totalmente inerte) de autenticación local cross-origin del Agent.
La feature se despliega en 3 fases:
- Fase 1 (este commit): backend + endpoints inertes + tests. Ningún cliente lo usa.
- Fase 2: frontend web — recoger y usar el token en requests locales.
- Fase 3: Agent .exe — generar, depositar y validar tokens.
Qué cambia en esta release
Backend
- Nuevo campo:
AgentInstance.local_token_enc(TextField, cifrado con Fernet). - Migración BD:
terminal/0003_agentinstance_local_token_enc.py. - Dos endpoints nuevos:
POST /api/agent/register-local-token— Agent deposita token (auth=None, autoverificado con JWT).GET /api/agent/local-token— Frontend recoge token (sesión +fleet:view+ aislamiento org).
- Schemas:
AgentLocalTokenSchemaenterminal/api/auth.py. - Cifrado seguro:
CredentialManager(Fernet) garantiza que el token nunca se guarda en claro.
Visible para clientes
Ningún cambio: la feature es totalmente inerte. Ningún cliente llama a los nuevos endpoints hasta la Fase 2.
Testing
- Suite completa:
tests/api/test_terminal_local_token.py(115 LOC). - Cobertura:
- Round-trip: Agent deposita, frontend recoge descifrado.
- Autenticación: Agent JWT requerido en POST, sesión en GET.
- Refresh tokens rechazados en POST.
- Aislamiento cross-tenant: admin de org B no puede leer token de org A (404).
- Sesión en GET: Agent JWT rechazado (403).
- Permisos granulares: solo
fleet:viewpermite leer. - Valores ausentes: Agent sin token aún → 404.
Propósito & Impacto
Problema
El Agent hoy no autentica requests locales. Cualquier página web puede enviar comandos a localhost:5050 sin restricción (Raíz 3 de la Auditoría Suprema).
Solución
Guardar de forma cifrada el token local del Agent. En Fase 2, el frontend lo usa en requests. En Fase 3, el Agent lo rechaza si falta o es inválido.
Impacto en seguridad
- Inerte esta fase: cero impacto visible (endpoints existen pero nadie los llama).
- Preparatorio: sienta las bases para Fase 2 y 3.
- Plan Hardening E: implementa
CredentialManager+ Fernet para secretos en BD.
Cambios técnicos
Ficheros modificados
CHANGELOG.md— entrada descriptiva sesión 125.RELEASE_NOTES.md— nota breve para usuarios (s125).terminal/models.py— campolocal_token_enc+ documentación.terminal/api/auth.py— 2 endpoints + helperAgentLocalTokenSchema.terminal/migrations/0003_agentinstance_local_token_enc.py— migración (AddField).tests/api/test_terminal_local_token.py— suite de tests (nuevo fichero).
Dependencias
core.security.credential_manager.CredentialManager— cifrado Fernet.core.utils.get_current_org— aislamiento por org.core.utils.require_perm— validación de permisos.
Schema de la DB
ALTER TABLE terminal_agentinstance ADD COLUMN local_token_enc TEXT NOT NULL DEFAULT '';
ADR & Decisiones
ADR: decision--20260610--terminal-auth-local-cross-origin
- Almacenamiento: Fernet (simétrico, cifrado en reposo).
- Endpoints: 2 (uno para Agent, uno para frontend).
- Autenticación: Agent JWT en POST, sesión en GET.
- RLS: aislamiento por organización en GET.
Versionado
Sin cambio de versión visible: la feature es inerte, no altera comportamiento del cliente.
(Próximo cambio de versión será cuando Fase 2 ó 3 active la feature.)
Fase 2 — Próximo paso
- Frontend: actualizar lógica de conexión local para:
- Solicitar token vía
GET /api/agent/local-token. - Pasarlo en header/body a requests contra
localhost:5050.
- Solicitar token vía
- Alcance: cambios en la web, lógica local del navegador.
Fase 3 — Agent .exe
- Agent: generar UUID local, depositarlo vía
POST /api/agent/register-local-token. - Agent: validar requests que lleguen de la web (usar token).
- Alcance: cambios en el binario del Agent (C# / TypeScript).
Véase también
- [[decision—20260610—terminal-auth-local-cross-origin]]
- [[entity—terminal—endpoint—register-local-token]]
- [[entity—terminal—endpoint—get-local-token]]
- [[entity—terminal—model—agentinstance]]
- [[concept—security—credential-encryption]]
Referenciado desde
- ADR · Autenticación local cross-origin del Local Agent (Auditoría Suprema · terminal · Raíz 3)
- Auditoría Suprema 2 · Tanda 5, 2ª pasada: revocar sin borrar, rotación del refresh y candado del invariante Primary
- Auditoría Suprema 2 · Tanda 5: revocación real del token del Agente y roles de flota sin condiciones de carrera
- Endpoint: GET /api/agent/local-token (Frontend recoge token local descifrado)
- Endpoint: POST /api/agent/register-local-token (Agent deposita token local cifrado)