Commit Graph
15 Commits
Author SHA1 Message Date
SergioandClaude Opus 5 b985570788 respaldo: un parcial de SÓLO LECTURA envenena esa ruta para siempre — y el bucle lo llamaba «red»
La primera corrida completa desde la caja (puerta 7) se cayó 40 veces seguidas con el MISMO fichero:

  open ".../fonts/dejavu/.rsync-partial/DejaVuSans-Oblique.ttf" failed: No such file… (2)
  ..  store: corte de red (rsync 23). Intento 39; reanudo en 60s.
  !!  store: 40 intentos y sigue cayéndose. Dejo lo subido y paro.

No era red. Una transferencia interrumpida el 11-sep dejó ese PARCIAL en el destino con modo
-r--r--r-- (el del origen: el store es de sólo lectura por diseño), y ningún intento posterior puede
reescribirlo. El fallo es ETERNO, y el bucle reintentaba cada 60 s contra esa pared — exactamente lo
que la cabecera de la función de reintento dice que no hay que hacer.

Dos arreglos:

1. `--chmod=Fu+w`: los ficheros del respaldo quedan escribibles por su dueño, así que un corte a
   mitad se RETOMA en vez de trabarse. El precio es no conservar el bit de sólo-lectura en la copia;
   es bajo: el contenido es lo que se respalda y los permisos los reconstruye el store.

2. Al rsync 23 se le dan TRES intentos, no cuarenta, y al tercero se NOMBRA la causa probable (un
   parcial de sólo lectura en el destino) en vez de llamarlo corte de red. El 23 es «some files were
   not transferred», que tanto puede ser un cable como una pared.

Comprobado: borrado el parcial envenenado, ese fichero sube a la primera.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-17 16:55:01 +00:00
SergioandClaude Opus 5 d1d6645bd0 respaldo: una guarda que evita BORRAR el historial de git del Storage Box
Encontrado corriendo el respaldo desde la caja de producción (SDD 28, puerta 7).

El paso [2/3] sube el repo **con `--delete`** —correcto: para eso está git—. Pero el `/opt/takana` de
la caja llegó por `rsync --exclude=.git`: es una COPIA, no un clon. Una corrida real desde ahí no
habría subido menos cosas: **habría borrado del respaldo todo lo que le falta al origen, empezando
por `.git`** — el historial entero. Y en silencio, porque rsync haría exactamente lo que se le pidió.

Ahora falla ruidosamente si la raíz no tiene `.git`, con `REPO_INCOMPLETO=1` como escotilla para el
caso deliberado. Probado en los tres sentidos: en gioser (con `.git`) pasa; en un árbol sin `.git`
aborta; con la escotilla pasa igual.

**Y el store tampoco estaba donde el script creía.** Usaba `$RAIZ/store` cableado — en gioser es un
bind-mount dentro del repo, pero en una caja takana instalada es una partición en `/store`, así que
el paso [3/3] moría con `change_dir "/opt/takana/store" failed`. Peor: rsync devuelve 23, que está en
la lista de reintentables, así que el bucle lo reintentaba — exactamente el cuadro que la cabecera de
este script ya documenta («un error reintentable que se repite 40 veces no es un corte de red: es
algo estructural»). Ahora `STORE` es env y hay una guarda ANTES del bucle que aborta si no existe.

Con eso el `--seco` completa los tres pasos desde la caja y lee la ocupación del box (111 G de 1 T).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RomoxEGZUhaT4pob1QSX5x
2026-09-11 03:04:24 +00:00
SergioandClaude Opus 5 01b1dcfefe respaldo: el rsync del propio corpus no podía correr el respaldo del propio corpus
Corriendo el respaldo desde la caja de producción (SDD 28, puerta 7):

    ==> [1/3] estado (el grafo: qué había construido y con qué hash)
    unknown compress name: zstd
    !!  estado: error 4 que NO es de red — no reintento

El script exige `--compress-choice=zstd` (medido: el doble de rendimiento efectivo, porque el 79 %
del store son secciones `.debug_*` y comprimen como texto) y **`recipes/rsync.toml` lo construía con
`--disable-zstd`**. El comentario de la receta decía por qué: «deps externas quitadas (no en
catálogo)». Ya no es cierto — `zstd` tiene receta y está sellada.

Dos arreglos, y los dos hacen falta:

1. **La receta enciende zstd** (dep `zstd`, fuera el `--disable-zstd`). Control en los dos sentidos:
   el rsync nuevo lista `zstd zlibx zlib none`, el anterior `zlibx zlib none`. Radio cero: `rsync` no
   es dep de ninguna receta (medido), así que no re-hashea nada más.
2. **El script DEGRADA en vez de morir.** Detecta el soporte (`rsync --version` → «Compress list»)
   y cae a zlib avisando. Un respaldo que no corre por un algoritmo de compresión es peor que un
   respaldo lento — y el fallo era especialmente malo porque el error 4 se clasifica como "no de red"
   y el script no reintenta: el respaldo simplemente no se hace.

Y de paso queda cubierto el caso de un hub que todavía no reconstruyó su rsync.

**También la raíz cableada**: el script hacía `cd /mnt/vvv/takana` por defecto —la ruta de gioser—
así que desde cualquier otro hub moría con `No such file or directory`. Ahora se deriva de la
ubicación del script, como el resto de `scripts/`; el override por `RAIZ` se conserva. Control: en
gioser sigue resolviendo a `/mnt/vvv/takana`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RomoxEGZUhaT4pob1QSX5x
2026-09-11 02:59:13 +00:00
Sergio f9ed89cd17 takana: espejo de GitHub renombrado, y las dos colas que quedaban
GitHub: sergiovelasquezzeballos/hammer -> takana (sigue privado). El segundo
pushurl del origin y el default de espejo-setup.sh actualizados; push real
verificado contra los DOS destinos.

Las dos colas las encontro hammer-4a y las verifique antes de aplicarlas:

1) CINCO scripts hardcodeaban /home/sergio/hammer y hoy funcionaban SOLO por el
   symlink que puse al mover. El peor era respaldo-storagebox.sh, cuya RAIZ por
   defecto salia de ahi: si alguien limpia el symlink dando el renombre por
   cerrado, el RESPALDO apunta a una ruta inexistente. Un respaldo que no
   encuentra su raiz no falla ruidosamente — se descubre el dia que lo
   necesitas. Ahora apuntan a /mnt/vvv/takana, la ruta real, sin depender de
   ningun enlace.

2) Cuatro referencias a gitea.gioser.net/sergio/hammer. El diagnostico del peer
   era el correcto y lo comprobe: ese HOST no resuelve, y no por el renombre —
   ya estaba mal antes, el remoto real es git.gioser.net. Cambiar solo
   hammer->takana las habria dejado igual de rotas. Van a
   https://git.gioser.net/sergio/takana, que responde 200.
2026-09-09 20:33:03 +00:00
Sergio 476168bb07 takana etapa 5c: comentarios de scripts, MOTD, y un BUG que introdujo la etapa 4
250 líneas de comentario en 151 scripts. Control verificado: el diff no toca
NI UNA línea que no empiece por #, y la sintaxis de los 151 pasa.

El barrido saltea heredocs y cadenas triples, y el guardián DISPARÓ 3 veces:
las tres eran el MOTD que el script escribe DENTRO de la imagen construida —
texto del producto, no comentario del script. Se cambiaron aparte y a
propósito, que es rebranding, no limpieza.

Y el hallazgo caro:  casaba contra ,
que es el TARGET de tracing — o sea el module_path!, o sea el nombre del crate.
La etapa 4 lo movió a  y el script quedó casando NADA. No fallaba:
imprimía cero atribuciones, indistinguible de un log sin problemas. Comprobado
con el binario (RUST_LOG=info sobre zlib), no deducido. Ahora acepta las dos, y
tiene que seguir aceptándolas porque los logs viejos en disco dicen la vieja.

Además 14 rutas de módulo  en docs, que el barrido anterior no tocó
porque  no es frontera de palabra.
2026-09-09 19:28:48 +00:00
SergioandClaude Opus 5 82f9055670 respaldo: el listado no recorre .dmerge — tardaba tanto que se cortaba
`du -s hammer/store/*` incluia `.dmerge`, la cache transitoria de fusion del
store: 48 GB en el box el 2026-08-12. Recorrerla hacia que --listar tardara
lo bastante como para cortarse por timeout, y entonces el box "devolvia 0
artefactos" — el guardian de los 0 hizo bien su trabajo y se nego a pisar el
manifiesto, pero la causa no era el enlace.

Con `store/[0-9a-f]*` el listado baja de cortarse a 3 s, y de paso descarta
en el origen `.bootstrap-tmp`/`.mirror-tmp`, que nunca son artefactos. Un
artefacto SIEMPRE empieza por hex: filtrar en el origen sale mas barato que
traerse la basura y descartarla despues.

Verificado: 2037 artefactos, 0 vacios.

⚠ Aparte: que .dmerge ESTE en el respaldo es en si un problema — son 48 GB de
cache reconstruible ocupando el box, y el propio script la excluye al subir
(`--exclude /.dmerge`). Llego por otra via. No se toca aqui: borrar en el
destino es una decision deliberada y aparte.

Y una limitacion del guardian de vacios que conviene conocer: durante una
SUBIDA en curso, los directorios a medio escribir son indistinguibles de los
rotos. Reporto 101 vacios a mitad del rsync y 0 al terminar. No es un falso
positivo del filtro: es que la pregunta no tiene respuesta estable mientras
se escribe.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-12 00:31:15 +00:00
SergioandClaude Opus 5 0377f4fa7a guardianes: un artefacto VACIO deja de contar como presente
Dos eslabones de la misma cadena, que el 2026-08-10 dejo cuatro recetas con
OK sin producir un solo fichero.

Store::has era path_of(..).is_dir(): un directorio vacio contaba como
sellado, asi que build() hacia cache-hit y devolvia Ok sin construir. Ahora
exige al menos una entrada. NO exige el sidecar .hammer/recipe.toml aunque
seria mas expresivo: ese lo escriben los llamantes, no seal(), y
hammer-bootstrap sella sin el ⇒ pedirlo lo haria reconstruir siempre. Va con
test de regresion.

--listar armaba el manifiesto con `ls`, que lista NOMBRES: un vacio es
identico a uno bueno, y de ahi build-state.py lo daba por sellado. Ahora usa
`du -s` (8,6 s sobre 1751, frente a un ls instantaneo), separa los vacios a
work/respaldo-vacios.txt y los DICE siempre, tambien cuando son 0.

Cuidado con el orden en ese awk: recortar la ruta antes se come el contador
de bloques y el filtro compara el nombre en vez del tamano — daba 406 vacios
falsos. Primero filtrar por numero, despues recortar.

Quedan 3 vacios sin curar en el respaldo (dbus x2 y un libxkbcommon de hash
superado); no caen en ninguna clausura construida.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-10 13:02:34 +00:00
sergioandClaude Opus 5 7dc6808567 respaldo: el progreso de rsync sólo con terminal — 2,6 MB de log ilegible
Sin TTY, --info=progress2 reescribe con \r y el progreso se acumula en
miles de copias de la misma línea. El respaldo corre DESATENDIDO, así que
ese log es la única forma de saber qué pasó; a 2,6 MB no se lee.

Mismo defecto y mismo arreglo que en worker-depositar.sh.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-10 00:06:45 -04:00
sergioandClaude Opus 5 9388beba49 store en el volumen: «está sellado» dejó de ser «está en este disco»
El store de trabajo se mudó al volumen de la granja y el laptop quedó con
219 artefactos: los de fuente privada (que la granja no puede rehacer) más
lo que todavía no está respaldado. 979 artefactos verificados en el box se
borraron de acá, y con ellos el caché .dmerge de 92 G que sostenía sus
bloques: 128 G → 8,2 G, 115 G libres.

Eso rompía tres cosas que leían el disco local como si fuera la verdad:

- build-state.py habría reportado `never` sobre ~950 sellados y el latido
  lo habría COMMITEADO. Un grafo recién escrito miente con más autoridad
  que uno viejo. Ahora resuelve la presencia contra la unión del store y
  los manifiestos, y expone `sealed_remoto` para que «el store se mudó»
  no se lea nunca como «el corpus creció».
- farm-sync.sh armaba el manifiesto con `ls ./store`, así que le habría
  dicho al worker «el hub tiene 220» y el worker habría rehecho ~950. Es
  el bucle de churn que el propio fichero documenta, al revés. Ahora es la
  unión, con un guardián que aborta si el manifiesto encoge.
- cosecha-cron.sh bajaba el store entero cada 30 min: habría deshecho la
  mudanza sola, como la poda de 24 G que se deshacía en 2026-08-07. Ahora
  baja sólo la lista de nombres.

Los cuatro grafos regenerados dan idéntico a antes del recorte
(766/11/2), que es la prueba de que no se perdió nada.

Queda abierto: los artefactos nuevos viven SÓLO en el volumen hasta que
alguien corra una pasada de respaldo. El volumen tiene borrado protegido,
pero es una copia.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-09 21:22:36 -04:00
sergioandClaude Opus 5 b38db3f35d respaldo: .dmerge tumbó la subida 40 veces seguidas y el mensaje nunca lo dijo
El respaldo se detuvo en 79 G de 127 G con «40 intentos y sigue cayéndose». El código era 23
(«some files/attrs were not transferred»), que está en la lista de reintentables — así que el
bucle lo reintentó cuarenta veces contra la misma pared y se rindió.

LA CAUSA, al mirar los errores de verdad en vez del código de salida: todos eran rutas
`/home/hammer/store/.dmerge/...`. Son directorios TRANSITORIOS de fusión del store, que aparecen
y desaparecen mientras hammer sella. rsync los empieza a copiar, se esfuman a media
transferencia, y falla. `cosecha-cron.sh` ya los excluía; este script no, y ésa era toda la
diferencia.

⇒ Añadido `--exclude /.dmerge`.

Y una lección para el propio bucle de reintentos: **un error reintentable que se repite 40 veces
no es un corte de red, es algo estructural**. Cuarenta reintentos idénticos deberían haber
gritado «esto no se arregla esperando» en vez de agotarse en silencio. Queda anotado; el bucle
todavía no distingue «se cayó una vez» de «falla siempre igual».

De paso, `appstream` —la única de las cinco del split que se perdió al borrarse el worker— se
reconstruyó en el hub: 18 M, 0 secciones .debug_. Las cinco quedan consistentes (hash vigente
con artefacto presente): bison 3M · appstream 18M · zstd 2M · expat 1M · ncurses 2M.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-08 08:41:28 -04:00
sergioandClaude Opus 5 129aff9dd9 granja: el worker rehacía 87 de 90 recetas por ciclo — el sync del store es de UN SOLO SENTIDO
Fui a mirar los fallos de la granja y el número no cuadraba: de 20 «fallos» de incoming-gnome,
17 correspondían a recetas CON ARTEFACTO VIGENTE en el hub. Primero pensé que eran marcadores
`.fail` rancios, y me equivoqué: `build-farm.sh` hace `rm -rf "$FARM"` al empezar cada ciclo,
así que el status es siempre del ciclo actual. Después pensé que el worker corría recetas
viejas, y también me equivoqué: comparados los md5, hub y worker tienen copias IDÉNTICAS.

LA CAUSA REAL está en `farm-sync.sh`: sube el código EXCLUYENDO /store y baja el store del
worker. El store viaja worker→hub y nunca hub→worker. O sea que **el worker no se entera de
nada de lo que se sella en el hub**, y cada ciclo reintenta —y vuelve a fallar— trabajo ya
hecho. Medido: de las 90 recetas de incoming-gnome, **87 ya están selladas en el hub**. El
worker estaba quemando el 97% de esa cola en repetir lo hecho.

EL ARREGLO no es mandarle los artefactos (gigabytes por un enlace de 8 Mbps) sino la LISTA:
`work/farm-sellados.txt` son los nombres `<hash>-<paquete>` del store del hub, unos kilobytes.
`farm-sync.sh` la regenera en cada sync (para que no envejezca sola) y `build-farm.sh` salta
la receta cuyo hash vigente ya esté ahí. Ojo al detalle de rsync: la subida excluye /work, así
que el manifiesto necesita un `--include` ANTES del `--exclude` — rsync aplica la primera regla
que casa, y sin ese orden el fichero no viajaba y el arreglo no habría hecho nada en silencio.

Y LAS TRES DEUDAS REALES DE LA GRANJA SON UNA. gnome-session, gnome-settings-daemon y gdm
mueren todas en GTK3, que el frente GNOME aparcó a propósito. Verificado contra el meson.build
de cada tag: gnome-session 48.0 lo pide incondicional; gnome-settings-daemon 48.1 pide gtk+-3.0
Y gtk+-x11-3.0 (dos de las tres deudas aparcadas, no una); gdm 48.0 lo tiene condicionado a
`if have_xdmcp` pero da igual, porque muere construyendo gnome-session.

De paso, gnome-session pasaba `-Dsystemd=false -Dsystemd_journal=false`, dos opciones que NO
EXISTEN en 48.0 (sus opciones reales son seis: deprecation_flags, session_selector,
systemduserunitdir, docbook, man, x11). Meson aborta en la primera opción desconocida, así que
la receta ni llegaba a configurar y el fallo real quedaba tapado. Corregido, más -Dx11=false.
El diagnóstico de fondo YA ESTABA en la receta desde el 2026-07-27 y era más completo que el
mío (dice GTK3 **y** libsystemd); quité la nota duplicada que había añadido.

Y en el respaldo: la comprobación inicial de SSH era de un solo intento y tiró la corrida al
relanzarlo llegando a casa, con el wifi aún sin levantar. Es el peor momento para rendirse —
quien relanza un respaldo interrumpido acaba de cambiar de red. Ahora reintenta.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 18:27:29 -04:00
sergioandClaude Opus 5 89c02db639 respaldo: rsync 24 (ficheros desaparecidos) no es un fallo — es podar mientras se sube
El bucle de reintentos trataba el 24 como «error que no es de red» y habría PARADO el
respaldo entero. Pero 24 significa que ficheros del origen desaparecieron durante la
transferencia, que en este proyecto es una combinación NORMAL: el respaldo dura ~19 h y
`store-gc.sh` es la válvula del disco, así que podar mientras se sube va a pasar. Y pasaría
justo cuando el disco aprieta, o sea cuando menos conviene quedarse sin copia.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 15:06:26 -04:00
sergioandClaude Opus 5 5e42d201b1 respaldo: reintentar los cortes de red — el primero murió a los 2,3 G
La primera corrida real subió el cerebro (estado + repo, lo irreemplazable) y 2,3 G de
store, y entonces murió: `Connection reset by peer` / `Broken pipe`, rsync 255. Con ~19 h
de subida sobre un enlace de oficina, que la conexión se corte NO es un accidente: es lo
normal. Y un respaldo que hay que relanzar a mano cada vez que se cae no se completa nunca,
porque nadie está mirando.

Como rsync ya es incremental y `--partial-dir` conserva los trozos a medio subir,
reintentar es barato y seguro: cada intento retoma donde quedó, no reempieza. El bucle
insiste sólo ante fallos de RED (10/12/23/30/35/255) con espera creciente hasta 60 s; un
error de verdad —permisos, destino lleno, ruta inexistente— sale a la primera en vez de
repetirse 40 veces contra la misma pared.

DE PASO, UN ERROR MÍO QUE VALE REGISTRAR: comprobé el respaldo con `pgrep -f
respaldo-storagebox` y dijo que corría, cuando llevaba rato muerto — el pgrep se estaba
matcheando A SÍ MISMO, porque la cadena buscada está en la propia línea de comando del
shell que la ejecuta. Ya me había pasado hoy con el worker. La comprobación buena es mirar
lo que el proceso PRODUCE (bytes en el destino, última línea del log), no si hay un proceso
vivo. Es la misma lección que el guardián de store-gc: un `rm` que devuelve 0 no prueba que
el fichero se fue, y un proceso vivo no prueba que esté avanzando.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 13:30:58 -04:00
sergioandClaude Opus 5 346cd59706 licencias: campo license en la receta — de 0 a 228 de 1141, sin re-hashear nada
La deuda legal bloqueante del SDD 19 §2.1. Medido hoy: **0 de 1141 recetas** declaraban
licencia, no «5 de 771» como decía el informe anterior. Los dos números estaban mal: los
«5» eran falsos positivos de `grep license` (el paquete `addlicense`, el paquete
`cargo-bundle-licenses`, una línea `install .../share/licenses/` y un comentario), y las
recetas son 1141. Contar con `grep -l <palabra>` sobre TOML cuenta comentarios y nombres,
no campos; `scripts/licencias.sh` cuenta el campo de verdad (clave en la raíz, antes del
primer `[table]`).

LO QUE HACE LA DEUDA PAGABLE: `Recipe::hash_inputs` es una LISTA BLANCA — sólo entran
source, compiler, target, link, patches, flags, phases y deps. `license` no entra, igual
que `evidence` y `slots`. Por eso se puede poblar en las recetas YA SELLADAS sin mover un
solo ArtifactHash. Verificado, no supuesto: en 40 recetas modificadas se comparó el hash
con y sin la línea — 40 idénticos, 0 cambiados. Si el campo entrara al hash, declarar la
licencia costaría reconstruir el corpus entero y no se haría nunca. Clavado con el test
`licencia_round_trip_y_no_afecta_el_hash`.

TRAMPA DE TOML: una clave suelta después de un `[table]` pertenece a esa tabla. Puesta al
final del fichero, `license` acaba dentro de `[deps]` y se pierde EN SILENCIO, porque serde
ignora los campos que no conoce — no hay error, simplemente no está. Va arriba, junto a
`name` y `version`; el sembrador la inserta tras `version`.

NO SE ADIVINA. Declarar mal una licencia es peor que dejarla vacía: convierte un hueco
visible en una afirmación falsa. Sólo se puebla desde una tabla curada entrada por entrada
(`docs/licencias-conocidas.tsv`); lo que no tiene evidencia queda vacío y se CUENTA.
Concretamente se descartó el atajo «k* = KDE ⇒ LGPL»: en este catálogo `kail`, `kind`,
`ko`, `kopia`, `krew`, `kustomize`, `kyverno`, `katana`, `kibi`, `kmon` y toda la familia
`kube*` son herramientas Go sin relación con KDE. El nombre no es evidencia.

Quedan 913, casi todas CLIs Go/Rust importados en masa — y ésas sí son automatizables con
evidencia real: Cargo.toml trae el campo `license` y los módulos Go traen su LICENSE en el
árbol. El cierre estructural es capturarlo en la fase de fetch, que ya descarga y extrae
cada tarball, y inyectar el texto en `hammer pack` (aguas abajo del ArtifactHash) en vez de
en la fase install (que sí re-hashearía).

De paso, respaldo-storagebox.sh reordenado por valor irreemplazable y con zstd: medido en
la oficina, el uplink da 8 Mbps iguales por cable y por wifi ⇒ 128 G no caben en una
sentada, así que sube primero el cerebro (estado + repo) y `--partial-dir` hace que cortar
a mitad de un artefacto no tire lo ya subido.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 10:24:11 -04:00
sergioandClaude Opus 5 60d8ca9bbf respaldo: Storage Box BX11 en Hetzner — no dependemos más de un laptop
El usuario pidió respaldar en «el volumen, aunque sea montándolo aquí». No se
puede: un HC Volume es un dispositivo de bloque por RED, sólo se adjunta a
servidores de Hetzner del mismo DC, y sólo a uno a la vez. harkaq-cosecha es el
disco del worker efímero y vvv está al 78%.

El producto correcto es un Storage Box: se monta desde el laptop (SSHFS/CIFS) y
habla rsync/borg/restic por SSH. BX11 = 1 TiB, €3,20/mes, sin alta. Creado en
hel1 con protección de borrado y la clave github5.

rsync plano y no borg: el store es CAS, los ficheros son inmutables y se nombran
por hash, así que incremental es exactamente lo correcto y no hay repo ni claves
que mantener. borg comprimiría 4x (79% del store es .debug_) pero 126 G en 1 TiB
no aprieta.

El store va SIN --delete a propósito: un respaldo que replica los borrados no
protege del borrado por error, y store-gc.sh acaba de demostrar que puede
equivocarse en silencio.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 08:10:30 -04:00