Skip to content

Contratos con propietario actual — 19 octubre 2026

Aplicación autorizada de los pendientes de review-next-runtime-20261019.md. Scope: scripts/service-onboarding, contrato ABS, comprobación de rutas central, secretos de entorno heredados en hunter/scanner. No se arrancó Onepacerr, Sablier, Bazzite ni se cambió la política equilibrada de los *arr.

Audiobookshelf: aplicado, propietario CT218

Contrato actualizado a /opt/personal/musica/compose.yaml, respetando los binds existentes; /podcasts y /audiobooks son de sólo lectura y ahora el contrato lo refleja. Se conserva config/metadata RW y se comprueba lectura de cuatro rutas.

El fleet de VM208 despacha sólo ABS a un endpoint SSH fijo. Clave dedicada sin secretos compartidos en reportes, from=192.168.0.XXX,restrict,command=... en CT218. El wrapper sólo acepta check/check-http, carga un manifiesto root-owned fijo y responde únicamente checks saneados. No permite comandos, URLs, hosts ni paths arbitrarios. Claves SSH de host pinneadas mediante canales ya autenticados: CT218 a través de pmx51/pct, CT212 por SSH del Mac. No se usó TOFU.

Runtime, Compose, entorno, mounts, backup nativo y backend HTTP en CT218; las dos afirmaciones del conversor siguen comprobándose en VM208. No se retiró la cobertura al mover el propietario ni se extendieron recibos vencidos.

Resultado real 20:36Z: ABS 0 fallos, 3 sin verificar (imagen mutable, flujo funcional con recibo vencido, login real pendiente). No se afirma reproducción real ni recuperación completa nueva. No se reinició ABS.

Rutas: aplicado, propietario CT212

El inspector VM208 importaba el homelab-ctl anterior y el pin rechazaba el código actual, dejando el informe del 18 octubre. Ahora usa un endpoint fijo de sólo lectura del CT212, con la lista root-owned de siete hostnames registrados. El caller coteja exactamente esa lista contra sus contratos, evitando informes parciales que aparenten cubrir productos ausentes.

Se revisó el código central y se mantiene pin SHA256 <REDACTED>. El adaptador no llama fetch_host ni collect_desired, porque escriben caches; recoge labels vivos con los comandos forzados existentes y transforma en memoria. El controlador, sus caches, el túnel y Caddy no se modificaron. La clave existente del controlador se usa únicamente para lectura de Caddy/labels; comprobación de hosts estricta. Cloudflare GET, sin peticiones a apps ni renovación de sesiones.

Resultado real 20:36:45Z: siete hostnames, exit_code 0, informe fresco en /home/monxas/appdata/service-onboarding/routes.json. Se validan origen, túnel, ambos Caddy y DNS; autenticación privada/login conserva su cobertura declarada.

Despliegue, comprobaciones y recuperación

Fuentes y versiones instaladas: VM208 checker/inspector/contrato; CT218 checker, wrapper y contrato root-owned; CT212 inspector/wrapper/allowlist root-owned. Jobs y calendarios existentes sin cambios. Backup VM208: ~/backups/service-onboarding-20261019/source.before. Authorized keys originales preservadas en ambos CT como /root/.ssh/authorized_keys.pre-contracts-20261019. No se sustituyeron claves existentes ni se retiraron permisos ajenos.

48 pruebas locales service-onboarding correctas. Nuevas regresiones verifican host/producto fijo, rechazo de resultado malformado, permanencia del conversor local y adaptador central sin escritura de caches. Ambos endpoints rechazan un comando arbitrario con rc2/stdout vacío, comprobado usando la clave dedicada.

Hunter/scanner: deriva real resuelta mediante recreación acotada

La comparación en memoria mostró CF_API_TOKEN presente en ambos runtimes del 18 octubre y vacío en Compose actual; las fuentes .env esperadas no lo contenían. No se imprimió ningún valor. El código de ambas aplicaciones no referencia ese campo: era entorno heredado sobrante, no una credencial necesaria que debiera copiarse a Compose para aparentar equivalencia.

Tras coordinación, se comprobó admisión sin trabajo puntual: scanner sin transferencias, hunter sin cola y cero slots activos en put.io. Se recrearon sólo plundrio-hunter y plundrio-scanner con --no-deps --no-build --pull never, sin cambiar imagen ni los binds ni flags previos (HUNTER_ENABLED, mantenimiento, autocleanup, notificaciones). Ambos volvieron healthy. El token heredado queda vacío, igual a Compose. Se preservó una huella sin secretos de imagen/mounts/flags en ~/backups/service-onboarding-20261019/runtime-before.json, modo600. Los loops automáticos que ya estaban habilitados retoman su operación normal; no se disparó un bulk/import/search manual ni se alteraron límites del hunter.

Fleet real 20:40:09Z: 11 contratos, 0 fallos, 29 sin verificar. El aumento 26→29 sin verificar es cobertura restaurada de los tres pendientes reales de ABS que el fallo del colector ocultaba; no se ampliaron caducidades ni se registró una restauración o login inexistente. Onepacerr original sigue fuera de esta intervención y de su política aún pendiente; plundrio-onepacerr conserva su contrato separado.

Informe guardado validado de extremo a extremo: 20 octubre 08:17 CEST

El lector service_check.py status aceptaba informes vacíos, esquemas desconocidos y un check fallido con contadores cero. Se reprodujeron tres salidas0 en memoria. La fuente main 1758160 valida estructura, fecha con zona, checks/servicios no vacíos y únicos, estados por categoría y contadores enteros recalculados. No acredita inventario completo ni salud de aplicaciones. Se preservan los estados skip, recorded y unverified; no se inventan recibos de login o recuperación.

El helper homelab-operations también omitía esa validación al leer el JSON directamente. Ahora consume sólo el CLI de lectura con ruta fija y timeout10s, valida su resumen y rechaza RC contradictorios o versiones sin marcador. No recurre al JSON bruto para dar por válido un estado rechazado. Filtro y fecha original se conservan; no ejecuta fleet, Docker ni sondas al consultar estado.

CI39379131318 SUCCESS: 84 pruebas de contratos +11 del consumidor en cada Python3.10/3.12. Hubo revisión independiente del validador y evaluación aislada del consumidor, sin informes reales ni red. El caso límite RC/summary contradictorio se añadió a las regresiones. quick_validate.py de la skill pasó.

Instalación protectora selectiva en VM208 a las 08:16–08:17 CEST: sólo /home/monxas/scripts/service-onboarding/service_check.py, SHA256 <REDACTED>. Se usó el bloqueo existente, backup privado, CAS de fuente/metadatos y sustitución atómica, preservando1000:1000/0644. Compilación nativa y validación del informe existente correctas. No restart, cambios de cron/RunWisp, HTTP ni jobs nuevos. CT218 conserva el checker anterior: las rutas check/fleet no cambiaron en este parche, y no se amplió el alcance de sus comandos forzados.

Backup VM208: /home/monxas/.local/state/service-check-validator-aqpio9eu (dir0700, original y recibo0600). El informe latest.json conservó hash e identidad: <REDACTED>. Su fecha es07:23:03 CEST, con11 servicios, 2 fallos y29 pendientes. El postcheck status devolvió1 por esos fallos; no se ocultaron ni se actualizó la fecha del informe.

La copia instalada de la skill en Mac se sincronizó por CAS, incluyendo helper, pruebas y entrada. Backup local privado: ~/.local/state/homelab-skill-status-20261020-m_cw7g51. Helper SHA256 <REDACTED>. La consulta real desde esa copia devuelve los dos servicios que requieren acción, los pendientes por separado, fecha original y live_verified=false.

Recuperación: revalidar fuentes y bloqueo antes de reponer sólo el helper desde su copia privada. No restaurar el informe ni lanzar el colector para probarlo. Si se repone un CLI anterior, el consumidor nuevo debe seguir rechazando su versión incompatible; reconciliar versiones, sin presentar el JSON bruto como estado validado.

Dos diferencias reales posteriores: clave n8n en Hunter/Scanner

El informe de07:23 muestra compose.environment en ambos servicios. La lectura privada de08:10 encontró exactamente una variable distinta, N8N_API_KEY, sin claves faltantes ni extra; imageID declarado y efectivo coinciden. Las dos apps comparten la misma clave efectiva y la misma declarada, pero ambas parejas difieren. No se imprimieron valores, hashes de claves ni payloads, ni se editaron secretos.

La revisión posterior de los11 Python versionados de custom/plundrio no encuentra lecturas N8N; el notificador visible utiliza ntfy. Esto no demuestra que ningún componente heredado de la imagen use esa clave. No se afirma un fallo de autenticación, ni se han enviado claves a una API, rotado credenciales o recreado contenedores. La lectura posterior de 08:20:27 CEST comprobó entrypoint/Cmd/WorkingDir como los wrappers Python previstos y ausencia de mounts que intercepten /app. Los seis archivos leídos (wrapper, plundrio_shared/__init__.py y arrapi.py en cada contenedor) coinciden por hash con el código versionado de stacks. En ese código propio acreditado no hay integración n8n ni uso de su clave.

No se auditó exhaustivamente la imagen base, startup hooks o terceros: no se presenta esto como prueba absoluta de que ningún componente lea esa variable. La reconciliación de un secreto posiblemente heredado queda pendiente de una limpieza acotada y revisada; no copiar automáticamente la credencial a Compose ni recrear contenedores sólo para silenciar el informe mientras sigue el hold.