1# Granja harkaq con volumen persistente + auto-shutdown
2
3La arquitectura que la 1ª campaña pidió (quedó idle ~4h cobrando). El worker no depende de que yo
4esté viva y no se queda idle. **Validado end-to-end 2026-07-16** (worker se auto-eliminó, volumen
5sobrevivió con las cosechas, helper efímero las recogió).
6
7## Ciclo
8
9```sh
10scripts/farm/harkaq-vol.sh up 2 # 2 workers midiendo → volumen; se auto-eliminan al agotar cola
11# ... (muelen la noche, se destruyen solos) ...
12scripts/farm/harkaq-vol.sh collect # (cuando esté viva) helper efímero baja las cosechas al hub
13scripts/farm/harkaq-vol.sh close # clausura el volumen (deja de cobrar)
14scripts/farm/harkaq-vol.sh status
15```
16
17## Cómo funciona (y por qué así)
18
19- **Volumen `harkaq-cosecha`**: los workers escriben verdicts a `/mnt/cosecha`, que **sobrevive a
20 su destrucción**. No dependo de estar viva para no perder el trabajo.
21- **Auto-shutdown**: tras `IDLE_CICLOS` ciclos con 0 mediciones nuevas, el worker **se auto-elimina
22 vía API REST** (`curl` + metadata service; NO hcloud CLI, que la golden no trae). En Hetzner un
23 server apagado **sigue cobrando** ⇒ apagar de verdad es `delete`.
24- **collect**: monta el volumen en un helper efímero barato (o un worker vivo), rsync al hub, lo
25 destruye. Clasificar los verdicts es un paso aparte (el worker MIDE, el hub CLASIFICA con el
26 store completo).
27
28## Rastrillos que ya se pagaron (todos verificados en vivo)
29
30- **`--exclude /work` es crítico**: `work/` son 69GB; sin excluirlo el rsync del `up` cuelga horas.
31- **Tipo de server**: en hel1 los AMD `cpx*` dan "unsupported/stock"; `ccx13`/`ccx23` sí van.
32- **El token va al worker** (para auto-delete): riesgo acotado — efímero, sin servicios entrantes.
33- **`${1:?...}` sin llaves** en el mensaje: una `}` interna cierra la expansión.
34- El bucle **borra el artefacto `-hkm` tras medir** (envenenaba la caché en la 1ª campaña) y gatea
35 por ABI≥7 + binario-con-jaula (no medir ciego).
36
37## Un volumen se adjunta a UN server (2026-08-22, costó 21 G)
38
39`farm-up.sh N` crea N workers en un bucle y **todos apuntan al mismo `VOL_NAME`**. Como un volumen
40de Hetzner se adjunta a un único server, del segundo en adelante no queda nada que adjuntar. La
41campaña de reconstrucción corrió sharded (`SHARD=i/N`) en dos workers: hworker-1 tomó
42`harkaq-cosecha` y hworker-2 nació sin volumen, construyó el shard 1/2 entero en su disco raíz y el
43dead-man se lo llevó a las 03:27Z. **397 artefactos sellados, 584 sin copia en ningún otro sitio**
44(la lista quedó en `work/perdidos-hworker2.txt`).
45
46Lo caro no fue la falta de detección: `farm-up` **ya imprimía** `⚠ el store NO quedó en el volumen ⇒
47lo que construya se PIERDE`. Lo imprimió esa noche, con esas palabras, y siguió adelante armando el
48dead-man y sembrando la cola. Un aviso que no detiene el pipeline no es un guardián cuando no hay
49nadie leyendo el log.
50
51Desde entonces `farm-up` **corta** (`sin_volumen()`): pregunta antes quién tiene el volumen tomado,
52ya no se traga el error del `attach`, y si el store no queda anclado borra el server recién creado
53(vacío, para no dejar un idle facturando) y sale 1. Para correr sharded hace falta **un volumen por
54worker**:
55
56```sh
57VOL_NAME=harkaq-cosecha-1 scripts/farm/farm-up.sh 1
58VOL_NAME=harkaq-cosecha-2 scripts/farm/farm-up.sh 1
59SIN_VOLUMEN_OK=1 scripts/farm/farm-up.sh 1 # worker deliberadamente desechable
60```
61
62## El latido en gioser va por cron, no por sesión
63
64`latido.sh` cuelga el bucle de **tu sesión de shell**, y eso es lo correcto *en el laptop*: no hay
65systemd y cronie sólo existe en uno de los dos arranques. En gioser la premisa se invierte — es un
66server, no hay sesión permanente, y crond sí corre. El mismo 2026-08-22 el rescate de hworker-2
67dependía de que un agente estuviera despierto mirando, y la respuesta llegó después del auto-borrado.
68
69```crontab
70*/30 * * * * HOME=/home/sergio PATH=/usr/local/bin:/usr/bin:/bin /mnt/vvv/takana/scripts/farm/cosecha-cron.sh >>/mnt/vvv/takana/work/cosecha-cron.log 2>&1
71```
72
73Rutas **absolutas** las dos: cron arranca en `$HOME`, así que una ruta relativa al script no
74resuelve y —peor— la redirección del log se abre en el directorio equivocado. El script hace su
75propio `cd "$ROOT"`, trae su `flock`, tolera workers ausentes y **no baja el store** (sólo el
76manifiesto de nombres) ⇒ no puede llenar el disco. Parar = borrar la línea.
77
78Lo que el latido **no** cubre: los artefactos siguen viviendo sólo en el volumen hasta que alguien
79haga una pasada de respaldo. El volumen tiene borrado protegido, pero es UNA copia.
80
81## Parar de emergencia
82```sh
83hcloud server list | grep hkw # ver workers
84hcloud server delete hkw-N # matar uno
85scripts/farm/harkaq-vol.sh close # y el volumen
86```
harkaq-granja-volumen.md
Granja harkaq con volumen persistente + auto-shutdown
La arquitectura que la 1ª campaña pidió (quedó idle ~4h cobrando). El worker no depende de que yo
esté viva y no se queda idle. Validado end-to-end 2026-07-16 (worker se auto-eliminó, volumen
sobrevivió con las cosechas, helper efímero las recogió).
Ciclo
scripts/farm/harkaq-vol.sh up 2 # 2 workers midiendo → volumen; se auto-eliminan al agotar cola
# ... (muelen la noche, se destruyen solos) ...
scripts/farm/harkaq-vol.sh collect # (cuando esté viva) helper efímero baja las cosechas al hub
scripts/farm/harkaq-vol.sh close # clausura el volumen (deja de cobrar)
scripts/farm/harkaq-vol.sh status
Cómo funciona (y por qué así)
- Volumen
harkaq-cosecha: los workers escriben verdicts a /mnt/cosecha, que sobrevive a
su destrucción. No dependo de estar viva para no perder el trabajo.
- Auto-shutdown: tras
IDLE_CICLOS ciclos con 0 mediciones nuevas, el worker se auto-elimina
vía API REST (curl + metadata service; NO hcloud CLI, que la golden no trae). En Hetzner un
server apagado sigue cobrando ⇒ apagar de verdad es delete.
- collect: monta el volumen en un helper efímero barato (o un worker vivo), rsync al hub, lo
destruye. Clasificar los verdicts es un paso aparte (el worker MIDE, el hub CLASIFICA con el
store completo).
Rastrillos que ya se pagaron (todos verificados en vivo)
--exclude /work es crítico: work/ son 69GB; sin excluirlo el rsync del up cuelga horas.
- Tipo de server: en hel1 los AMD
cpx* dan "unsupported/stock"; ccx13/ccx23 sí van.
- El token va al worker (para auto-delete): riesgo acotado — efímero, sin servicios entrantes.
${1:?...} sin llaves en el mensaje: una } interna cierra la expansión.
- El bucle borra el artefacto
-hkm tras medir (envenenaba la caché en la 1ª campaña) y gatea
por ABI≥7 + binario-con-jaula (no medir ciego).
Un volumen se adjunta a UN server (2026-08-22, costó 21 G)
farm-up.sh N crea N workers en un bucle y todos apuntan al mismo VOL_NAME. Como un volumen
de Hetzner se adjunta a un único server, del segundo en adelante no queda nada que adjuntar. La
campaña de reconstrucción corrió sharded (SHARD=i/N) en dos workers: hworker-1 tomó
harkaq-cosecha y hworker-2 nació sin volumen, construyó el shard 1/2 entero en su disco raíz y el
dead-man se lo llevó a las 03:27Z. 397 artefactos sellados, 584 sin copia en ningún otro sitio
(la lista quedó en work/perdidos-hworker2.txt).
Lo caro no fue la falta de detección: farm-up ya imprimía ⚠ el store NO quedó en el volumen ⇒ lo que construya se PIERDE. Lo imprimió esa noche, con esas palabras, y siguió adelante armando el
dead-man y sembrando la cola. Un aviso que no detiene el pipeline no es un guardián cuando no hay
nadie leyendo el log.
Desde entonces farm-up corta (sin_volumen()): pregunta antes quién tiene el volumen tomado,
ya no se traga el error del attach, y si el store no queda anclado borra el server recién creado
(vacío, para no dejar un idle facturando) y sale 1. Para correr sharded hace falta un volumen por
worker:
VOL_NAME=harkaq-cosecha-1 scripts/farm/farm-up.sh 1
VOL_NAME=harkaq-cosecha-2 scripts/farm/farm-up.sh 1
SIN_VOLUMEN_OK=1 scripts/farm/farm-up.sh 1 # worker deliberadamente desechable
El latido en gioser va por cron, no por sesión
latido.sh cuelga el bucle de tu sesión de shell, y eso es lo correcto en el laptop: no hay
systemd y cronie sólo existe en uno de los dos arranques. En gioser la premisa se invierte — es un
server, no hay sesión permanente, y crond sí corre. El mismo 2026-08-22 el rescate de hworker-2
dependía de que un agente estuviera despierto mirando, y la respuesta llegó después del auto-borrado.
*/30 * * * * HOME=/home/sergio PATH=/usr/local/bin:/usr/bin:/bin /mnt/vvv/takana/scripts/farm/cosecha-cron.sh >>/mnt/vvv/takana/work/cosecha-cron.log 2>&1
Rutas absolutas las dos: cron arranca en $HOME, así que una ruta relativa al script no
resuelve y —peor— la redirección del log se abre en el directorio equivocado. El script hace su
propio cd "$ROOT", trae su flock, tolera workers ausentes y no baja el store (sólo el
manifiesto de nombres) ⇒ no puede llenar el disco. Parar = borrar la línea.
Lo que el latido no cubre: los artefactos siguen viviendo sólo en el volumen hasta que alguien
haga una pasada de respaldo. El volumen tiene borrado protegido, pero es UNA copia.
Parar de emergencia
hcloud server list | grep hkw # ver workers
hcloud server delete hkw-N # matar uno
scripts/farm/harkaq-vol.sh close # y el volumen