gioser

sergio/takana

> Una distribución Linux construida con un laboratorio funcional y hermético en el > sótano, y una terminal mutable, clásica y anárquica en el piso de arriba — diseñada > desde el suelo para que una IA programadora entienda, modifique y co…

TOML 1569Shell 203Rust 109Markdown 993728 Definiciones

buzón verificado en un worker real — y las dos veces que la prueba mintió

Cadena completa medida hoy: el worker depositó 63 artefactos (1,34 GB) en
8 s a 153 MB/s, y el hub los promovió con mv. Respaldo 1542 → 1605, buzón
en 0, guardián cuadrando. Contra los 440 kB/s del laptop, 348×.

Dos fallos de la prueba, que importan más que el resultado:

1. `ssh` cortaba con «Host key verification failed» porque la granja REUSA
   IPs y la host key cambia con razón. Como el stderr iba a /dev/null, el
   error salía como «no obtuve la pública del worker»: culpaba al worker
   cuando el problema estaba en el known_hosts del laptop. Ahora va por
   ssh_worker(), que purga la entrada vieja — correcto sólo acá, porque la
   identidad del worker es su label hcloud, no su llave.

2. verificar-buzon daba «aislado ✓» sin haber probado que escribe. El
   aviso de host key se comía el head -1, y el testigo se llamaba .btest,
   que `ls` no muestra. Un testigo invisible no prueba nada. Ahora exige
   las dos mitades y las dos están verdes: escribe en su buzón, y
   ../hammer/store no existe para él.

Y el progreso de rsync sólo con TTY: sin terminal reescribe con \r y deja
miles de copias de la misma línea. 8 segundos bastaron para un log
ilegible; en el latido sería cada media hora.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

ddc8561e07b7f1881ba804998196a1f3a5a5f471 · por sergio · (hace 2 meses) · padre 400f7171e · ver el árbol en este commit

Delta semántico

Ninguna definición cambió: el cambio es de texto, documentación o datos.

2 archivos · +47 −17

diff --git a/scripts/farm/respaldo-subcuenta.sh b/scripts/farm/respaldo-subcuenta.sh
index 9eb6f178..8a7be7a2 100755
--- a/scripts/farm/respaldo-subcuenta.sh
+++ b/scripts/farm/respaldo-subcuenta.sh
@@ -71,6 +71,20 @@ BUZ_USER="${BUZ_USER:-u647150-sub2}"
 KEY="${KEY:-$HOME/.ssh/github5}"
 CRED="$HOME/.config/hammer/storagebox-worker.env"
 
+# ── LA GRANJA REUSA IPs, ASÍ QUE LA HOST KEY CAMBIA CON RAZÓN ──────────────────────────────────
+# Los workers son EFÍMEROS y Hetzner recicla las direcciones: una IP que ayer fue `hworker-1` hoy es
+# otra máquina distinta con otra llave. Sin esto, `ssh` corta con «Host key verification failed» —
+# y como estas funciones mandaban stderr a /dev/null, el fallo salía como «no obtuve la pública del
+# worker», que apunta al worker cuando el problema estaba en el known_hosts del laptop. Un mensaje
+# que culpa al sitio equivocado cuesta más que no tener mensaje.
+# Purgar la entrada vieja es correcto ACÁ y sólo acá: la identidad del worker no es su llave, es el
+# label `role=hammer-worker` del proyecto hcloud; el secreto no viaja por este canal.
+ssh_worker() {  # $1 = ip, resto = orden remota
+  ip="$1"; shift
+  ssh-keygen -R "$ip" >/dev/null 2>&1
+  ssh -i "$KEY" -o StrictHostKeyChecking=accept-new -o ConnectTimeout=20 "root@$ip" "$@"
+}
+
 case "${1:-}" in
 crear)
   # La contraseña la exige la API aunque después se use llave. Debe traer mayúscula, minúscula,
@@ -85,9 +99,9 @@ crear)
   ;;
 autorizar)
   IP="${2:?falta la IP del worker}"
-  PUB=$(ssh -i "$KEY" "root@$IP" 'mkdir -p /root/.ssh
+  PUB=$(ssh_worker "$IP" 'mkdir -p /root/.ssh
     [ -f /root/.ssh/sb_sub ] || ssh-keygen -q -t ed25519 -N "" -C "hworker→respaldo(ro)" -f /root/.ssh/sb_sub
-    cat /root/.ssh/sb_sub.pub' 2>/dev/null | tail -1)
+    cat /root/.ssh/sb_sub.pub' | tail -1)
   [ -z "$PUB" ] && { echo "!! no obtuve la pública del worker"; exit 1; }
   T=$(mktemp); printf '%s\n' "$PUB" > "$T"
   ssh -4 -p 23 -i "$KEY" "$MAIN" 'mkdir -p hammer/.ssh' >/dev/null 2>&1
@@ -97,7 +111,7 @@ autorizar)
   ;;
 verificar)
   IP="${2:?falta la IP del worker}"
-  ssh -i "$KEY" "root@$IP" "SB=\"ssh -4 -p 23 -i /root/.ssh/sb_sub -o StrictHostKeyChecking=no $SUB_USER@$SUB_HOST\"
+  ssh_worker "$IP" "SB=\"ssh -4 -p 23 -i /root/.ssh/sb_sub -o StrictHostKeyChecking=no $SUB_USER@$SUB_HOST\"
     lee=\$(\$SB 'ls' 2>/dev/null | tr '\n' ' ')
     borra=\$(\$SB 'rm -rf estado' 2>&1 | head -1)
     echo hola > /tmp/.wtest; esc=\$(scp -4 -P 23 -i /root/.ssh/sb_sub -o StrictHostKeyChecking=no /tmp/.wtest $SUB_USER@$SUB_HOST:.wtest 2>&1 | head -1)
@@ -113,9 +127,9 @@ autorizar-buzon)
   IP="${2:?falta la IP del worker}"
   # La MISMA llave del worker sirve para las dos subcuentas: son dos autorizaciones distintas del
   # box, no dos identidades del worker. Se genera allá y nunca viaja una privada desde el laptop.
-  PUB=$(ssh -i "$KEY" "root@$IP" 'mkdir -p /root/.ssh
+  PUB=$(ssh_worker "$IP" 'mkdir -p /root/.ssh
     [ -f /root/.ssh/sb_sub ] || ssh-keygen -q -t ed25519 -N "" -C "hworker→respaldo" -f /root/.ssh/sb_sub
-    cat /root/.ssh/sb_sub.pub' 2>/dev/null | tail -1)
+    cat /root/.ssh/sb_sub.pub' | tail -1)
   [ -z "$PUB" ] && { echo "!! no obtuve la pública del worker"; exit 1; }
   T=$(mktemp); printf '%s\n' "$PUB" > "$T"
   ssh -4 -p 23 -i "$KEY" "$MAIN" 'mkdir -p incoming/.ssh' >/dev/null 2>&1
@@ -129,18 +143,29 @@ verificar-buzon)
   # La verificación tiene DOS mitades y las dos son necesarias: que SÍ escriba (si no, el respaldo
   # no se alimenta y nos enteramos el día que haga falta) y que NO alcance el respaldo consolidado
   # (si lo alcanza, el aislamiento es decorativo). Una sola mitad no prueba nada útil.
-  ssh -i "$KEY" "root@$IP" "SB=\"ssh -4 -p 23 -i /root/.ssh/sb_sub -o StrictHostKeyChecking=no $BUZ_USER@$BUZ_HOST\"
-    echo hola > /tmp/.btest
-    esc=\$(scp -4 -P 23 -i /root/.ssh/sb_sub -o StrictHostKeyChecking=no /tmp/.btest $BUZ_USER@$BUZ_HOST:store/.btest 2>&1 | head -1)
-    ve=\$(\$SB 'ls store' 2>&1 | head -3 | tr '\n' ' ')
-    fuera=\$(\$SB 'ls ../hammer/store' 2>&1 | head -1)
-    \$SB 'rm -f store/.btest' >/dev/null 2>&1
-    echo \"escribir en buzón : \${esc:-(sin error = OK)}\"
-    echo \"ve su buzón       : \$ve\"
+  # ⚠ DOS DETALLES QUE HICIERON QUE ESTA PRUEBA MINTIERA LA PRIMERA VEZ (2026-08-09):
+  #  1. el «Warning: Permanently added …» del host key sale por stderr y se comía el `head -1`, así
+  #     que el resultado de escribir era el aviso y no el resultado. Se filtra explícitamente.
+  #  2. el testigo se llamaba `.btest` y `ls` NO muestra los ficheros con punto ⇒ el listado salía
+  #     vacío tanto si escribió como si no. Un testigo que no se puede ver no prueba nada.
+  ssh_worker "$IP" "SB=\"ssh -4 -p 23 -i /root/.ssh/sb_sub -o StrictHostKeyChecking=no $BUZ_USER@$BUZ_HOST\"
+    limpia() { grep -v 'Warning: Permanently added' | head -1; }
+    echo testigo > /tmp/testigo-buzon.txt
+    esc=\$(scp -4 -P 23 -i /root/.ssh/sb_sub -o StrictHostKeyChecking=no /tmp/testigo-buzon.txt $BUZ_USER@$BUZ_HOST:store/testigo-buzon.txt 2>&1 | limpia)
+    ve=\$(\$SB 'ls store' 2>/dev/null | grep -c testigo-buzon)
+    fuera=\$(\$SB 'ls ../hammer/store' 2>&1 | limpia)
+    borra=\$(\$SB 'rm -f store/testigo-buzon.txt' 2>&1 | limpia)
+    queda=\$(\$SB 'ls store' 2>/dev/null | grep -c testigo-buzon)
+    echo \"escribir en buzón : \${esc:-(sin error)}\"
+    echo \"testigo visible   : \$ve  (1 = escribió de verdad)\"
+    echo \"limpia lo suyo    : \${borra:-(sin error)} → queda \$queda\"
     echo \"alcanza respaldo  : \$fuera\"
-    case \"\$fuera\" in *[Nn]o\ such*|*denied*|*[Ff]ailure*|'')
-        echo 'VEREDICTO: buzón aislado ✓' ;;
-      *) echo 'VEREDICTO: ⚠ ALCANZA EL RESPALDO — no usar'; exit 1 ;; esac"
+    ok=1
+    [ \"\$ve\" = 1 ] || { echo 'VEREDICTO: ⚠ NO ESCRIBE — el respaldo no se alimentaría'; ok=0; }
+    case \"\$fuera\" in *[Nn]o\ such*|*denied*|*[Ff]ailure*) ;;
+      *) echo 'VEREDICTO: ⚠ ALCANZA EL RESPALDO — no usar'; ok=0 ;; esac
+    [ \"\$ok\" = 1 ] && echo 'VEREDICTO: escribe en su buzón ✓ · no alcanza el respaldo ✓'
+    [ \"\$ok\" = 1 ] || exit 1"
   ;;
 *) sed -n '/^# Uso:/,/verificar-buzon/p' "$0"; exit 2 ;;
 esac
diff --git a/scripts/farm/worker-depositar.sh b/scripts/farm/worker-depositar.sh
index d67fafcd..52da2688 100755
--- a/scripts/farm/worker-depositar.sh
+++ b/scripts/farm/worker-depositar.sh
@@ -41,7 +41,12 @@ echo "==> store $N_L · respaldo $N_R  ⇒  a depositar $N_N"
 [ "$N_N" -eq 0 ] && { echo "    nada nuevo"; exit 0; }
 
 # `--files-from` con los nombres a secas (son directorios del store) + `-r` para bajar en cada uno.
-rsync -aHr --files-from="$TMP/nuevos.txt" --partial-dir=.rsync-partial --info=progress2 \
+# `--info=progress2` SÓLO con terminal: sin TTY reescribe la misma línea con \r y, como nadie
+# interpreta el retorno de carro, el «progreso» se acumula en miles de copias de la misma línea. Un
+# depósito de 8 segundos dejó así un log ilegible; en el latido, cada media hora, sería peor.
+PROG=""; [ -t 1 ] && PROG="--info=progress2"
+# shellcheck disable=SC2086
+rsync -aHr --files-from="$TMP/nuevos.txt" --partial-dir=.rsync-partial $PROG \
   -e "$SSH_SB" "$STORE/" "$BZ_USER@$BZ_HOST:store/" || { echo "⚠ el depósito falló"; exit 1; }
 
 # Guardián: recontar el buzón. «rsync salió 0» no es «llegaron N».