Lanzador Estable del Agente (F1) — auto-update sin tocar el .exe
Resumen
v1.45.14 · Agent 2.14.0 (2026-07-07)
Fase F1 del Plan Lanzador Estable: reingeniería radical de la arquitectura de auto-update del Local Agent. El programa instalado (.exe) pasa a ser un bootstrap inmutable que se instala una sola vez y jamás vuelve a tocar. El código de la aplicación viaja en paquetes firmados (app-X.Y.Z.zip) que el bootstrap descarga, verifica y monta contra una cadena de fallback.
Problema resuelto: la raíz de los 6+ incidentes de actualización fallida (30-06 → 05-07/2026) era la carrera entre:
- El antivirus reteniendo/bloqueando un programa recién llegado sin reputación.
- El agente intentando ejecutarlo (porque el SaaS lo ordenó).
Resultado: brickazo silencioso o loop de actualización. Solución por construcción: si el binario instalado jamás cambia, el antivirus no tiene nada nuevo que evaluar. El riesgo criptográfico (firma Ed25519 verificada en el propio bootstrap) es incomparable a Authenticode.
Arquitectura
Bootstrap (terminal/agent/main.py)
~220 LOC, instalado una sola vez (versión 2.14.0 + clientes anteriores → migrate).
main.py (bootstrap, verificador Ed25519+SHA-256)
├─ Descarga manifest del SaaS
├─ Lee puntero atómico: qué versión ejecutar (current / previous / embebida)
├─ Verifica sha256 + firma Ed25519 del zip
├─ Monta `app_main.py` (y resto del paquete)
└─ Ejecuta app_main.py con fallback: current → previous → embebida
Clave pública Ed25519 embebida: PACKAGE_PUBKEYS_HEX en main.py (generada por scripts/agent/generate_signing_key.py, fuera del repo).
Paquete de código (app-X.Y.Z.zip)
~0,5 MB por release típica (Python compilado a bytecode), contiene:
app_main.py— la aplicación del Agente (código trasladado desde el viejo main.py)- Módulos:
core/,routes/,sentinel/,network/,platform_mod/ - Assets:
assets/terminal.html,debug.html,metrics.html,oui_vendors.json config.py,version.py,models.py, etc.
NO incluye:
LICENSES_AGENT.txt(servido desde .exe)- Dependencias compiladas (empaquetadas en bootstrap, cambiarlas require release de bootstrap)
Firma y distribución
-
scripts/agent/build_app_package.py(invocado porbuild_agent.batpaso 4.7):- Empaqueta el código →
app-X.Y.Z.zip - Calcula SHA-256
- Firma con clave privada Ed25519 (
~/.crearack/keys/agent_signing_ed25519.pem, FUERA del repo) - Emite
app-X.Y.Z.meta.json(version, sha256, sig_ed25519, min_bootstrap_api, built_at)
- Empaqueta el código →
-
Clave privada: generada una vez por
scripts/agent/generate_signing_key.py. FUERA del repo, en~/.crearack/keys/. Hacer backup cifrado manual. Si se pierden,--rotategenera nueva + actualiza PACKAGE_PUBKEYS_HEX del bootstrap. -
Descarga: endpoint
GET /downloads/agent-app/<version>(auth idéntica a/downloads/CreaRackAgent.exe).
Actualización en la máquina del cliente
Coordinada por core/pkg_updater.py:
pkg_updater.py (viaja en el paquete, se actualiza a sí mismo)
├─ Descarga zip + meta.json del SaaS
├─ Verifica con funciones del bootstrap (módulo agent_bootstrap, cero duplicación)
├─ Selftest: `--selftest-package` en destino
├─ Swap de puntero atómico (en ocio, nunca durante ejecución)
├─ pkg-watchdog: rollback de puntero si falla
└─ Jamás crea un binario nuevo
Fallback garantizado:
current= último paquete verificado OKprevious= paquete anterior (si current falla)embebido= paquete dentro del bootstrap (nunca brickazo, en el peor caso reverts a fábrica)
Manifest v2 (API)
Campo nuevo en /api/agent/manifest (heartbeat):
{
"version": "1.45.14",
"sha256": "<SHA-256 del .exe viejo, para legacy>",
"url": "/downloads/CreaRackAgent.exe",
"package_version": "1.45.14",
"package_url": "/downloads/agent-app/1.45.14",
"package_sha256": "<SHA-256 del .zip>",
"package_sig": "<firma Ed25519 en hex>",
"package_min_bootstrap_api": 1
}
Orden de publicación (crítico para evitar race conditions):
- Publica
app-X.Y.Z.zip+app-X.Y.Z.meta.jsonen media/downloads - Publica
.exe(bootstrap, si cambió) - AL FINAL: publica
.version(el manifest anuncia versión leyendo este file)
Así, jamás se ordena un update cuyos artefactos no estén ya servidos.
Testing y validación
19 tests nuevos (tests/agent/test_agent_launcher.py):
- Verificación SHA-256 + firma Ed25519
- Fallback current → previous → embebido
- Integridad ZIP (si se altera)
- Gate del pkg_updater
- Punteros atómicos (lectura/escritura race-free)
- Payload del servidor (campos package_*)
- Prune de versiones viejas
Suite agente: 83/83 verde · Suite servidor: 12/12 verde
Limitaciones (F1 → F2)
El bootstrap 2.14.0 aún no está compilado ni instalado en clientes. Pendiente F2 (task #187):
- Compilar el bootstrap (
build_agent.batpaso 4.7) - Instalar en la máquina de Edu (manual o última travesía por el swap legacy)
- 2-3 releases de validación E2E en producción
Sin impacto en Ayuda (nada visible para usuarios hasta que todos migren a bootstrap).
Plan completo
Ver agent-lanzador/PLAN_LANZADOR_ESTABLE.md (actualizado F1 completada).
Impacto de seguridad
- Antes: SaaS comprometido = poder inyectar código a máquinas de clientes (el SHA lo servía el SaaS).
- Ahora: SaaS comprometido = poder servir un paquete alterado, pero la firma Ed25519 lo detecta. La clave privada está FUERA del SaaS → nada que comprometer. Antivirus jamás ve un binario nuevo.
Véase también
- [[entity—terminal—service—app-main]]
- [[entity—core—service—pkg-updater]]
- [[entity—core—endpoint—download-agent-package]]
- [[feature—terminal—auto-update-self-healing-2-13-5]]
- [[runbook—terminal—firma-y-distribucion-paquetes]]