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 1181Shell 82Rust 62Markdown 521515 Sutichaykuna

granja: ciclo de vida EFÍMERO por hcloud API (farm-up/farm-down) + baseline €0

Migración del worker pet-24/7 a workers efímeros on-demand. farm1 (ccx23 persistente que
idleaba quemando plata) → snapshot golden 'hammer-golden-2026-07-05' (img 405120842: toolchain
+ store sellado horneados) → destruido. Baseline ahora 0 cajas / €0.

- farm-up.sh [N]: crea N workers desde el snapshot (cache-hit instantáneo del catálogo baked),
  los alinea con la cola actual del laptop, registra la flota en scripts/farm/.fleet (gitignored).
- farm-down.sh [name...]: cosecha el store (CAS, merge seguro) al laptop y DESTRUYE; promueve+firma.
  Orden seguro: sólo destruye si el pull de store salió bien.
- Validado punta a punta: up 1 → boot+cache+toolchain OK → down (cosecha+destruye) OK.
- Cron laptop harvest-go contra farm1 removido (la cosecha ahora la hace farm-down).

El worker sigue sin secretos (hub-and-spoke): compute puro y descartable.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

5658e9337ca9b937ff158b0649ad55a77f4de427 · ruwaq sergio · (3 killa ñawpaq) · tayta c516f9a5e · kay commitpi sach'ata qhaway

Semántico sayk'uy

Mana ima sutichaypas t'ikrakurqanchu: qillqalla, yachachiylla utaq willaylla t'ikrakurqan.

3 qillqakuna · +113 −0

diff --git a/.gitignore b/.gitignore
index da572db0..f729e667 100644
--- a/.gitignore
+++ b/.gitignore
@@ -24,3 +24,4 @@ __pycache__/
 
 # Scratch para bootstrap del frente rust (mrustc clone + fuentes rustc, multi-GB)
 .scratch/
+scripts/farm/.fleet
diff --git a/scripts/farm/farm-down.sh b/scripts/farm/farm-down.sh
new file mode 100755
index 00000000..6ba0d013
--- /dev/null
+++ b/scripts/farm/farm-down.sh
@@ -0,0 +1,52 @@
+#!/usr/bin/env bash
+# farm-down.sh [name...] — COSECHA el store sellado de cada worker efímero al laptop y luego lo
+# DESTRUYE. Sin args opera sobre toda la flota registrada en scripts/farm/.fleet. Con args, sólo
+# sobre los workers nombrados (para bajar la escala parcialmente).
+#
+# El orden importa: primero rsync-eamos el store (CAS, merge seguro con el del laptop), y SÓLO si
+# el pull salió bien destruimos el server. Tras destruir todo, promovemos+firmamos lo cosechado
+# (cache-hit en los artefactos recién bajados) — el store del laptop es la fuente canónica.
+#
+# Uso:   scripts/farm/farm-down.sh            # cosecha + destruye toda la flota
+#        scripts/farm/farm-down.sh hworker-2  # sólo ese
+# Env:   SSH_KEY (def ~/.ssh/github5), REMOTE (def /opt/hammer), NO_PROMOTE=1 (sólo cosecha+destruye)
+set -uo pipefail
+
+SSH_KEY="${SSH_KEY:-$HOME/.ssh/github5}"
+REMOTE="${REMOTE:-/opt/hammer}"
+ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
+FLEET="$ROOT/scripts/farm/.fleet"
+SSH="ssh -i $SSH_KEY -o StrictHostKeyChecking=accept-new -o ConnectTimeout=10"
+cd "$ROOT"
+[ -s "$FLEET" ] || { echo "flota vacía (scripts/farm/.fleet)"; exit 0; }
+
+want=" $* "   # si hay args, filtrar por nombre; si no, todos
+harvested=0
+newfleet="$(mktemp)"
+
+while read -r name ip; do
+  [ -n "${name:-}" ] || continue
+  if [ "$#" -gt 0 ] && [ "${want#* $name }" = "$want" ]; then
+    echo "$name $ip" >> "$newfleet"; continue   # no seleccionado: sigue vivo
+  fi
+  echo "==> cosechando store de $name ($ip)"
+  if rsync -az -e "$SSH" "root@$ip:$REMOTE/store/" "$ROOT/store/"; then
+    echo "==> destruyendo $name"
+    if hcloud server delete "$name" >/dev/null 2>&1; then
+      harvested=$((harvested + 1))
+    else
+      echo "  ⚠ no pude destruir $name — lo dejo en la flota"; echo "$name $ip" >> "$newfleet"
+    fi
+  else
+    echo "  ⚠ falló el pull de store de $name — NO lo destruyo (queda en la flota)"
+    echo "$name $ip" >> "$newfleet"
+  fi
+done < "$FLEET"
+
+mv "$newfleet" "$FLEET"
+echo "==> cosechados+destruidos: $harvested; flota restante:"; cat "$FLEET"
+
+if [ "${NO_PROMOTE:-}" != "1" ] && [ "$harvested" -gt 0 ]; then
+  echo "==> promote+firma final (cache-hit en lo cosechado)"
+  scripts/build-farm.sh || echo "  (build-farm devolvió error; revisá logs)"
+fi
diff --git a/scripts/farm/farm-up.sh b/scripts/farm/farm-up.sh
new file mode 100755
index 00000000..5d3f3244
--- /dev/null
+++ b/scripts/farm/farm-up.sh
@@ -0,0 +1,60 @@
+#!/usr/bin/env bash
+# farm-up.sh [N] — levanta N workers EFÍMEROS desde el snapshot golden y los alinea con la cola
+# actual del laptop. Modelo hub-and-spoke: el worker NO tiene secretos (ni firma ni gitea); es
+# compute puro y descartable. La imagen 'hammer-golden' trae el toolchain + el store sellado
+# horneados ⇒ el worker arranca con TODO el catálogo cacheado (cache-hit instantáneo), sólo
+# construye lo NUEVO que el hub le empuje.
+#
+# Flujo:  scripts/farm/farm-up.sh 3      # crea hworker-1..3, los alimenta con la cola actual
+#         ... (muelen) ...
+#         scripts/farm/farm-down.sh      # cosecha su store al laptop y los DESTRUYE
+#
+# La flota viva se registra en scripts/farm/.fleet ("<name> <ip>" por línea; gitignored).
+# Idempotente al escalar: re-correr con más N agrega workers sin pisar los vivos.
+#
+# Env: IMAGE (snapshot, def 405120842=hammer-golden-2026-07-05), TYPE (def ccx23), LOCATION
+#      (def hel1), SSHKEY (llave del proyecto Hetzner, def desarrollo@jlsoltech.com), SSH_KEY
+#      (privada local p/ SSH, def ~/.ssh/github5), REMOTE (def /opt/hammer).
+set -euo pipefail
+
+N="${1:-1}"
+IMAGE="${IMAGE:-405120842}"
+TYPE="${TYPE:-ccx23}"
+LOCATION="${LOCATION:-hel1}"
+SSHKEY="${SSHKEY:-desarrollo@jlsoltech.com}"
+SSH_KEY="${SSH_KEY:-$HOME/.ssh/github5}"
+REMOTE="${REMOTE:-/opt/hammer}"
+ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
+FLEET="$ROOT/scripts/farm/.fleet"
+SSH="ssh -i $SSH_KEY -o StrictHostKeyChecking=accept-new -o ConnectTimeout=10"
+cd "$ROOT"
+touch "$FLEET"
+
+# índice de arranque: el mayor hworker-N vivo + 1 (para escalar sin colisión de nombres)
+base=$(sed -n 's/^hworker-\([0-9]*\) .*/\1/p' "$FLEET" | sort -n | tail -1)
+base="${base:-0}"
+
+for k in $(seq 1 "$N"); do
+  i=$((base + k)); name="hworker-$i"
+  echo "==> creando $name ($TYPE @ $LOCATION desde image $IMAGE)"
+  hcloud server create --name "$name" --image "$IMAGE" --type "$TYPE" \
+    --location "$LOCATION" --ssh-key "$SSHKEY" --label role=hammer-worker >/dev/null
+  ip=$(hcloud server ip "$name")
+  echo "$name $ip" >> "$FLEET"
+  echo "   ip=$ip; esperando SSH…"
+  until $SSH root@"$ip" true 2>/dev/null; do sleep 3; done
+
+  echo "   alineando cola con el laptop (rsync código + recipes)…"
+  rsync -az --delete -e "$SSH" \
+    --exclude /work --exclude /store --exclude '/store-*' --exclude /target \
+    --exclude /dist --exclude /.dev-fs --exclude /.git --exclude /.scratch \
+    --exclude '*.png' --exclude '/content*' \
+    ./ "root@$ip:$REMOTE/"
+  # el servicio ya arrancó en el boot (baked/enabled); reiniciar fuerza rescan inmediato de la cola
+  $SSH root@"$ip" 'systemctl restart hammer-farm' 2>/dev/null || true
+  echo "   ✓ $name muele la cola actual"
+done
+
+sort -u -o "$FLEET" "$FLEET"
+echo "==> flota viva:"; cat "$FLEET"
+echo "cosechar+destruir con: scripts/farm/farm-down.sh"