Immich y originales — revisión nocturna 19 octubre 2026¶
Se cerró una copia lógica independiente del dump19 en Synology. Tras un reinicio imprevisto de pmx50 se reanudó una vez la tarea existente de originales para terminar pics19, conservando evidencia anterior. No se inició restauración DB, contenedor, poda, scrub o aplicación. Los apartados finales contienen el estado más reciente; las lecturas anteriores se conservan con sus horas. No se han leído fotografías/vídeos privados ni modificado el copiador activo. La única mutación autorizada posterior fue ampliar el receiver con grupo de dumps y consulta readonly, mediante reemplazo atómico; se detalla abajo. Sin commit/push.
Copia que sigue activa y cómo continuar¶
Comprobación autoritativa con sudo en VM208, 23:47:16 CEST:
- Python root PID197552, padre197551, start_ticks399646; estado S/do_wait.
- rsync PID395430, padre197552, start_ticks838444; estado S/do_select.
- rsync rchar 41.508.160.938, wchar 41.518.869.044. Muestras anteriores rchar37.230.208.541 y38.248.473.856 demuestran avance, no un proceso atascado. Una muestra D durante NFS no basta para diagnosticar bloqueo: luego volvió S y siguió aumentando I/O.
last.jsongeneración20261019: documents/proyectos/photos complete, pics copying. Photos terminó23:15:31; pics comenzó23:15:35. No duplicar, señalizar ni reiniciar.
Este proceso se lanzó con backend antiguo y no escribe PID/heartbeat en last.json.
Leer /proc/197552/{stat,io} y /proc/395430/{stat,io} con sudo; monxas no
puede leer sus I/O. Comprobar start_ticks y PPID para evitar confundir PID reutilizado.
Cuando terminen, leer last.json y generación remota cerrada antes de proceder.
El observador existente conoce este modo legado; no se ha cambiado su fuente.
Qué acreditan los manifiestos¶
Photos apunta a
/volume1/backups/terramaster/snapshots-originales/photos/20261019, con tres
hashes SHA256 y lecturas de restauración aisladas registradas. Las tres muestras
son encoded-video, derivados; ninguna es un original upload/library. Por tanto
no acreditan fotos originales recuperadas. Pics aún no tiene muestras completas.
No existe photos-20261019.json individual en el estado local; el resultado
está conservado en last.json legado. No se fabricó manifiesto retrospectivo.
El backend nuevo crea manifiestos por grupo, pero el proceso que sigue activo
ejecuta la versión anterior. Preservar last.json al cierre, sin sobrescribirlo.
La política futura ya está en backup_originals.py mediante el trabajo paralelo
publicado en1925763: reserva muestras de upload/library. Esta revisión reutiliza
su función photo_original_area en tests; se retiró el módulo duplicado propuesto.
Véase night-originals-sampling-20261019.md. No se instaló esa política sobre el
copiador activo ni se reinterpretan sus tres muestras de derivados.
Generación de base de datos elegida¶
Fuente viva: /home/monxas/scripts/pg-dumps.sh, mantenida en
scripts/vm208/pg-dumps.sh. Exporta pg_dumpall a temporal, comprueba gzip y
publica atómicamente sin sustituir archivo diario existente.
Dump elegido: immich-db.2026-10-19.sql.gz, 183.486.870 bytes, terminado
19oct01:30:31 CEST (18oct23:30:31Z). Se ha leído gzip completo y comprobado
CRC; SHA256 comprimido:
<REDACTED>.
Procede de PostgreSQL16.10.
El análisis de COPY asset del dump devuelve 31.595 assets:
| Árbol respaldado | Assets dump | Assets vivos consultados | Checksum de Immich |
|---|---|---|---|
photos, montado /usr/src/app/upload |
10.803 | 10.832 | sha1 del contenido |
pics, montado /mnt/pics |
20.792 | 20.792 | sha1-path: hash de ruta, no del contenido |
Las 29 fotos nuevas posteriores al dump son diferencia de RPO, no prueba de pérdida. Recuperar ese dump conserva el punto01:30, aunque los árboles se hayan copiado muchas horas después. Los archivos posteriores pueden existir sin fila en ese dump. Lo contrario —filas cuyo archivo fue borrado/movido antes de la copia— solo se detecta cotejando cada referencia contra la generación cerrada.
Fingerprint anónimo de referencias COPY, en orden del dump:
<REDACTED>.
No se muestran IDs, nombres, propietarios o rutas personales.
immich-recovery-inventory.py reproduce este inventario, detecta gzip/COPY
truncado, raíces no cubiertas y escapes de ruta; solo devuelve agregados. No
lee bytes de fotos ni afirma cobertura de archivos o restauración.
Cobertura independiente del dump pendiente¶
El dump19 está en /home/monxas/appdata/_pgdumps, local a VM208. El script
appdata incluiría esa carpeta en un tar; sin embargo el destino vivo por defecto
/mnt/nfs_media/backups/appdata contiene únicamente un tar publicado
appdata-2026-09-08.tar.gz de23.438.860.208 bytes y dos artefactos corruptos.
No hay tar de octubre en ese destino. No se ha descomprimido23GB ni disparado backup.
Contexto acreditado después por el coordinador: el tar appdata se retiró intencionalmente de la programación02:00 desde20260908 tras comprobar recuperación PBS; Healthchecks pausado y RunWisp sin appdatajob. No es una regresión ni se ha reactivado. PBS protege la VM, pero acreditar el dump concreto dentro de PBS requiere su extracción/verificación. No se afirma que la base carezca globalmente de protección. La nueva copia lógica Synology complementa PBS y permite identificar ese dump de manera independiente; originales19 no contiene el dump por sí sola.
immich-recovery-receiver-extension.patch documenta la ampliación revisada:
grupo separado immich-dumps y operación status estrictamente readonly. Tras12
pruebas sobre árbol temporal, se aplicó la misma adición a fuente y receiver vivo
por autorización del coordinador, mediante reemplazo atómico y backup privado. No reutilizar proyectos para el
dump: contaminaría la selección de previous/link-dest de su siguiente generación.
No se cambiaron claves, configuración SSH ni ejecución rsync activa. El receiver
instalado conserva sus restricciones de rutas, staging y generaciones cerradas.
Secuencia revisada por el coordinador para la copia lógica dedicada:
- Extensión revisada/instalada atómicamente sin tocar procesos del rsync existente. status immich-dumps20261019 ya demuestra espacio real sin crear staging: 241.145.114.624 bytes libres, staging=false, complete=false. Exigir reserva64GiB más tamaño dump y staging. No usar prepare como consulta de espacio.
- Staging local privado0700 con copia inmutable dump0400 y SHA256 comprobado.
Manifest privado asocia hash/timestamp/assets a photos19 y pics19; registrar
coherence_verified=falsehasta cotejo de referencias. Nunca publicar secretos. - Preparar grupo immich-dumps/generación20261019 por canal restringido existente, transferir únicamente staging privado con rsync limitado, verificar SHA256 remoto, restaurar una copia privada acotada y verificarlo de nuevo; entonces complete. No reabrir ni escribir generaciones photos/pics ya cerradas.
- Registrar manifiesto vinculante local más copia remota. Solo después acreditar protección independiente del dump; no prometer consistencia de archivos todavía.
Receiver runtime /var/services/homes/monxas/.ssh/originals-receiver.py, uid1026,
modo0700; .ssh y base snapshots también0700. Hash anterior
<REDACTED>,
nuevo <REDACTED>.
Backup privado0600 originals-receiver.py.before-immich-dumps-20261019T235315.
Instalado por canal administrador existente con credencial específica solo en
memoria, sin imprimirla ni ampliar claves. Status posterior se ejecutó con la
clave restringida normal desde208 y terminó0. No prepare ni transferencia.
Ensayo DB aislado preparado y bloqueado por recursos¶
Imagen DB exacta existente:
sha256:<REDACTED>.
DB viva634.048.995 bytes; extensiones vectors0.2.1, vector0.8.1 y vchord0.4.3.
Preload vchord.so,vectors.so. UID/GID postgres verificado999:999.
Fuente immich-recovery-db-plan.py y ejecutor immich-recovery-db-drill.py:
- Por defecto plan; --execute explícito, imagen local exacta --pull=never.
- Red none, sin puertos, servidor Immich/Redis/ML/jobs no arrancan.
- Rootfs readonly, usuario999, todas capacidades retiradas, no-new-privileges. Solo pgdata privada nueva RW; ningún original o volumen de producción montado.
- Bypass del entrypoint Immich que escribe
/etc/postgresql, usando entrypoint PostgreSQL estándar. /tmp y socket de bootstrap en tmpfs acotados. - Memoria1GiB sin swap de contenedor, CPU0,5, pids128, shm64MiB, shared_buffers128MiB.
- Preflight exige MemAvailable≥3GiB para reservar2GiB, disco libre≥32GiB para workspace≤8GiB y reserva24GiB. Watchdog de bloques realmente asignados y espacio libre cada segundo; detiene si workspace≥8GiB, free<24GiB, MemAvailable<2GiB, SwapFree disminuye o timeout20min. Es vigilancia de consumo real, no cuota de FS: puede haber pequeño sobrepaso entre muestras; no se presenta como hard quota.
- Copia privada inmutable dump/CRC/hash antes de crear contenedor. Restore SQL ON_ERROR_STOP, logs0600; solo query de conteos/raíces/checksumAlgorithm al terminar.
- Inspect solo Image/Labels/límites/State, sin env. Timeouts10s en init/query/inspect. Cleanup comprueba name+token aunque docker run falle antes de devolver éxito; hijos TERM→KILL+wait; errores de limpieza conservados en evidencia.
No ejecutado: a23:47 VM208 MemAvailable2.740.928.512 bytes, inferior3GiB; SwapFree2.419.310.592 de6.424.817.664. No se consume margen N‑1 para conseguir una prueba. Disco local40.823.451.648 libres y TerraMaster5.210.X.X.X libres. Synology status posterior241.145.114.624 libres; esto no compensa la RAM.
El ejecutor tiene pruebas, pero no es una restauración PostgreSQL realizada. Comprobar recursos de nuevo y revisión del coordinador antes de --execute. No se ha creado workspace ni contenedor del ensayo. 12 tests locales de recuperación y12 del receiver pasan, incluyendo recursos, copias corruptas, rutas, anonimización, muestras originales, timeouts/cleanup selectivo y terminación de hijos. Sin pruebas sobre producción.
Criterio para una recuperación completa¶
Una DB restaurada no basta. Tras cerrar ambas generaciones y proteger el dump: restaurar DB aislada, exportar referencias a fichero privado y comprobar todas contra snapshots readonly. Exigir presencia/size consistente y comprobar SHA1 de los10.803 originales subidos. Para20.792 externos sha1-path no sirve de hash de bytes: añadir verificación independiente SHA256 y muestras de originales reales, sin tratar las tres muestras encoded-video como originales. No crear la app ni desencadenar análisis durante la prueba.
Limpieza posterior: solamente contenedor cuyo nombre y label figuren en result.json, después de confirmar detenido; preservar dump inmutable, informe y logs. Retirar únicamente pgdata del workspace creado por el ensayo cuando ningún proceso lo use. Nunca rm genérico de Docker, volúmenes de producción ni carpetas de backup.
Copia independiente del dump cerrada23:57:24¶
Por autorización concreta del coordinador se completó la secuencia anterior, sin esperar ni tocar el rsync de originales:
- Staging local
/home/monxas/.local/state/immich-recovery-dump/202610190700, copia dump0400, manifest0600. Hash del dump verificado antes de transferencia. - Transferencia única nice19, bwlimit4096KiB/s, al grupo dedicado immich-dumps; no retry automático ni modificación de photos/pics/proyectos/documents.
- Administrador verificó rutas literales, uid1026 y directorios0700; chmod0600 solo a los dos archivos exactos del staging, nada recursivo.
- Hash remoto coincidente. Lectura de restauración del archivo183.486.870 bytes
hacia
/home/monxas/.local/state/immich-recovery-dump/restore-20261019, privado0700, archivo0400; SHA256 coincide y gzip completo/CRC correcto. - Solo entonces
complete immich-dumps20261019cerró la generación. Destino:/volume1/backups/terramaster/snapshots-originales/immich-dumps/20261019. - Status posterior: staging=false, complete=true, espacio235.759.337.472 libres.
- Hash manifest remoto/local:
<REDACTED>. Vincula dump01:30/31.595assets y generaciones photos19/pics19, conservando coherence_verified=false y pics copying en momento del binding. - Evidencia privada0600:
/home/monxas/.local/state/immich-recovery-dump/evidence-20261019.json. Preservados dump inmutable, manifest, archivo leído de vuelta y evidencia.
Fuente reutilizable versionada immich-recovery-copy-dump.py: plan por defecto,
--stage o --finish --permissions-reviewed; valida hashes de generaciones ya
cerradas sin sobrescribirlas, no rehace automáticamente transferencias parciales,
no lanza DB ni aplicación. Las pruebas cubren idempotencia y requisito de permisos.
No se añadió cron ni se integró todavía en la tarea de originales11:10.
Última observación23:57:31: pics sigue copying, PID395430/ppid197552/start838444, rchar55.438.858.397 y wchar55.453.416.595, claramente avanzado. No se tocó last.json.
Esto acredita copia independiente y recuperación del archivo de dump, no una restauración de PostgreSQL ni recuperación coherente completa de Immich. La DB aislada sigue pendiente de margen de RAM/revisión y el cotejo de todos los originales sigue pendiente de cerrar pics y usar hashes de contenido apropiados.
Receptores alternativos: consulta readonly, sin arrancar plataformas¶
Bazzite192.168.0.XXX respondió TCP22; solo después se efectuó una conexión SSH
sin reintentos y con host trust estricto. Estaba ya encendido. Datos agregados:
MemAvailable26.016.210.944 bytes, disco home277.706.403.840 libres, swap16.771.444.736
completamente libre. Cumple presupuesto3GiB RAM/32GiB disco, pero docker no
está instalado en PATH y no hay proceso dockerd/podman. No se acreditó receptor
Docker activo ni se buscó una nueva plataforma. No se movieron imagen o dump.
Mac: docker CLI existente, contexto desktop-linux; docker info con timeout5s terminó1, daemon no disponible. Colima CLI ausente. No se arrancó Docker Desktop ni ninguna VM/runtime. Al no haber receptor elegible dentro del alcance, se cerraron las consultas; VM208 conserva guard/reserva2GiB y no se lanzó DB drill.
Podman existente acreditado; ensayo pausado por reinicio imprevisto¶
La consulta inicial de Docker no descartaba un motor daemonless. Tras revisión
expresa se comprobó Podman ya instalado5.8.7, rootless uid/gid1000, amd64,
cgroupv2/systemd con cpu/io/memory/pids, overlay, SELinux y seccomp activos.
podman info terminó0 sin arrancar socket/daemon. La imagen exacta769aa… no
existía en ese store. Bazzite tenía26GB RAM/277GB disco libres y swap sin uso.
No se instaló plataforma nueva ni se tocó AI/GPU.
Adaptación fuente immich-recovery-podman-plan.py y db-drill.py --engine podman:
misma imagen/SQL/watchdog, rootless keep-id999→host1000, pgdata privada :Z,
rootfs readonly, tmpfs explícitos acotados, networknone, memoria1GiB/CPU0,5,
pids128 y swap0. Antes de cualquier SQL se comprueban valores reales de
memory.max, memory.swap.max, cpu.max y pids.max, imagen, readonly, red y UIDmap;
si difieren o una opción falla, se detiene solamente el contenedor propio.
Inputs transferidos y DB workspace se suman para el límite vigilado8GiB.
Fundamento revisado: ayuda instalada5.8.7 y documentación oficial Podman run, que explica keep-id, etiquetado SELinux y tmpfs readonly. La prueba efectiva de cgroup es obligatoria, especialmente ante diferencias de versiones sobre memory-swap.
Se autorizó exportar solamente imagen existente769aa… desde208, sin pull,
y transferir el dump desde la lectura privada Synology verificada. Herramienta
versionada immich-recovery-transfer-bazzite.py: SSH estricto, nice19/gzip1,
4MiB/s, archivos0400/stage0700, hashes de transporte, deadline20min y limpieza
TERM→KILL; guard de consumo agregado del staging, sin retry automático.
La exportación abortó antes de recibir datos: a00:03 el alias media-208 vía
pmx50 devolvió No route to host. En Bazzite quedó únicamente
/home/monxas/.local/state/immich-recovery-input/20261020/postgres-image.tar.gz
de 0 bytes. No se transfirió dump, no se hizo podman load y no se creó ningún
contenedor. El coordinador confirmó reinicio inesperado de pmx50, no una
acción de mantenimiento autorizada. No se atribuye causa al intento de exportación.
Se pausaron exportación, traslados y ensayo; no hay reinicios/reintentos por este agente.
Evidencia y ledger después del reinicio,20oct00:04¶
VM208 respondió por su IP directa con el mismo host trust. A00:04:22 uptime40s;
PID197552 y395430 anteriores desaparecidos, ningún backup_originals.py nuevo.
last.json sigue diciendo pics copying, pero es estado persistido obsoleto:
la copia quedó interrumpida. Un rsync nuevo9214/ppid9197(sshd) no tenía sourcepics,
ni target snapshots-originales, ni .incomplete19; no se intervino ese proceso ajeno.
Consultas readonly al receiver tras arranque:
| Grupo19 | Staging | Complete |
|---|---|---|
| pics | sí | no |
| photos | no | sí |
| immich-dumps | no | sí |
Espacio Synology229.571.403.776 bytes. Ningún prepare, complete o relanzamiento posterior al reinicio. El dump y su evidencia previa siguen presentes; no se repitieron hashes completos ni se iniciaron copias durante el diagnóstico.
Ledger durable nuevo0600, con fsync de archivo y directorio:
/home/monxas/.local/state/immich-recovery-dump/interruption-20261020T0004.json.
Registra estado obsoleto, procesos desaparecidos, stagingpics conservado,
generación dump cerrada/coherencefalse y ensayo pausado0bytes/0load/0containers.
No modifica last.json ni manifiestos del copiador.
El siguiente paso requiere que el coordinador cierre el diagnóstico de estabilidad; no reanudar automáticamente ni publicar pics incompleto. La herramienta se niega a pisar staging previo. Las fuentes de recuperación tienen 16 tests locales, más12 de receiver; ejecución real de DB sigue pendiente.
Reanudación acotada de pics19 a00:11:13¶
Por autorización del coordinador, tras comprobar estabilidad de arranque y sin reanudar exportación/DB:
- Corregido
backup_originals.py: capturaprevious=dict(estado)antes de preparing. Si prepare devuelve already_complete, falta manifiesto individual y el estado previo era complete del mismo destino, conserva exactamente evidencia anterior. Otro destino no hereda muestras. No crea manifiestos ni declara nueva verificación. 17 tests backend pasan. - Preflight: ningún productor de backup, lock libre; RunWisp healthy, scheduling activo, config_stale=false y originals_backup sin ejecución previa. Pics source 73.785.622.528 bytes, destino229.571.403.776 libres: pasa incluso reserva conservadora de fuente completa+64GiB. El sizing se realizó con sudo como el backend; el usuario sin sudo no tiene acceso a algunos subdirectorios privados.
- Backup privado600+fsync de script y last.json en UTC221009:
backup_originals.py.before-pics-resume-20261019T221009Zylast.json.before-pics-resume-20261019T221009Z. Script previo; last previo . - Despliegue atómico del backend mantenido, hash
<REDACTED>. Se comprobó que last.json no cambió durante el despliegue. - Una invocación real mediante CLI --daemon hacia daemon existente y tarea
originals_backup; no nuevo daemon/job/cron. RunID 01M5B3G0E7CFGMZJVC7T6SJZ7Y, start20oct00:11:13+02, triggerapi. La tentativa anterior de obtener password automático falló antes de invocar exec y no disparó otro job; el canal local no necesitó revelar credenciales. Horario11:10, overlap skip, timeout18000 y bwlimit49152KiB/s conservados.
Documentos/proyectos/photos se saltaron como ya completos. Photos quedó exactamente igual al backup legado (tres muestras encoded-video y timestamps); ninguna reinterpretación como muestras nuevas de originales. Pics reutiliza su staging19; backend31548, rsync31621/parent31548/start_ticks45428, heartbeat10s. A00:15:14 rchar5.264.794.931/wchar5.266.737.349, avanzando.
Sesión rsync remota huérfana neutralizada sin cleanup¶
El chequeo posterior a la invocación encontró dos árboles rsync para el mismo TARGET: viejo7779→7780(parent7778, start2051788032/2051788074) y nuevo9498→9499. Las lecturas sudo mostraron que el viejo estaba inerte en poll_schedule_timeout, I/O invariable y con un archivo temporal abierto; el nuevo escribía activamente. El productor208 antiguo se había perdido por el reinicio.
El coordinador autorizó acción específica tras revisar riesgo de que TERM/cleanup renombrara un temporal viejo sobre la copia actual. Se revalidaron UID1026, commrsync, target exacto, PPID y startticks; SIGSTOP al hijo7780 y luego7779, confirmaciónT en ambos, SIGKILL a ambos sin TERM ni cleanup. No señales a sshd, PGIDs o sesión nueva. Ambos desaparecieron y quedó solamente9498/9499.
Temporal viejo 3.145.728 bytes preservado, existe y mantiene inode. Ningún
archivo borrado ni renombrado. Evidencia con rutas/FD/I/O reales se conserva
privada600 en Synology:
/var/services/homes/monxas/.ssh/orphan-pics-20261020T001434.json.
El informe público solo retiene metadatos anónimos. El temporal puede quedar
como artefacto extra de la generación; no se ha podado.
Ledger durable600+fsync en208:
/home/monxas/.local/state/immich-recovery-dump/resume-20261020T001113.json.
Conserva runID, hashes, handles, estado anterior y acción exacta de huérfanos.
No se volvió a modificar el receiver mientras la copia actual sigue abierta;
el hash vivo de la extensión es
Fallo de lectura de muestra y corrección del transporte SSH¶
El primer run terminó a00:20:30 con exit1/CalledProcessError después de completar la transferencia principal, durante la lectura aislada de una muestra. No se cerró la generación ni se fabricó verificación. El argumento remoto se construía con shlex.quote dentro de un argv subprocess; rsync3.2.7 ya protege argumentos SSH y aquellas comillas adicionales llegaban literalmente al receiver, que rechazó el comando. No era evidencia de fallo de los originales.
Se corrigió una línea: argv conserva el path remoto sin prequoting. Fixture real
mínimo VM208 rsync3.2.7/protocol31 → Synology rsync3.0.9/protocol30, misma clave y
receiver vigente: tres archivos sintéticos con espacio, apóstrofo y Unicode se
copiaron y restauraron con hashes iguales3/3; construcción antigua falla12.
Fixture privado700/600, sin complete, bajo staging separado
.incomplete-quote-fixture-20261020t0022-96f6e190; no se tocó pics19 durante el
ensayo. La limpieza selectiva de estos sintéticos queda pendiente del cierre.
La regresión usa transporte rsync real cuando existe3.2+; en Mac antiguo se omite.
Revisión y autorización del coordinador: preflight task anterior endedfailed,
sin proceso productor y cero receivers NAS de pics19. Copias0600+fsync de
script965 y last.json antes del reemplazo atómico22:28:41Z:
backup_originals.py.before-quote-fix-20261019T222841Z y
last.json.before-quote-fix-20261019T222841Z.
Estado previo SH<REDACTED>.
Se reutilizó una sola vez la tarea originals_backup existente a00:28:52+02, runID 01M5B4GB39GECPYM97ZZR9T41C. La fase main saltó rápidamente los bytes iguales y pasó verifying a22:28:56Z. Resultado todavía pendiente: solo el cierre nativo tras hashes remotos y restauraciones aisladas permite acreditar complete. No exportación de imagen ni ensayo DB durante esta recuperación.
Cierre acreditado de pics19 a00:30:08¶
RunWisp 01M5B4GB39GECPYM97ZZR9T41C terminó success/exit0 a 20oct00:30:08.372+02, duración75.402ms. El backend cerró nativamente pics a 22:30:08.312Z después de cuatro muestras con hash del origen, hash del destino y restauración privada aislada con hash igual; tamaños anónimos906.978, 5.113.691,4.828.134 y713.298.013 bytes. No complete manual ni poda.
Estado Synology readonly posterior: pics19 complete_exists=true,
staging_exists=false,217.287.405.568 bytes libres. Los cuatro grupos figuran
complete; photos conserva exactamente la evidencia legada de tres derivados.
No quedan procesos backup/rsync en208. Manifiesto pics19 SHA256
<REDACTED>.
Ledger privado0600+fsync:
/home/monxas/.local/state/immich-recovery-dump/pics-closed-20261020T003008.json.
El temporal huérfano preservado puede quedar como artefacto extra; no se eliminó.
El fixture sintético separado sigue privado y no representa otra generación real.
Este resultado acredita muestras reales restauradas de pics y cierre del conjunto de copias de archivos. No acredita todavía restauración PostgreSQL ni coherencia entre el dump y ambas generaciones, ni originales Immich recuperados end-to-end.
Nueva pausa del ensayo DB tras aviso de segundo reboot¶
El coordinador autorizó continuar DB-only después del cierre de pics. Preflight Bazzite: UID1000/Podman5.8.7, MemAvailable26.006.122.496 bytes, SwapFree16.771.444.736 y disco277.697.945.600 libres. Se preservó stage antiguo 20261020 con imagen0B y se inició una transferencia bounded a stage separado 20261020-retry0032,0700, archivo0400; límite4MiB/s/deadline1200s/reserva24GiB y presupuesto agregado8GiB conservados. Fuente exacta769aa… ya disponible en208.
El coordinador avisó de un segundo reboot pmx50 y ordenó pausa antes de cualquier
SQL. Se interrumpió solamente el orquestador propio con SIGINT; su finally cerró
ambos SSH propios mediante TERM→KILL+wait. Exit130. Comprobación posterior:
ambas imágenes staging 0 bytes, ningún dump transferido, ningún proceso
docker image save de nuestra imagen en208. No podmanload, contenedor ni SQL.
No se reintentará hasta nueva autorización; inputs se conservan sin borrado.
El cierre pics posterior al segundo boot sigue acreditado por run ended y NAS.
Propuesta siguiente: copia lógica diaria durable, solo análisis de fuente¶
Pendiente de revisión; nada implementado ni desplegado. Fuentes leídas:
scripts/vm208/pg-dumps.sh, ansible/host_vars/media-208.yml,
ansible/roles/homelab_tools/templates/runwisp.toml.j2,
scripts/original-backups/runwisp-task.toml, backend originals y helper dump19.
No consultas NAS/runtime adicionales para este diseño.
La tarea pg_dumps existente está a01:30, catch_up=skip/on_overlap=skip. Exporta seis DB, no despierta contenedores, protege con flock, publica por archivo temporal tras pipeline+gzipCRC y conserva un archivo final por día sin sobrescribirlo. Solo aplica retención local KEEP=5 si ninguna DB falla. Su exportación actual es la fuente: la nueva copia no debe ejecutar pg_dump ni consultar la aplicación. La copia externa puede fallar independientemente de otras cinco DB; no conviene acoplar el acceso NAS ni su retención a ese bucle de exportación.
Se propone invocar un publicador acotado una vez al final de originals_backup 11:10, después del bucle de cuatro grupos, incluso cuando se omiten como ya completos. Se conserva la misma tarea RunWisp, horario, catch_up y overlap; se mantiene el flock originals durante publicación. No nueva tarea/cron. Si falla un grupo y el flujo no llega al final, la publicación queda pendiente y visible; el dump local ya generado se preserva. Una futura política de rescate de backlog requiere revisión propia; no copiar todos los días históricos automáticamente.
Contrato propuesto para la primera implementación:
- Seleccionar un solo dump final Immich del día local esperado, no ocultos,
enlaces ni
.failed/.verified; exigir archivo regular, origen permitido, tamaño acotado y propietario/permisos privados. Si falta el de hoy, fallar de forma visible sin convertir ayer en éxito de hoy. No ampliar la retención local ni exportar otro dump como reacción. Abrir/fstat y copiar a staging700 con fichero0400, hashSHA256 y CRCgzip completo; volver a comprobar identidad del origen. Publicar manifiesto600+fsync y directorio fsync antes de red. - Identidad de generación, compatible con validador receptor:
d20261020-<sha256_completo64hex>(74 caracteres; límite80). Lleva fecha del dump y hash completo, por lo que otro contenido del mismo día obtiene otro destino. El dump19 ya cerrado conserva su nombre legado20261019. Nunca sobrescribir, renombrar o reutilizar otra generación publicada. - Manifiesto inmutable con nombre/tamaño/hash/CRC, fecha del archivo dump,
hora de staging, esquema de formato y referencias exactas a generaciones
photos/pics realmente observadas en evidencia local. No asignar automáticamente
fecha dump a generaciones de archivos. Contadores anónimos del dump pueden
venir del parser existente, sin SQLlive ni rutas personales en logs públicos.
coherence_verified=false,database_restored=falseyoriginals_restored=falsepermanecen explícitos. Su publicación no demuestra coherencia aunque las tres fechas coincidan. - Bajo el canal restringido existente: status readonly, reserva remota64GiB además de bytes entrantes; prepare únicamente la identidad derivada; sin link-dest/delete/inplace. Copiar dump+manifiesto a4MiB/s/nice19, deadline y presupuesto local explícitos. Hash remoto de ambos; restauración privada del dump entero (unos184MB actualmente), igualdadSHA256 y CRC antes de complete. Esto acredita recuperación de archivo, nunca recuperación PostgreSQL. Complete nativo solo después de terminar rsync y bajo lock remoto revisado.
- Idempotencia: si destino complete existe, comparar ambos hashes con la evidencia inmutable local; coincidencia devuelve éxito already_verified sin otra copia. Diferencia aborta sin sobrescribir. Staging existente solo se reanuda para misma identidad/manifiesto; sin relanzamiento ciego ni procesos paralelos. Ledger privado marca fases staging/hash/readback/published; no escribir last.json de los cuatro grupos ni alterar su evidencia.
- Retención externa inicial append-only sin poda automática: jamás eliminar generaciones anteriores ni incompletas ajenas. Si se alcanza reserva64GiB, fallar y avisar para revisión humana. Retención local pg_dumps5 permanece con su propietario actual. Staging privado de publicación fallida se preserva para diagnóstico; su limpieza selectiva y cualquier retención externa son cambios independientes revisables. Informar edad del último dump externo, estado y bytes mediante el informe/tarea existentes, sin nuevo daemon.
Bloqueos concretos antes de automatizar: el helper actual está atado a fecha,
hash y contadores de dump19; no reutilizarlo como programa diario. El finish
actual requiere revisión de permisos por administrador; para un flujo sin nueva
credencial se debe revisar primero umask077 aplicado solamente a rsync receptor
immich-dumps y demostrar directorios700/archivos600 con el mismo canal. El
receiver candidato de locks/gramática aún no equivale a receiver instalado;
conservar live5bab hasta revisión y despliegue explícitos del coordinador.
La fecha fija del helper dump19 es evidencia histórica y no debe cambiar para
“hacerlo diario”. Adaptación nueva necesita tests de fecha/hash/idempotencia,
colisión, CRC, readback y fallo antes de complete sobre fixtures privados.
Revisión del helper de transferencia DB antes de publicarlo¶
immich-recovery-transfer-bazzite.py sigue siendo un one-shot de la incidencia,
no una tarea automática. Su STAGE cambió de20261020 a20261020-retry0032 para
preservar el0B previo; el segundo stage también acabó0B y se conserva. El helper
actual volverá a fallar cerrado si se invoca otra vez porque rechaza stage
existente. No quitar esa guarda ni reutilizar esos directorios. También se
explicitan ProxyJump=none/ProxyCommand=none manteniendo StrictHostKeyChecking=yes
para acceso directo existente, sin nuevas claves.
Antes de promoverlo a herramienta reutilizable: sustituir STAGE fijo por
argumento obligatorio validado bajo raíz privada fija, registrar origen/destino
exactos y exigir --execute con plan por defecto; permitir únicamente crear un
stage nuevo, sin auto-rename/retry/cleanup. Imagen/dump deben continuar ligados a
ID/hash revisados. Conservar deadline1200s,4MiB/s y presupuesto agregado8GiB.
El SIGINT del orquestador sí ejecuta finally y cierra hijos; no presentar una
muerte abrupta como garantía de terminar procesos remotos. Tras interrupción,
comprobar solo nuestros procesos exactos y artefactos, como se hizo aquí.
No implementado; no transferencia, DB ni SQL adicionales tras la segunda pausa.
Fixture final del receiver: a28c832, sin instalación¶
Por autorización expresa, el candidato exacto SHA256
<REDACTED>
se ensayó directamente en NAS a00:34:31, Python2.7.18 y rsync3.0.9/protocol30.
Esta prueba es distinta de la anterior90bb5de; no implica que aquel hash cubriera
las correcciones posteriores de gramática. BASE sustituido únicamente en una
copia privada temporal; ningún acceso a208/pmx50 ni instalación de producción.
Resultado PASS: cuatro variantes con operandos extra rechazadas exit1;
flags standalone -L, -s, -rtL, -rs, --copy-links, --secluded-args,
--delete y --inplace rechazados exit1. Los perfiles actuales
-ltre.iLsfxCIvu y sender -te.LsfxCIvu alcanzaron rsync real, esperaron su
protocolo y terminaron43 al cerrar EOF, sin enviar datos de aplicación.
FD3 del lock permaneció heredado tras exec; segundo writer y complete concurrentes retornaron75. Sender readonly no quedó bloqueado. Tras cerrar el writer, complete funcionó, repetir complete y escribir tarde se rechazaron; hash del archivo sintético completo siguió igual. Lockdir700 y lockfile600. Ningún PID remanente; BASE temporal propio eliminado. Solo tres bytes sintéticos locales, ninguna transferencia ni archivo real del homelab leído en esta prueba.
Evidencia privada600+fsync en NAS:
/var/services/homes/monxas/.ssh/receiver-fixture-a28c832-20261019T223431Z.json.
Harness SHA256
<REDACTED>.
El receiver live no se modificó durante el ensayo. Despliegue pendiente de nueva
instrucción del coordinador; DB y transferencias siguen pausados.
Despliegue autorizado del receiver final a00:35:13¶
Tras revisar el fixture final, el coordinador autorizó instalar exactamente
<REDACTED>.
Precondiciones satisfechas: hash live5bab exacto y cero writers rsync de
originals, sin señales a procesos. Backup privado0600+fsync del script anterior:
/var/services/homes/monxas/.ssh/originals-receiver.py.before-a28c832-20261019T223513Z.
Reemplazo atómico, fsync del directorio y hash posterior correcto; live conserva
UID1026 y permisos0700. Evidencia privada600+fsync:
/var/services/homes/monxas/.ssh/receiver-deploy-a28c832-20261019T223513Z.json.
Cinco status readonly mediante la clave restringida existente desde208: documents, proyectos, photos, pics e immich-dumps de20261019 tienen complete_exists=true y staging_exists=false; libres217.287.405.568 bytes. No cambios a datos/generaciones, archivos lock existentes, sshd, claves ni perfiles. Rollback: previa comprobación de ausencia de writers, verificar hash del backup5bab, reemplazar atómicamente el script por esa copia con UID1026/mode0700 y fsync del directorio; nunca borrar o reinicializar locks para volver atrás.
Este despliegue protege writers futuros con lock NAS y gramática rsync cerrada. No incorpora el publicador diario propuesto ni permisos específicos para dumps; no vuelve a ejecutar backups. DB y transferencias siguen pausados.
Publicador diario implementado en fuente, todavía desactivado¶
Módulo nuevo scripts/original-backups/immich-dump-publisher.py, API
publish_existing_dump(report, channel=None), sin efectos al importar ni
exportaciones SQL. La integración fuente opt-in pertenece al agente de
mantenimiento; no se cambió RunWisp ni se desplegó el módulo. El estado por
defecto sigue desactivado. El CLI separado muestra plan y rechaza ejecutar fuera
del lock/tarea originales; no añade un segundo scheduler.
Selecciona exclusivamente el nombre final de hoy según el reloj local del exportador, independientemente de report.stamp. Resuelve productor monxas mediante pwd y exige UID1000, mientras staging pertenece al caller root del job sudo. Rechaza symlinks en padres/origen, ficheros públicos/no regulares/hardlinks, mtime de más36h o más5min futuro. Fuente comprimida máxima2GiB; copia solamente bytes del fstat inicial con detector de crecimiento, reserva local8GiB+2dump y watchdog300s; no usa la reserva remota64GiB como requisito local. CRC expandido máximo8GiB/300s. Generación fecha+hash completa de74 caracteres, manifiesto inmutable600, dump400/directorios700 y fsync. Conserva referencias concretas a photos/pics y declara siempre coherence/database/originals_restored=false.
El canal exige capabilities immich-dump-private-v1 y auditoría de permisos del
receiver antes de upload, antes de complete y después; ausencia o false abortan.
El prerequisito nuevo sigue sin desplegar al escribir este apartado; livea28
no acredita esa capability y no permite activar esta integración. Hash de ambos
archivos y readback privado del dump completo+CRC preceden a complete nativo.
Destino publicado existente solo devuelve éxito tras comparar hashes; no borra
ninguna generación ni poda externa. Conserva stages fallidos para diagnóstico.
SSH/rsync locales usan sesiones propias y timeout; cancelación/timeout ejecutan TERM→KILL del grupo propio y reap. SIGTERM tiene handler temporal solo dentro del publisher en mainthread; cleanup ignora brevemente SIGTERM/SIGINT repetidos y restaura handlers al terminar. Esto no promete eliminar procesos remotos tras una caída del host: el lock NAS protege futuras conexiones mientras sobrevivan. El readback monitoriza también temporales hermanos de rsync cada0,5s, tamaño esperado y reserva local8GiB; es watchdog con posible tránsito entre muestras, no cuota dura de filesystem. No se ha ejecutado contra NAS.
Validación local: 36 tests dedicados PASS, incluidos callerroot/productor1000, 40.422.686.720 bytes locales admitidos/reserva remota64GiB intacta, origen cambiante, CRC/fecha/identidad/permisos, idempotencia, reserva, readback corrupto y pérdida de privacidad antes/después de complete. Fixtures reales de árbol de procesos prueban timeout, KeyboardInterrupt y señales repetidas sin descendientes vivos, fallo al crear proceso y restauración de handlers. Backend integrado:25 tests PASS, uno omitido por rsync antiguo del Mac. Ninguna prueba usó SSH ni datos reales.
Pendiente antes de enable: revisar/desplegar prerequisito privacy del receiver, revisar módulo+hook, verificar contrato UID/rutas desplegadas y documentar flag opt-in en la tarea existente. Ningún cambio de runtime, cron, credenciales, DB o retención se autoriza mediante este apartado.
Ensayo del prerequisito privacy6dcc: transferencia local NAS bloqueada¶
El candidato fuente
<REDACTED>
aún no se desplegó. Un fixture aislado Python2 intentó usar rsync Synology como
sender local hacia un shim/candidate privado: root devuelve44 userdisabled y
monxas52 servicedisabled, antes de transmitir el payload sintético. Preservar el
entorno SSH no cambió52. No se habilitó servicio ni se modificó autenticación.
Esto es una limitación del sender Synology del harness, no un resultado PASS ni
un fallo demostrado del receiver. Todos los temporales propios se eliminaron,
sin PIDs restantes; producción permanece a28c832. Evidencias privadas0600 en
.ssh/receiver-fixture-6dcc7cca-20261019T224356Z.json,224417Z,224435Z y224452Z.
Se comunicó al coordinador antes de cambiar método; DB/publicador pausados.
Prerequisito privacy: fixture real y despliegue a00:48:34¶
La limitación del sender NAS se resolvió usando el canal administrador existente
Mac→NAS, autorizado por el coordinador, sin208 ni cambios de servicio/configuración.
El destino /tmp del primer transporte también fue rechazado por política vendor;
se usó únicamente un directorio NUEVO privado bajo el share permitido
/volume1/backups/terramaster/.receiver-private-mac-fixture-*, separado de
snapshots-originales. Nunca se cambiaron permisos de los padres/share. Un primer
input600 no distinguía la umask; el definitivo usó archivo sintético644 dentro
de directorio privado700 y payload de tres bytes. Openrsync Mac2.6.9→NAS3.0.9
terminó0 en ambos envíos; --dirs redundante se normalizó solo en el shim del
fixture. La gramática de producción no se relajó.
PASS final a00:48:13, candidato exacto6dcc7cca: receiver ejecutado como UID1026
con umask heredada022 produce dump0600 y pics0644, hashes sintéticos iguales.
Status acredita capability y private_permissions true/false/null; rechaza
archivo0644, directorio0750, symlink y más de dos archivos. Tras complete del
fixture, complete=true/staging=null. Monitor administrador solo leyó FD porque
DSM restringe /proc al propio usuario del rsync; cada receiver siguió con UID1026.
FD3, writer/complete75, sender readonly, perfiles vigentes, rechazo de operandos
extra/flags peligrosos y complete inmutable también PASS. Sin PIDs remanentes,
temporales propios eliminados. Evidencia privada600+fsync:
/var/services/homes/monxas/.ssh/receiver-fixture-6dcc7cca-mac-20261019T224813Z.json.
Con precondiciones livea28 exacto y cero writers originals, despliegue
atómico autorizado a00:48:34, fsync del directorio y verificación de hash:
<REDACTED>, UID1026,
modo0700. Rollback privado0600+fsync del a28 anterior:
/var/services/homes/monxas/.ssh/originals-receiver.py.before-6dcc7cca-20261019T224834Z.
Evidencia privada del despliegue:
/var/services/homes/monxas/.ssh/receiver-deploy-6dcc7cca-20261019T224834Z.json.
Status readonly del dump19 por receiver instalado, como UID1026: complete_exists
true, staging_exists false, capability immich-dump-private-v1,
private_permissions complete=true/staging=null; libres217.287.401.472 bytes.
No se cambiaron permisos previos de datos, generaciones, locks, sshd ni claves.
La nueva umask se limita a futuras operaciones immich-dumps; no hace chmod de
copias anteriores. Publicador diario/flag de job, DB y transferencias masivas
siguen pausados hasta decisión conjunta posterior del coordinador.
Método nuevo de inputs DB, sin VM208: pendiente de adquisición¶
El coordinador autorizó sustituir la exportación de imagen desde208 por la cadena
verificada en night-immich-db-image-digest-20261020.md: manifest plataforma
<REDACTED> referencia
config/ImageID exacto769aa218…, linux/amd64,21 capas comprimidas208.950.383 bytes.
Para respetar4MiB/s también en imagen se descarta podman pull nativo (sin
limitador equivalente) y se elige un único método OCI serial HTTPS, sin tags ni
fallback. No se usa208, TerraMaster.20 ni los directorios0B de intentos anteriores.
Fuentes nuevas: immich-recovery-oci.py, immich-recovery-offhost-inputs.py y
test_offhost_inputs.py.14 tests fakes/locales PASS; revisión independiente del
agente update_retries PASS antes de ejecución. El formato sigue
OCI Image Layout
y descriptores OCI.
Manifest/config/cada capa exigen SHA256 y tamaño exactos; config prueba plataforma.
Token anónimo readonly solo RAM, HTTPS obligatorio y redirects limitados a
GHCR/githubusercontent; Authorization se elimina al cambiar host. Lecturas read1
con watchdog y pacing64KiB sin crédito por espera;4MiB/s, alarm1200s. OCIarchive
sin tags, load rootless y verificación posterior de ID769/amd64/linux obligatoria.
No se crea un Dockerarchive adicional: el runner existente inspecciona imagen
local exacta y no consume archivos de imagen directamente.
Dump19 inmutable183.486.870 bytes/SHA6c3b… y manifiesto701 bytes se recuperan por rsync administrador NAS→Mac, credencial existente solo RAM, hosttrust estricto; stageMac700/dump400, hash y CRC expandido≤8GiB/300s, fsync, reservaMac2GiB. Después se transmiten a stage Bazzite NUEVO exclusivo,≤4MiB/s y hash exacto. Prerrequisitos Bazzite3GiB RAM/32GiB disco, reserva durante operación2GiB/24GiB, sin swap adicional para faseOCI; stages≤8GiB agregado. El imagestore rootless está fuera de esos8GiB, cubierto por guardia global de espacio libre24GiB. Se preservan stages fallidos; ningún prune ni eliminación de capas compartidas. Ownership privado registra si la imagen existía antes del load; rollback manual solo puede retirar el ID importado propio si antes no existía y no tiene ningún consumidor. No se promete rollback atómico del imagestore.
El primer intento 20261020-oci0055 terminó antes del primer evento de stages,
en0,29s; el stage Mac no existe, por lo que no alcanzó lectura NAS, streaming ni
faseOCI/load. El mensaje genérico no conservó clase/fase del preflight; se corrigió
fuente para registrar únicamente fase y clase de error sin secretos ni URLs.
No reintento automático ni sondeo repetido de host offline. Pendiente distinguir
TCPoffline de preflight remoto mediante comprobación acotada del coordinador.
No contenedor ni SQL; publisher diario continúa OFF.
Causa del preflight y adquisición en curso¶
La lectura diagnóstica acotada confirmó Bazzite online, UID1000, sin procesoOCI ni
stage0055 existente. El segundo intento con fase/clase identificó el fallo antes
del mkdir en preflight remoto. Evidencia de metadatos: /home es symlink root-owned
a /var/home; input root canónico /var/home/monxas/.local/state/immich-recovery-input
es UID1000/mode700. La guarda contra padres symlink rechazaba correctamente la
ruta alias, sin que ello acreditara host offline. No se relajó: ambas constantes
pasaron a ruta canónica explícita, manteniendo las mismas restricciones.
MemAvailable26.002.321.408, SwapFree16.771.444.736 y disco277.697.892.352 bytes.
Delta revisado independientemente PASS,14 tests de nuevo PASS. Hashes revisados:
offhost
Inputs dump completos; OCI detenido antes de load¶
El método nuevo recuperó el dump NAS→Mac:183.486.870 bytes, SHA6c3b… y CRC completos
verificados; manifiesto701 bytes/SHA55014e15… iguales. Ambos se transmitieron a
/var/home/monxas/.local/state/immich-recovery-input/20261020-oci0055 con tamaño
exacto/hash y fsync; stageUID1000/mode700. El stage Mac también conserva los dos
archivos privados400 bajo .local/state/immich-recovery-offhost/20261020-oci0055.
OCI falló durante la config antes de capas: manifest4281 bytes/SHA886090 correcto, config0B, dos blobs en total, sin archive, sin image-ownership ni verified-record. Por orden del código no se ejecutó podmanload. Allocated stage183.517.184 bytes. Nada de SQL/contenedores, ni cambios a208. Se preserva este stage completo, incluido el blob0B; no se sobrescribe ni se vuelve a invocar a ciegas.
Una única consulta diagnóstica sin destino de archivo recuperó después la misma config769 correctamente:15.677 bytes, hash igual, redirect único a pkg-containers.githubusercontent.com. No se relajó allowlist/auth/TLS. El tipo original no se conservó; no se afirma una causa HTTP concreta ni se da el ensayo por completo. Se añadió observabilidad fuente de fase/digest/clase/statusHTTP, sin URLs firmadas ni token. Pendiente decisión del coordinador para un NUEVO stageOCI que aproveche el dump ya acreditado sin duplicar tráfico NAS.
Inputs finales acreditados; todavía sin contenedor ni SQL¶
La fase0105 descargó/verificó manifest, config y21 capas, creó OCIarchive y cargó
la imagen una sola vez. Falló después por una plantilla propia incompatible:
Podman5.8.7 usa .ID, no .Id. Lectura correcta mostró el ID completo esperado,
amd64/linux; no era un mismatch de imagen. Se corrigió formato y normalización
estricta64hex en fuente futura. No se repitió fetch/load ni se eliminó el failure
histórico de0105. Root revisó y autorizó UNA reconciliación de solo lecturas.
immich-recovery-reconcile.py SHA
<REDACTED>
rehaseó manifest/config/21capas/archive y dump+manifiesto, repitió CRC con identidad
before/after estable, inspeccionó .ID y escribió un NUEVO combinedproof0105.
21 tests de inputs/reconcile PASS. Resultado inputs_verified_after_inspect_format_fix:
- Imagen exacta
sha256:<REDACTED>, linux/amd64. No existía antes del load; ninguna retirada automática de imagen. - OCIarchive208.998.400 bytes, SHA256
<REDACTED>. - Dump0055 conserva183.486.870 bytes/SHA6c3b…, CRC verificado y nodo estable; manifiesto701 bytes/SHA55014e15…; sin cambios a0055 ni nuevas lecturas NAS.
- Padre input-root agregado601.653.248 bytes; disco libre276.318.248.960, MemAvailable25.980.825.600 y SwapFree16.771.444.736 bytes.
- Python existente 3.14.8, sin instalar ni cambiar herramientas.
Proof remoto0400/UID1000:
/var/home/monxas/.local/state/immich-recovery-input/20261020-oci0105/combined-inputs-verified.json,
SHA256<REDACTED> (1746bytes).
Copia Mac0600:
~/.local/state/immich-recovery-offhost/20261020-oci0105/inputs-verified.json,
SHA256<REDACTED>
(1747bytes; mismo JSON, newline final adicional).
El siguiente runner debe usar input-root padre canónico
/var/home/monxas/.local/state/immich-recovery-input y work como hermano,
para contabilizar0055+0105+stages0B. El imagestore sigue fuera del presupuesto8GiB,
con reserva global24GiB. Esto acredita inputs exactos disponibles; no acredita
restore PostgreSQL, originales ni coherencia dump+archivos. Contenedor/SQL
siguen pendientes de revisión del runner y autorización posterior del coordinador.
No se tocaron los informes de coordinación/layout que mantiene Root.
Ensayo DB-only 1: rechazado antes de crear contenedor, sin SQL¶
Tras publicación4e6a091 y autorización explícita del coordinador se instalaron
cuatro fuentes en NUEVO code stage privado20261020-code0120, verificando hashes:
runner/var/home/monxas/.local/state/immich-recovery-db-20261020-0120.
Plan/preflight pasó: MemAvailable25.999.097.856, SwapFree16.771.444.736, disco libre276.318.175.232, inputs601.690.112 bytes y blockers=[]; originals_mounts=[]. Se ejecutó una sola vez el runner congelado con fuente0055 SHA6c3b… y presupuesto padre canónico. Terminóexit1 antes de kernelproof.
Causa concreta del log privado (60bytes): Podman5.8.7 rechaza uid=999 en opciones
--tmpfs, durante el parseo de /var/run/postgresql. No se creó contenedor:
podman container exists immich-recovery-da28cf68e463 devolvió1, ausencia verificada
por lectura independiente. No hay kernel-proof.json y no se alcanzó SELECT1 ni
restore del dump ni ningún SQL. No límites kernel/UID/GID efectivos que acreditar
porque nunca existió el objeto.
Resultado privado600 en work/result.json: statusfailed, restore_completefalse,
stoppedfalse, cleanup_errors=[container_state_unavailable]. Este código describe
que el inspect no encontró objeto; no se presenta como contenedor parado.
La lectura independiente exists1 acredita ausencia y explica ese diagnóstico.
Ownership token
Work conserva input copiado privado400, pgdata nueva, restore.log y result.json; inputs0055/0105 y proof029f… se conservan íntegros. No reintento automático ni relajación de límites. El coordinador prepara corrección mínima del formato tmpfs Podman; fuente revisada/publicada y nueva autorización serán necesarias para otro work. Imagen/dump siguen acreditados como inputs, no como restauración DB lograda.
Ensayo DB-only 2: aislamiento acreditado, bootstrap detenido por socket¶
Tras publicación765dad4 y autorización explícita, una única ejecución nueva usó
code0125 privado con runnerc52391e0 y podman-pland07b5f92; guard/baseplan sin cambios.
Work hermano nuevo /var/home/monxas/.local/state/immich-recovery-db-20261020-0125.
No descargas/load ni cambios en0120/0055/0105. Preflight: MemAvailable25.989.709.824,
SwapFree16.771.444.736, disco276.134.608.896, inputs601.722.880 bytes, sin blockers.
El gate efectivo acreditó /bin/sh y argv exacto, control bind RO separado de
PGDATA vacío. kernel-proof.json0600/fsync precedió cualquier initdb/SQL:
imagen769aa… exacta, rednone, rootfsRO, User999:999, UID/GID999→1000,
memory.max1073741824, swap.max0, cpu.max50000/100000 y pids.max128.
Sólo después se publicó start0400/fsync y arrancó el entrypoint original.
Bootstrap falló antes del stream del dump: el PostgreSQL temporal escuchaba en
/tmp, mientras psql interno del entrypoint buscó /var/run/postgresql.
Contenedor propio immich-recovery-6921df27d3e7 salió2, OOMfalse,
01:25:24.855–01:25:29.649+02. El runner terminóexit1 tras fallar la espera init;
result statusfailed, stoppedtrue, cleanup_errors=[], sin conteos restaurados.
No se confunde SQL de bootstrap tras proof con importación del dump, que no comenzó.
Log propio bootstrap-container.log600/fsync preservado en work0125:2580bytes,
SH
Kernelproof0125:980bytes, UID1000/mode0600,
SHA256<REDACTED>.
Lectura posterior del entrypoint exacto desde el contenedor propio detenido:
14179bytes, copia privada600/fsync en work0125/docker-entrypoint.original.sh,
SHA256<REDACTED>.
Confirma que docker_process_sql limpia PGHOST/PGHOSTADDR: la propuesta envPGHOST
por sí sola queda descartada. Ajuste fuente propuesto por Root: socketserver en
/tmp,/var/run/postgresql, ambos tmpfs ya revisados, preservando runner-h/tmp.
Pendiente revisión/publicación y nueva autorización; ningún restart/exec/SQL.
Ensayo DB-only 3: restauración PostgreSQL verificada¶
Tras publicar5bbc06c y revisión Root+maint18+17tests, una única ejecución nueva usó code/work0132: runnerc52391e0, guard5050ccad, baseplanc65ed776 y podman-pland07b5f92. Se reutilizaron imagen exacta769aa… y dump0055SHA6c3b…, sin fetch/load ni cambios sobre intentos0120/0125 o inputs0055/0105. Ambos sockets privados permitieron el bootstrap original. Gate argv/controlRO/PGDATA vacío y proof kernel duradero precedieron cualquier initdb/SQL:1GiB,swap0,.5CPU,pids128,rednone,rootfsRO, User999:999 y mappings UID/GID999→1000. database_restored=true.
Runnerexit0/statusdb_restored, conteos exactos del dump19: assets31595, photos10803, pics20792, content_sha110803, path_sha120792. Contenedor propio immich-recovery-c920ccf0f843: stoppedtrue, cleanup_errors=[], lectura independiente runningfalse/exit0/OOMfalse y ownershiplabel exacto. Postflight sin blockers: MemAvailable25.805.119.488, SwapFree16.771.444.736 sin cambio, disco274.693.378.048, inputs+work1.943.101.440bytes. Los anteriores workspaces se preservan también en disco y quedan cubiertos por reserva global24GiB.
Evidencia UID1000/mode0600 bajo
/var/home/monxas/.local/state/immich-recovery-db-20261020-0132:
- kernel-proof.json1000B SHA256
<REDACTED>. - result.json872B SHA256
<REDACTED>. - restore.log9871B SHA256
<REDACTED>.
El campo global restore_complete=false del runner se mantiene: este resultado acredita sólo la restauración de base de datos, no la aplicación Immich completa. originals_verified=false y coherence_verified=false; no originales montados, app/Redis/ML/GPU ni red. Los29assets posteriores al dump siguen siendo desfaseRPO, no pérdida acreditada. No se elimina ningún workspace/contenedor/imagen automáticamente. Rollback selectivo posterior sólo debe retirar artefactos propios con label/identidad revalidados y procesos parados, preservando dump/proofs/logs; nunca prune global.
Productor natural20: lectura01:31, pendiente de finalizar¶
Después del DBdrill, UNA lectura directa208 a01:31:16+02 (sin proxy, confianzaSSH existente) encontró dump20final ausente, un temporal agregado58.982.400bytes y productor pg-dumps.sh PIDs87308/87312. RunWisp systemunit active/running, Usermonxas/MainPID830. Evidencia local privada metadata-only; no contenido, hash, export, HTTP ni tarea manual. Estado PENDING, sin retryloop ni activación del flag publisher. Publicación diaria seguirá OFF hasta validar productor natural final/frescura y revisiónCAS del coordinador.
Manifiesto privado de referencias y productor natural20¶
Assembler fuente publicada e5ce2e7, revisión16tests sintéticos: primera lectura falló cerrada sin output por una línea ajena aasset>1MiB. Diagnóstico acotado confirmó in_assetfalse/is_COPYprefixfalse; corrección revisada descarta únicamente esas líneas por chunks1MiB+1, manteniendo SHA/CRC completos,8GiB/300s y límites estrictos sobre asset/COPY/EOF. No se ampliaron límites ni se leyeron fotos.
UNA ejecución corregida local produjo NUEVO manifiesto privado0600 en
~/.local/state/immich-recovery-references/20261020-0144/references.ndjson
(parent0700), sin alterar dump0055. DumpSHA6c3b…/CRC/identidad antes-después PASS.
Referencias31595 (photos10803,pics20792), únicasU31595, directoriosD10266;
operaciones metadata estimadas4D+U+64=72723≤80000. NDJSON4219168B,
SHA256<REDACTED>;
bodySHA256<REDACTED>.
Se descartó1línea ajena de5047430bytes. Rutas/filenames sólo dentro del artefacto
privado; salidas e informe contienen agregados. NAS filesystem coverage pendiente.
Checker fuent
Segunda lectura metadata natural autorizada01:41:05+02: dump20final existe, regularUID1000/mode0600/nlink1,183629054bytes, mtime01:31:41.053609+02; temporales0, productorPIDs[], RunWispactive/runningmonxasPID830. Sin contenido/hash/export/trigger/retryloop. Productor natural final/frescura metadata PASS, sin acreditar aún CRC del dump20 ni su copia externa. Publisher no se activa por este agente y conserva condición de revisiónCAS del coordinador.
Cobertura metadata NAS31595: PASS y publicación automática habilitada¶
Tras publicar8d88baf y reviewsRoot+maint, UNA ejecución del bridge96464a0 con checker e6c2d7 y manifiesto0144pinned terminóexit0/statusmetadata_match. ASKPASSforce autenticó UID1026 directo, sin sudo ni ficheros remotos nuevos; stdinPIPE exacto/EOF separado de autenticación. Shimlocal700 sin secreto/PYc eliminado al salir; recibo local privado0600/fsync. No fotos abiertas ni NASwrites.
coverage_complete=true, processed_references31595/pending0/unique31595, root_changed=false tras reanchor final. Photos10803 y pics20792: todos archivos regulares, no vacíos y propietario esperado. Missing/symlink/unexpected_type/ unexpected_uid/empty_file/unavailable=0 en ambos grupos. 72461 operaciones metadata≤80000, ritmo≤100/s, peak_owned_fds8≤60; 900s NAS/1000s transporte. Manifiesto3bffd1ff…/bodyb409bb2e…/dump6c3b… idénticos. No hubo retry ni relajación de límites. content_verified=false y coherence_verified=false: existencia/tipo/propiedad no prueban todos los bytes ni restauración completa Immich.
Recibo local1340bytes bajo
~/.local/state/immich-recovery-references/20261020-check0149/receipt.json,
SHA256<REDACTED>.
Candidatos de originales, todavía sin leer sus archivos¶
Una ejecución local sourcecb72a5f releyó exclusivamente dump0055SHA/CRC/identidad
PASS y produjo manifest idéntico más candidatos privados en NUEVOstage0158:
library0/upload100, JSON0600 SHA256
<REDACTED>.
Contienen sólo rutas upload y checksums SHA1 del dump para futura selección;
no fotos leídas/copias/stat adicionales ejecutados. Las muestras históricas3encoded-video
siguen acreditando derivados; no se sustituyen por originales sin ensayo de bytes.
Estado actual de la publicación diaria¶
El coordinador habilitó el flag en fuente e4d4cfb y runtime RunWisp por CAS:
configSHA256<REDACTED>,
backend/helper hashes intactos,19jobs/otros18intactos, reloadrc0.
RPC01:48:59+02 healthytrue/schedulingtrue/config_stalefalse/TZEuropeMadrid;
originals_backup siguiente11:10. Último estado613875… intacto y rollback privado
~/runwisp/rollbacks/dump-publication-q8_uhj04. Esto reemplaza el estadoOFF previo
sin reescribir su historia. Primera publicación automática PENDING11:10,
sin job manual; las5métricas aún reflejan último enabled0 hasta completar la tarea.
Tres originales upload recuperados: PASS02:05CEST¶
Fuentes336d44f, revisión32tests+integración y nativecompileNASPython2.7.18/euid1026 antes de leer originales. UNAejecución real sesión76249 terminóexit0: samples_verified3 / sample_bytes6098264 / sample_content_verifiedtrue. OrigenDBdump19SHA6c3… + NASphotosgeneración20261019/pool100uploadSHA80bf…; SHA1 del dump verificado en origen y en tres relecturaslocales, más SHA256 remoto/local. No son las muestras encoded-video históricas: éstos son tres originalesupload.
Nuevo stage local privado700
~/.local/state/immich-recovery-references/20261020-samples0210:
archive.tar/tresanónimos/proof/receipt600;0210identifica stage, hora real02:05.
ReceiptSHA256<REDACTED>.
Máximo16MiBporarchivo/48MiBtotal,4MiB/s combinado,120sNAS/180sparent,
FDnofollow/pins/reanchor; TARUSTARREG-only verificado y extracciónmanual exclusiva,
sinNASwrites ni cambios de producción. Artefactos y pruebas previas se conservan.
originals_verified=false y coherence_verified=false: resultado sólo de3muestras, no todos los bytes ni restauración completa de Immich. DB-only31.595y cobertura metadata31.595yaPASS son evidencias separadas. Publicación diaria habilitada, primera automática11:10aún pendiente. Detalles y fuentes en informe de originales.
Raíz de entrada contabilizada y compatibilidad de limpieza: 20 octubre, 19:16 CEST¶
La auditoría local encontró que guard.observe omitía raíces extra inexistentes:
un nombre equivocado se contaba como cero bytes y podía dejar pasar la guardia
de recursos. El runner rootless exigía declarar input-root, pero no acreditaba
que el dump perteneciera a ese árbol. Se reprodujeron ambos casos con temporales,
sin motor o datos reales.
PR81 exige raíz existente y
recorrido de componentes de directorio no-follow, dump regular dentro de esa
raíz y rechazo de .. antes de normalizar. Esto evita validar la ruta
normalizada y copiar una distinta a través de un symlink seguido de ...
Los rechazos preceden a observación de recursos, comprobación de imagen y
creación de workspace. Se mantiene rechazo de solapamiento work/input-root;
el guard ya no considera ausencias como cero. No es aislamiento atómico ante
cambios concurrentes posteriores ni una reescritura del recorrido recursivo.
La primera CI Linux pasó Python3.12 y alertas, pero Python3.10 descubrió un
fallo previo de limpieza: add_note no existe en esa versión y podía ocultar
la excepción original. La corrección conserva el mismo objeto/tipo de error,
retiene notas y emite un aviso fijo de limpieza no confirmada sin mensajes
privados. La regresión simula esa ausencia también en Python reciente y verifica
que se intentan todos los hijos y se restauran señales/máscara.
La revisión independiente y la repetición local pasan 24 pruebas de recuperación
y18 de ciclo de vida. Las dos suites se añaden al workflow Linux existente,
con triggers para los módulos Immich importados; no se añade otro job o daemon.
La CI corregida39464581944
pasa Python3.10/3.12 y contratos/alertas PBS. PR81 quedó fusionada a las
19:17:43 CEST en <REDACTED>.
La CI posterior39464818300
también terminó correctamente. No se ejecutaron motores, SSH,
NAS, dumps reales, restauraciones o instalación runtime para estas pruebas.
La restauración completa sigue pendiente.