Skip to content

Originales: informe desconocido no equivale a colección correcta

Estado actual, 20 octubre 23:26:57 CEST: PR86 permanece incorporada al backend y PR87 está instalada, con rollback privado y comprobación independiente posterior a un reset natural. El primer postcheck UNKNOWN se conserva. La recogida ordinaria de las 21:15 publicó collection_success1 después de PR86; no es evidencia posterior a PR87.

Integración inicial en Git (20:37 CEST)

20 octubre 2026. Solo fuente desde 29f6f39; sin SSH, NAS, PBS, HTTP de apps, jobs, alertas forzadas, secretos/configuración privada ni cambios a Vera. Backend: scripts/original-backups/backup_originals.py. La sección final distingue la integración inicial y la comprobación posterior del despliegue.

La PR86 se fusiona el 20 de octubre a las 20:37:48 CEST en <REDACTED>. La CI39476196714 termina correcta: Linux Python 3.10/3.12 y contratos de alertas con promtool. La validación de integración repite las 100 pruebas correctas y una omisión local; MkDocs estricto no produce avisos. En esa fase inicial el backend vivo seguía sin sustituir: integrar fuente no corrige todavía el comportamiento del exportador instalado. La CI posterior39476432311 también termina correcta. La publicación documental 39476497021 acredita la revisión construida <REDACTED>.

Defecto reproducido y alcance

El exportador atrapaba last.json ausente/truncado como {}, devolvía rc0 y publicaba last_success0, failed0 y first_cycle_started=hora de observación para los cuatro grupos. Repetirlo dos días después reiniciaba esa edad otra vez: OriginalsStale nunca vencía. El wrapper existente interpretaba rc0 como originals_collection_success1. Esto no probaba una copia válida, pero ocultaba el estado desconocido al criterio de colección y de primer ciclo.

Reproducción privada únicamente con datos sintéticos: ~/.local/state/originals-metrics-report-proof-20261020/proof.json (0600, padre0700). Compara fuente base y candidata con ledger temporal ausente/truncado, reloj simulado en dos instantes separados por172801s, /proc ficticio y configuración simulada. No contiene datos del homelab ni credenciales.

Cambio de contrato de lectura

--metrics ahora devuelve rc2 y un mensaje genérico en stderr si falta el ledger, JSON/tipos son inválidos o un manifest seleccionado no puede leerse. La salida se acumula hasta completar toda la evaluación: no imprime métricas parciales saludables, ceros de bytes o fechas inventadas antes de fallar. No escribe last.json, manifests, lock o historial.

Los grupos deben pertenecer al conjunto configurado: una clave ajena no puede aportar el ancla de los cuatro grupos reales. Un recibo de éxito exige state=complete y completed/verified ISO válido con zona. {}, error+completed y complete sin fecha acreditada fallan en vez de convertirse en éxito o cero. El mismo criterio se aplica a complete en last.json. El rollover también puede archivar complete_existing: es historial parcial legítimo, pero no aporta éxito o fecha de verificación. Con ancla se observa como desconocimiento de éxito; sin ancla y sin otro recibo válido produce error de colección.

Si cualquier grupo configurado carece de éxito anterior y de una fecha inicial acreditada, también falla la colección: es evidencia desconocida, no pérdida de datos ni un primer ciclo recién empezado. El ancla conserva el literal ISO con zona, o el primer started acreditado del formato legacy. No usa ahora como fallback. Todos los grupos legacy con éxito fechado siguen observables aunque no haya ancla; en ese caso omite first_cycle y la alerta usa edad del éxito.

Un ledger legítimo guardado antes de la primera copia tiene groups{} y ancla: continúa válido, con fecha estable. Grupos nuevos/parciales y complete_existing conservan compatibilidad si hay ancla acreditada. Sin ella y sin éxito son desconocidos, no sanos. No se añade una métrica huérfana o un estado persistente.

La publicación del dump ausente en recibos legacy sigue permitida. Sus tipos malformados ya no se normalizan a failed0/success0 con rc0. El test anterior que exigía ese comportamiento se divide explícitamente en ausencia compatible y corrupción desconocida; no se afloja el contrato para preservar ese test.

Wrapper y alertas, sin cambios

ansible/roles/observability_collector/files/pvesr-collect.sh conserva su contrato: rc distinto de0 de la CLI remota produce únicamente collection_success0 y la hora del intento, sin inventar métricas de grupos. El archivo Prometheus se reemplaza como ya hacía; el backend no modifica ningún archivo Prometheus. La salida global del wrapper sigue siendo la de PVE, no certifica originales.

OriginalsCollectorMissing ya consume collection_success0 durante15m, tanto en scripts/original-backups/alerts.yml como en el template de alertas. No se cambian expresiones, plazos, canales, wrapper, calendario o jobs. No se fuerza la alerta ni se presume que vaya a recibirse una notificación real.

Despliegue: efecto potencial de publicar la fuente

  • Backend VM208 conocido: /home/monxas/scripts/backup_originals.py. Las fichas anteriores acreditan instalación manual/reemplazo atómico. En esta base no existe tarea Ansible que copie este backend; RunWisp solo referencia la ruta. Merge de este cambio no contiene mecanismo para reemplazarlo automáticamente. No se revalidó el binario vivo ni su hash después de los reinicios.
  • Wrapper: el rol observability_collector lo copia a /usr/local/bin/pvesr-collect.sh en los nodos. Play ordinario observability, sin never; main/Ansible-pull cada15m podría desplegar cambios de ese archivo. Este cambio lo conserva idéntico, igual que su cron de cinco minutos.
  • Tests se añaden a la matriz GitHub existente, sin nuevo job operativo. Publicar fuente solo ejecuta la CI ordinaria. El hold runtime posterior a los reinicios sigue vigente: no instalar backend, reiniciar ni ejecutar colección como smoke test por el mero merge.

Una instalación futura exige coordinar procesos/copia actual, preservar estado, reconciliar hash de destino y sustituir únicamente backend con rollback privado. No hay que borrar informes para probar un error. Rollback de fuente restaura también el blindspot; no acredita recuperación o coherencia de originales.

Verificación offline

test_metrics_report.py: once pruebas con ficheros privados, sin lectura de /proc real ni configuración. Incluyen falta/truncado/esquemas, ancla constante, éxitos legacy sin ancla, grupo nuevo desconocido, fallo con éxito previo y manifest corrupto. Wrapper real con comandos simulados comprueba CLIrc2 → collection_success0 y el contrato de OriginalsCollectorMissing15m existente. Cada lectura conserva bytes/inodos de los estados locales.

Se repiten las suites existentes de backup, capacidad y configuración; las fixtures minimalistas incorporan anclas reales cuando esperan métricas válidas. No se modifica el escritor del backup ni su interpretación de recibos de copia. Resultado local: backup59 (uno omitido por transporte rsync del Mac), capacidad14, configuración17 y métricas11: 100 correctas y una omitida. La CI conserva la matriz Python3.10/3.12 existente e incorpora el test nuevo en paths y ejecución. Su resultado no demuestra validación del runtime del homelab.

Cierre acotado: compatibilidad real a las 21:10 CEST

La comprobación del 20 de octubre a las 21:10:04 CEST confirma VM208 en proxmox, ejecutándose; el boot de huésped sigue siendo <REDACTED_UUID> y el padre conserva <REDACTED_UUID>. No es prueba de estabilidad física. El backend instalado continúa en SHA256 <REDACTED>.

El lector consume únicamente backend, ledger y hasta siete manifests por grupo, con límites y resolución de descriptores sin seguir enlaces sobre filesystem local. Selecciona exactamente el patrón grupo-????????.json del backend; la primera versión del lector sólo admitía fechas numéricas y se corrigió antes de utilizar esta comprobación para desplegar. Los validadores extraídos por AST de 26315bb aceptan el ledger y los tres manifests seleccionados; existe ancla de ciclo. No se importó ni ejecutó la aplicación, no se escribió estado ni se accedió al NAS.

La comparación AST entre el backend instalado y la fuente de PR86 acredita que sólo difieren las declaraciones de métricas y la rama CLI --metrics. Escritor, copia, imports y ejecución superior son iguales. Por ello el candidato acotado es PR86 (26315bb), SHA256 <REDACTED>. En esa intervención PR87 con fsync quedó excluida: su coste de I/O aún no estaba medido. Su admisión e instalación posteriores se describen al final.

Recibo privado de compatibilidad (0600 en directorio0700): ~/.local/state/originals-metrics-final-uozdfeq3/compatibility.json. SHA256 del recibo: <REDACTED>. Esta lectura cambia la decisión de compatibilidad, pero no acredita una instalación; no se ejecutan jobs ni se fuerza el colector como prueba.

Instalación real de PR86 a las 21:13 CEST

El backend se sustituye el 20 de octubre a las 21:13:22 CEST por el candidato PR86 exacto de 26315bb. Se acredita el hash nuevo f33f1b…6651, propietario root:root, modo0755, enlace único y 37422 bytes. No se instala la PR87 ni se ejecuta la aplicación para comprobarlo.

El instalador conserva el lock existente con flock exclusivo, comprueba dos veces ausencia de escritores y hace CAS del inode, nueve campos de identidad y hash del backend antes del reemplazo. Recibos de propietario PVE antes y después y boot del huésped coinciden con la comprobación de compatibilidad. Se conserva el estado: ledger y tres manifests, cuatro archivos de control, coinciden en bytes e identidad antes/después. No se escribe configuración, se reinician servicios ni se ejecutan copias, productor o colector.

Antes de ejecutar el instalador, el preflight inicial abortó: exigía por error root:root al lock, cuyo propietario histórico y vivo es1000:1000. Se corrigió esa condición sin modificar la exigencia root:root del backend, y otro agente revisó el diff. Un intento del lanzador local tampoco llegó a ejecutar el instalador por la licencia Xcode pendiente de /usr/bin/python3 del Mac; la lectura posterior confirmó backend antiguo intacto. Se usó el Python operativo de la sesión. No se aceptó ni modificó ninguna licencia del sistema.

Rollback local privado en VM208: /root/rollback-originals-metrics-20261020T191322Z-8codc05m/backend.before. Directorio0700, archivo0600 root:root y hash antiguo completo verificado. El recibo hermano receipt.json acredita fase installed, reemplazo confirmado, sin aplicación ejecutada y sin revisión manual pendiente. No se ejecuta rollback: restaurarlo reintroduciría el defecto de métricas y exige nueva reconciliación.

La lectura independiente posterior confirma backend, controles y rollback. Recibos locales privados: ~/.local/state/originals-metrics-install-drevdm52/. SHA256 de postverify-independent.json: <REDACTED>. La compilación nativa y los hashes prueban instalación; el comportamiento ordinario del colector requiere su siguiente ejecución normal. Ningún recibo acredita que las copias, NAS, hardware o restauración de Immich estén resueltos.

Recogida ordinaria posterior, 21:15 CEST

Prometheus CT217, con propietario fresco en proxmox2 y boot del observador sin cambios, contiene a las 21:15:37 CEST una muestra de las21:15:29.666. Los timestamps del productor y del intento son 21:15:02 CEST, posteriores al reemplazo21:13:22. originals_collection_success=1 y process_running0. No se ejecutó el colector manualmente: se consultó su resultado ya almacenado. La lectura anterior21:15:02 aún contenía productor21:10:01 y se descartó como prueba de recogida posterior; una muestra reciente no equivale a productor nuevo.

Recibo privado: ~/.local/state/originals-metrics-postdeploy-collected-eegsmz77/projection.json, SHA256 <REDACTED>. Acredita compatibilidad de la colección ordinaria con registros válidos. El rechazo de corrupción se probó offline; no se corrompió el ledger de producción.

Siguen firing en la selección consultada PBSDataReserveLow, nas_nfsd_dstate_degraded, OriginalsCopyFailed y OriginalsDumpPublicationRequired. No se presenta este despliegue como reparación de esos cuatro problemas, prueba de entrega de alertas o recuperación del hardware/NAS/Immich.

Actualización posterior: PR87 instalada a las 23:23:47 CEST

El backend se actualiza una única vez de PR86 f33f1b…6651 a PR87 <REDACTED>, root:root, modo 0755, enlace único y 41366 bytes, sin ejecutar el backend, jobs ni colector. Preflight fresco, lock existente, dos scans sin escritores y CAS protegen el reemplazo; controles del estado y propietario/boots se conservan durante esa instalación, antes del reset posterior. La corrección de métricas de PR86 permanece; este cambio añade durabilidad al writer y tratamiento explícito del checkpoint incierto.

La admisión previa de las 23:21:23 usa tres muestras sintéticas de 64 KiB en un sibling local del mismo dispositivo: dos fsync cada una, totales 6,226 / 9,883 / 6,810 ms, cleanup propio confirmado, sin NAS ni lectura de contenidos del estado. No es prueba de picos, resets, recuperación de almacenamiento o estabilidad física. El informe de cancelación conserva los hashes completos de instalación/admisión y los límites.

Rollback privado: /root/rollback-originals-checkpoint-20261020T212347Z-lq5gdmmk/backend.before. Recibo ~/.local/state/originals-durable-checkpoint-install-ofk29bx3/install-result.json, SHA256 <REDACTED>. El primer postcheck quedó UNKNOWN al fallar el guard del padre tras el reset natural de las 23:24:41 CEST, sin llegar a leer el huésped. Se conserva postverify-aborted.json, SHA256 <REDACTED>. No se repite la instalación. VM208 completó su arranque automático a las 23:25:20 CEST; boots nuevos: padre <REDACTED_UUID>, huésped <REDACTED_UUID>.

La única lectura independiente posreset de las 23:26:57 CEST confirma backend PR87 exacto, identidad instalada (inode 829863), rollback PR86 y su recibo fase installed, lock y cuatro controles intactos en bytes/identidad; diferencias vacías. Recibo postreset-independent.json en el mismo stage, SHA256 <REDACTED>. No se importa/invoca la aplicación, métricas, jobs, NAS ni escrituras. Sólo acredita persistencia observada del archivo tras este reset natural: no prueba picos/checkpoints de copia real ni causalidad fsync/reset. El mecanismo watchdog está acreditado, pero la causa del keepalive perdido sigue desconocida. La muestra ordinaria 21:15 anterior sólo acredita PR86; no se fuerza recogida.