CreaRack-SL

Servicio pkg_updater — Descarga, verificación y actualización de paquetes del Agente

Descripción

Módulo: core/pkg_updater.py
Introducido: v1.45.14 (Agent 2.14.0, 2026-07-07)
Contexto: Lanzador Estable (F1)

Servicio de descarga, verificación de integridad y instalación atómica de paquetes de código del Agente (app-X.Y.Z.zip). Viaja dentro de cada paquete y se actualiza a sí mismo.

Cero carrera con el antivirus: nunca crea un binario nuevo. Solo descarga zip, verifica, y swapea un puntero atómico en el filesystem.

Interfaz pública

Funciones principales

async def download_and_verify_package(version: str, manifest_payload: dict) -> bool:
    """Descarga app-<version>.zip + meta.json del SaaS.
    
    Parámetros:
      - version: ej. "1.45.14"
      - manifest_payload: dict con package_* fields del heartbeat
        (package_sha256, package_sig, package_url)
    
    Retorna:
      - True si descarga, verifica y prepara OK
      - False si falla cualquier paso (SaaS inaccesible, firma inválida, etc.)
    
    Comportamiento:
      - Descarga a ~/.crearack/packages/pending/<version>/
      - Verifica SHA-256
      - Verifica firma Ed25519 contra clave pública embebida en bootstrap
      - Ejecuta --selftest-package en destino
      - Marca como ready (atomic marker file)
    """

def swap_package_pointer(new_version: str, old_version: str | None) -> bool:
    """Swapea el puntero atómico de versión ejecutable.
    
    Parámetros:
      - new_version: versión a activar (debe estar ready)
      - old_version: guardar como fallback (prev)
    
    Comportamiento:
      - Lee puntero current → guarda en prev
      - Escribe new_version en current
      - Operación en ocio (jamás durante ejecución)
      - Retorna False si falla (filesystem lleno, permisos, etc.)
    """

def rollback_package_pointer() -> bool:
    """Revierte al paquete anterior (prev).
    
    Usado por pkg-watchdog si el paquete current falla. Retorna True si éxito.
    """

def verify_package_with_bootstrap_functions(
    zip_path: str, 
    sha256_expected: str, 
    sig_ed25519: str,
    pubkey_hex: str
) -> bool:
    """Verifica integridad usando LAS MISMAS funciones que el bootstrap.
    
    Parámetros:
      - zip_path: ruta a app-X.Y.Z.zip
      - sha256_expected: de app-X.Y.Z.meta.json
      - sig_ed25519: de app-X.Y.Z.meta.json
      - pubkey_hex: PACKAGE_PUBKEYS_HEX embebida en el bootstrap
    
    Comportamiento:
      - Importa modulo agent_bootstrap (registrado en sys.path por el propio bootstrap)
      - Llama agent_bootstrap.verify_ed25519_signature(...)
      - **Cero duplicación de código** → validación idéntica en descarga y en carga
    
    Retorna: True si firma OK
    """

Componentes internos

ComponenteResponsabilidad
DownloaderGET desde package_url, manejo de timeouts, reintentos, resume
VerifierSHA-256, Ed25519 (delega a agent_bootstrap), integridad ZIP
SelftestEjecuta --selftest-package en el zip desempaquetado (sanity check)
Atomic PointerLee/escribe ~/.crearack/agent/version.current y version.prev race-free
WatchdogMonitor async: detecta si current falla en <N ejecuciones, auto-rollback
PrunerLimpia versiones viejas (mantiene current + prev + embebido)

Ciclo de vida

Descarga y verificación

1. pkg_updater recibe manifest con package_*
2. Valida que package_sha256, package_sig, package_min_bootstrap_api son coherentes
3. Descarga app-X.Y.Z.zip a ~/.crearack/packages/pending/<version>/
4. Verifica SHA-256
5. Verifica firma Ed25519 (llamando agent_bootstrap.verify_ed25519_signature)
6. Extrae + selftest: verifica que imports funcionan, no hay archivos corrompidos
7. Marca como ready (toca file ~/.crearack/packages/<version>/.ready)

Swap atómico

8. En próximo ocio (idle, sin ejecutar app_main):
   - Lee ~/.crearack/agent/version.current → guarda en version.prev
   - Escribe <new_version> en version.current
   - Próximo restart del bootstrap: carga new_version

Rollback por watchdog

9. Si app_main falla en <3 ejecuciones:
   - pkg-watchdog detecta
   - Lee version.prev
   - Escribe version.prev en version.current
   - Próximo restart: carga prev (fallback garantizado)

Cleanup

10. Prune: elimina paquetes >2 versiones atrás (mantiene current + prev + embebido)

Gestión de errores

Sin Internet / SaaS inaccesible

if not download_and_verify_package(...):
    log.warning("Descarga/verificación falló, mantener current")
    # Próximo intento en 6 horas
    return

El Agente sigue funcionando con paquete anterior.

Firma inválida / alteración del ZIP

if not verify_ed25519_signature(...):
    log.critical("FIRMA ED25519 FALLO — INTENTO DE INYECCIÓN DETECTADO")
    # No swapea puntero
    # Próximo intento en 6 horas (por si fue glitch)
    return False

Jamás ejecuta código alterado. La clave privada está fuera del SaaS → un SaaS comprometido no puede falsificar firma.

Selftest falla

if not selftest_package(...):
    log.warning("Selftest del paquete falló (corrupt ZIP?)")
    # Descarta versión fallida
    # Próximo intento con versión anterior del manifest
    return False

Swap falló (filesystem lleno, permisos)

if not swap_package_pointer(...):
    log.error("No se pudo swapear puntero (FS lleno?)")
    # Paquete ready pero no activado
    # Próximo restart puede intentar de nuevo
    return False

Watchdog NO activa porque no hay error de ejecución (el swap falló antes de reiniciar).

Integración con bootstrap

Importación en el paquete

# En app_main.py, o en un handler de actualización:
from core.pkg_updater import download_and_verify_package
from core.agent_bootstrap import verify_ed25519_signature  # módulo registrado por bootstrap

# Usa las mismas funciones → validación idéntica

Punto de entrada asíncrono

async def update_agent_from_heartbeat(manifest: dict):
    """Invocado por el handler de heartbeat si hay package_*."""
    if manifest.get("package_version"):
        await download_and_verify_package(
            manifest["package_version"],
            {
                "package_sha256": manifest["package_sha256"],
                "package_sig": manifest["package_sig"],
                "package_url": manifest["package_url"],
            }
        )

Véase también

  • [[feature—terminal—lanzador-estable-f1]]
  • [[entity—terminal—service—app-main]]
  • [[entity—core—endpoint—download-agent-package]]
  • [[runbook—terminal—firma-y-distribucion-paquetes]]