Commit Graph
1808 Commits
Author SHA1 Message Date
Sergio fb7dba530d atuq: el que estaba «nombrado como hueco» entra a la suite — 17 verdes y 2 rojos en 32 min
La primera version dejaba afuera al guardian de notificaciones (`dunst-headless.sh --via-atuq`) con
la excusa de que vive en el arnes de sway, y lo escribia en la cabecera «para que su ausencia no se
lea como cobertura». Eso dura tres dias: al cuarto la lista ES la cobertura y el parrafo no lo lee
nadie. Corrido aparte: verde en 180 s. Entra.

Es ademas el que mide lo que ningun auditor de ELF puede ver: una pagina llama `new
Notification(...)`, `libxul` hace `dlopen("libnotify.so.4")` —que no es NEEDED de nada—, el bus
activa dunst y dunst DIBUJA. Cinco piezas y la unica forma de saber que estan las cinco es verlo.

Para que entrara, la lista pasa a llevar el COMANDO de cada guardian en vez de su nombre de fichero:
lo que lo tenia afuera era el despacho por extension, no una decision.

Corrida entera de los diecinueve sobre `b3:e556024b`: **17 verdes, 2 rojos, 32,2 min**, que es
exactamente lo que la cabecera predice — y la cabecera es el control, no un adorno.

Los caros: sct 242 s, archivo semantico 238 s, ia 182 s, instalacion 180 s, notificaciones 180 s.
Los cuatro primeros salen en 5 s y no tocan el navegador.
2026-09-16 14:05:51 +00:00
Sergio 216b981484 estado: cosecha granja 2026-09-16T14:02:14Z — avance del árbol KDE 2026-09-16 14:02:14 +00:00
SergioandClaude Opus 5 eaa7d02725 el corte, con hora: 93 minutos — y en un servicio que ya autoriza no va límite por tasa
Del `access.log`, sin ambigüedad. La IP del usuario (`45.234.61.160`, autenticada como `sergio`,
2709 peticiones) tuvo tres silencios: **93,2 min (11:59:37 → 13:32:49 UTC)**, 50,1 min (10:44:58 →
11:35:06) y 10,0 min (11:45:06 → 11:55:05). Los tres son el `timeout` de 10 minutos del ban
disparándose una y otra vez: cada vez que volvía, su primera ráfaga lo baneaba de nuevo. El corte
grande terminó exactamente cuando vacié los sets.

🧨 Y LA EXENCIÓN QUE TENÍA NO SERVÍA: la ACL histórica de squid exime `45.234.60.0/24` y él entra
desde `45.234.61.160` — **una red de al lado**. Parecía cubierto y no lo estaba. La pertenencia se
MIDE (`ip in red`), no se deduce del parecido del prefijo.

DECISIÓN DE FONDO: en un servicio que YA AUTORIZA no va límite por tasa. El uso de esta caja es
INTENSIVO —varias sesiones de agente en paralelo, cada una abriendo túneles a la vez— y un límite
calibrado para «una persona normal navegando» no protege de nada: el atacante ajusta su ritmo, el
usuario no puede trabajar más despacio. Squid ya pide usuario y contraseña y tiene su ACL. El ban del
puerto queda apagado (60000/min, ráfaga 1000) y contra un flood queda el tope GLOBAL
`syn_nuevas_por_seg`, que no distingue usuarios ni banea a nadie por ser rápido.

Control tras aplicar: su túnel pasando —`45.234.61.160 … CONNECT api.anthropic.com:443 sergio`— y
`45.234.61.160 ∈ 45.234.60.0/23` comprobado con aritmética de redes, no a ojo.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-16 13:38:54 +00:00
SergioandClaude Opus 5 618dcfd851 🧨 el cortafuegos baneaba a los usuarios del proxy — y squid nunca se cayó
Preguntaron si el proxy estaba caído. NO lo estaba: el ente llevaba horas corriendo con ↻ 0 y
contestando 407. Lo caído era el ACCESO — el cortafuegos de ayer baneaba a los autorizados.

La prueba en una línea: **154.197.1.2, una IP que está en la ACL de squid (gente con contraseña),
apareció en `@ban4`**, con el contador del ban en 38.246 paquetes tirados.

LA CAUSA NO ES LA TASA, ES LA RÁFAGA: `limit rate over 300/minute burst 5 packets` tolera cinco
conexiones por encima del promedio, y un navegador o un agente detrás de un proxy abre DECENAS de
CONNECT en el mismo instante aunque su promedio sea bajísimo. Y como @ban4 es UN SOLO set consultado
antes que todo, caer por el puerto del proxy te tira también el web y el git.

⚠ El tipo ya traía el campo `rafaga` (default 5) con un comentario que dice «para un servicio web va
alto (100-200)». Otra vez leí el .ron de EJEMPLO y no el TIPO.

Hecho, en orden de urgencia: flush de los sets (servicio restaurado en el acto) · los 21 IPs y 5
redes de la ACL de squid a `confiables` · `rafaga` por servicio según su clientela (ssh 5, git 20,
web 100, proxy 200) · proxy a 1200/min con ban de 60 s, que es red de contención contra un flood y
no control de uso. Control después: CERO autorizados en el set, 24 baneados (escáneres) y tráfico
real pasando —`154.194.14.37 … CONNECT api.deepseek.com:443 sigma`, 298 K tunelados—.

⚠⚠ Y un fallo de método que casi lo tapa: **`nft -f` SUMA a la tabla existente**. Cargué el reglaset
corregido encima del viejo y quedaron 30 reglas con las VIEJAS ADELANTE, así que los `confiables`
nuevos no se evaluaban nunca. Por eso `cortafuegos apply-input` borra la tabla antes de cargar.

La lección: un cortafuegos correcto y uno usable no son lo mismo, y la diferencia NO se ve al
aplicarlo. El control que lo habría cazado es el que no corrí: una ráfaga de conexiones desde una IP
que no esté en `confiables`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-16 13:33:50 +00:00
Sergio 9923aa2779 estado: cosecha granja 2026-09-16T13:32:44Z — avance del árbol KDE 2026-09-16 13:32:45 +00:00
SergioandClaude Opus 5 e903f486d9 el enlazador va DENTRO del compilador — /etc/cargo/config.toml es una ruta que cargo no lee
La prueba de la caja recién instalada —`cargo build` en un proyecto nuevo, sin flags ni rutas a
mano— falló con `linker `cc` not found`, y destapó que la pieza que había empaquetado era falsa:
**cargo NO LEE `/etc/cargo/config.toml`**. Sólo lee `$CARGO_HOME/config.toml`, los
`.cargo/config.toml` del proyecto y sus padres, y lo que se le pase con `--config` — que es lo que yo
venía pasando a mano en cada prueba, y por eso «funcionaba».

Y el arreglo no era mover el fichero: **el que enlaza es `rustc`, y rustc no lee la config de
cargo**. Las flags van DENTRO del compilador, en el spec del triple:

· `linker = "ld.lld"` + `linker_flavor = Gnu(Cc::No, Lld::Yes)` — sin esto rustc busca `cc`, que una
  caja takana no tiene.
· `crt_static_default = true` — sin esto enlaza dinámico y pide `-lgcc`, que no existe (la distro se
  construye con zig, que trae compiler-rt).
· `crt_static_allows_dylibs = true` — su pareja obligatoria: sin ella `crt-static` apaga los dylibs
  y con ellos los PROC-MACROS (serde_derive, clap_derive, las 44 recetas `cargo-*` con `derive`).
  El compilador en sí sigue dinámico: el `bootstrap.toml` fija `crt-static = false` para el build, y
  eso gana sobre el default del spec.

⇒ `cargo-config` (receta, árbol y copia en scripts/) se van enteros: un paquete que instala un
fichero que nadie abre es peor que no tenerlo.

Y de paso, `lld21` instala los cuatro alias como SYMLINKS y no como copias: cmake los duplicaba, 5 ×
96 M = 478 M de artefacto para 97 M de enlazador. Ahora 108 M, y `ld.lld` sigue despachando por
`argv[0]`, que es como upstream lo diseñó.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-16 13:07:56 +00:00
Sergio c57fcb214f estado: cosecha granja 2026-09-16T13:02:56Z — avance del árbol KDE 2026-09-16 13:02:56 +00:00
SergioandClaude Opus 5 0a894cfac1 perfil.servidor: fuera el prebuilt ajeno, entra el toolchain propio
Sale `rust-toolchain-bin` —799 M de bytes AJENOS sellados tal cual (`foreign`)—, que era el escalón 1
del SDD 31 y cumplió su función: con él se construyó el escalón 2, y el escalón 2 ya compila,
enlaza y corre.

Entran TRES, que son el mismo hecho («esta caja compila Rust») partido en piezas:
· `rust` (353 M) — el compilador y cargo, de la granja, desde fuente.
· `lld21` — el enlazador. NO es opcional: una caja takana no tiene `cc`, y sin enlazador `rustc`
  compila objetos y no produce un ejecutable.
· `cargo-config` — receta nueva que publica `/etc/cargo/config.toml` con las tres flags que unen a
  los dos. Sin ellas `cargo build` muere con «linker `cc` not found»: el toolchain estaría completo
  y MUDO, que es [[subcomando-sin-driver]] otra vez. Es config de SISTEMA y no de usuario a
  propósito — un `~/.cargo/config.toml` funcionaría para quien lo escribió y no para el siguiente.

⚠ Y `rust` se re-hashea otra vez (702094a8 → fe277b32) por una línea que descubrió el control de
PATH: **x.py instala en `/usr/local` por defecto, y el PATH de una caja takana es
`/bin:/usr/bin:/sbin:/usr/sbin`** — medido en la caja. O sea que el perfil habría proyectado un
`rustc` perfecto que nadie puede invocar: «existe ≠ se encuentra». Ahora `[install] prefix = "/usr"`,
como el resto del corpus.

Control previo que sí pasó: `rustc` y `cargo` corren SIN `LD_LIBRARY_PATH` — su `RUNPATH` es
`$ORIGIN/../lib`, así que la proyección del artefacto basta.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-16 12:38:15 +00:00
Sergio 240069801e estado: cosecha granja 2026-09-16T12:32:03Z — avance del árbol KDE 2026-09-16 12:32:03 +00:00
Sergio d22bc8443a estado: cosecha granja 2026-09-16T12:02:13Z — avance del árbol KDE 2026-09-16 12:02:13 +00:00
Sergio f8a900a923 estado: cosecha granja 2026-09-16T11:32:14Z — avance del árbol KDE 2026-09-16 11:32:15 +00:00
SergioandClaude Opus 5 72ba86c290 la cadena rehecha: llvm21 arreglado, lld enlaza, rust reconstruido — los seis controles pasan
Un cambio de dos líneas en `llvm21` re-selló tres artefactos y los molió la granja sola, en orden,
por dependencia: llvm21 95956a16→bd4a4094, lld21 27be53c1→95a4794b, rust 015a07fb→702094a8.

    llc --version        Stack dump: + segfault   →   LLVM 21.1.2, Optimized build
    opt --version        ídem                     →   LLVM 21.1.2
    ld.lld               «not built with zlib»    →   ENLAZA
    rustc --version                               →   1.97.0 (built from a source tarball)
    --print target-list                           →   x86_64-alpine-linux-musl
    cargo build + correr                          →   0,16 s y el binario habla

La config del sitio pasa a usar `lld` (más rápido); el `ld` de binutils queda documentado como
alternativa que también funciona.

LA REGLA QUE SE GANÓ SU LUGAR: **`link = "static"` en el encabezado decide cosas que la receta no
dice**. Van tres, todas medidas: apagó los threads de openssl, le mintió a libtool, y acá rompió los
registros estáticos de LLVM (`cl::opt`, `TargetRegistry`) porque el `static-pie` descarta sus
constructores globales. Con C++ grande la postura por defecto pasa a ser `dynamic` y medirlo — y el
control no es que selle: es CORRER UNA HERRAMIENTA del artefacto.

Y el corolario de por qué nadie lo vio en meses: el único consumidor de llvm21 era rust, que usa
`llvm-config` y las `.a`, nunca una herramienta. **Un artefacto puede estar roto en todo lo que nadie
usa** — como los 44 `cargo-*` sin cargo y los 23 binarios sin cargador.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-16 11:19:18 +00:00
Sergio 3a68db44b8 estado: cosecha granja 2026-09-16T11:02:38Z — avance del árbol KDE 2026-09-16 11:02:38 +00:00
Sergio 2ff2a31150 estado: cosecha granja 2026-09-16T10:31:47Z — avance del árbol KDE 2026-09-16 10:31:47 +00:00
Sergio 5d30ef4b58 estado: cosecha granja 2026-09-16T10:01:44Z — avance del árbol KDE 2026-09-16 10:01:44 +00:00
Sergio 10878bf4a7 estado: cosecha granja 2026-09-16T09:31:22Z — avance del árbol KDE 2026-09-16 09:31:22 +00:00
Sergio c936d55675 estado: cosecha granja 2026-09-16T09:02:07Z — avance del árbol KDE 2026-09-16 09:02:07 +00:00
Sergio 83f5ec6e66 estado: cosecha granja 2026-09-16T08:31:20Z — avance del árbol KDE 2026-09-16 08:31:20 +00:00
Sergio 3a58cc6cbe estado: cosecha granja 2026-09-16T08:01:51Z — avance del árbol KDE 2026-09-16 08:01:51 +00:00
Sergio c0fbf75353 estado: cosecha granja 2026-09-16T07:31:21Z — avance del árbol KDE 2026-09-16 07:31:21 +00:00
Sergio ba6680c7b2 estado: cosecha granja 2026-09-16T07:01:54Z — avance del árbol KDE 2026-09-16 07:01:55 +00:00
Sergio 611d420f99 estado: cosecha granja 2026-09-16T06:31:20Z — avance del árbol KDE 2026-09-16 06:31:20 +00:00
Sergio 60e6197970 estado: cosecha granja 2026-09-16T06:01:45Z — avance del árbol KDE 2026-09-16 06:01:45 +00:00
Sergio afae751e65 estado: cosecha granja 2026-09-16T05:31:21Z — avance del árbol KDE 2026-09-16 05:31:21 +00:00
Sergio 4a6696bb52 estado: cosecha granja 2026-09-16T05:01:35Z — avance del árbol KDE 2026-09-16 05:01:35 +00:00
SergioandClaude Opus 5 ef1f9252a1 EL TOOLCHAIN COMPILA, ENLAZA Y CORRE — en una caja sin compilador de C
$ cargo build --offline
       Compiling prueba v0.1.0 … Finished in 0.44s
    $ ./target/debug/prueba
    cargo, rustc y ld: los tres nuestros

`cargo` y `rustc` son los que construyó la granja; el enlazador es el `ld` de nuestro `binutils`.

EL MURO 8 SE CERRÓ SIN `lld`. La cadena, que es lo que vale: `-C linker-flavor=ld.lld` → «linker
`lld` not found» (el build con LLVM externo no genera rust-lld); `rust.lld = true` → el bootstrap lo
RECHAZA; `lld21` propio → crasheaba con cualquier entrada; `lld21` con zlib → **no se puede encender
desde ahí**, porque `LLVMConfig.cmake` dice `LLVM_ENABLE_ZLIB 0` y el build standalone lo hereda.

Lo que faltaba NO era un enlazador: era decirle a rustc que enlace en ESTÁTICO. Con `+crt-static`,
rustc usa su propio libunwind/compiler_builtins y deja de pedir `-lgcc` — que es lo que rompía el
intento con `ld`, porque la distro se construye con zig (compiler-rt) y libgcc no existe. Y estático
es el modo natural de musl.

⇒ `scripts/servidor/cargo-config.toml` (instalado en `/etc/cargo/config.toml`) cierra el pendiente
«config del sitio» del §5, con las tres flags y el porqué de cada una.

🧨 Y `lld21` destapó algo que nadie había visto: **las 79 herramientas de `llvm21` están ROTAS**.
`llc --version` crashea igual —también en el worker— porque salen `static-pie` y el enlace estático
descarta los constructores globales de los que dependen `cl::opt` y `TargetRegistry`. Funciona lo
que no los necesita (`llvm-ar`, `llvm-config`) y revienta lo que sí; nadie lo notó porque `rust` usa
`llvm-config` y las `.a`, nunca una herramienta. Queda como deuda DECLARADA: arreglarlo (o encender
zlib) re-hashea `llvm21` y con él `rust`. Hoy no bloquea nada.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-16 04:48:25 +00:00
Sergio 8f3bc54f55 estado: cosecha granja 2026-09-16T04:31:36Z — avance del árbol KDE 2026-09-16 04:31:36 +00:00
Sergio cea668376c estado: cosecha granja 2026-09-16T04:02:11Z — avance del árbol KDE 2026-09-16 04:02:11 +00:00
Sergio 3f3bb2ed16 estado: cosecha granja 2026-09-16T03:31:40Z — avance del árbol KDE 2026-09-16 03:31:40 +00:00
Sergio ffe26377fb estado: cosecha granja 2026-09-16T03:01:33Z — avance del árbol KDE 2026-09-16 03:01:34 +00:00
Sergio 4f78a80c47 estado: cosecha granja 2026-09-16T02:31:35Z — avance del árbol KDE 2026-09-16 02:31:35 +00:00
Sergio 0ffcf4ed65 estado: cosecha granja 2026-09-16T02:01:47Z — avance del árbol KDE 2026-09-16 02:01:47 +00:00
Sergio 6b72d8d57b estado: cosecha granja 2026-09-16T01:31:45Z — avance del árbol KDE 2026-09-16 01:31:45 +00:00
Sergio 8a6c928803 estado: cosecha granja 2026-09-16T01:01:51Z — avance del árbol KDE 2026-09-16 01:01:51 +00:00
Sergio 1eff89d870 estado: cosecha granja 2026-09-16T00:31:55Z — avance del árbol KDE 2026-09-16 00:31:56 +00:00
SergioandClaude Opus 5 be95b71449 SDD 31: EL ESCALÓN 2 SELLÓ — rust 1.97.0 desde fuente, b3:015a07fb…
353 M, construido por la granja desde `llvm21` del corpus y el stage0 del lab. Los controles, en la
caja (que sí tiene cargador musl; el worker no):

    rustc --version            → 1.97.0 … (built from a source tarball)   ← NUESTRO
    rustc --print target-list  → x86_64-alpine-linux-musl                 ← EL PARCHE, DENTRO
    cargo --version            → 1.97.0 … (built from a source tarball)

Y compila: `--emit=obj` da un objeto de 3,5 K y `--crate-type=rlib` una biblioteca, sin enlazador.

🧱 MURO 8, YA MEDIDO: el compilador no trae con qué ENLAZAR. En una caja sin `cc` —cualquier takana
que no sea un hub— `-C linker-flavor=ld.lld` da «linker `lld` not found» y el `ld` de binutils muere
con «cannot find -lgcc». La causa estaba en una línea del log que parece informativa:

    skipping llvm-tools (x86_64-alpine-linux-musl): external LLVM

Al usar LLVM externo —que es lo que queremos— x.py se salta las herramientas de LLVM, y `rust-lld`
es una de ellas. El prebuilt oficial sí la trae, y por eso el escalón 1 enlazaba. Y `llvm21` no
sirve de repuesto: NO publica ningún `lld` (medido sobre sus 1,6 G).

Dos salidas: (a) `[rust] lld = true`, que construye rust-lld desde el llvm-project vendoreado y deja
el toolchain autosuficiente; (b) publicar `lld` desde llvm21 y fijar el linker en un
`/etc/cargo/config.toml` del sitio.

Y la lección de método: el artefacto SELLÓ, reproduce y NO ENLAZA. Es [[subcomando-sin-driver]] en
el corazón del toolchain. Un `build ok` no es la prueba: la prueba es el objeto en el disco.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-16 00:23:46 +00:00
Sergio 953eadc046 estado: cosecha granja 2026-09-16T00:02:19Z — avance del árbol KDE 2026-09-16 00:02:19 +00:00
Sergio c8aa1ddd08 estado: cosecha granja 2026-09-15T23:31:40Z — avance del árbol KDE 2026-09-15 23:31:40 +00:00
Sergio efbb27d548 estado: cosecha granja 2026-09-15T23:01:47Z — avance del árbol KDE 2026-09-15 23:01:47 +00:00
Sergio 3ec12df8db estado: cosecha granja 2026-09-15T22:36:03Z — avance del árbol KDE 2026-09-15 22:36:03 +00:00
Sergio d68fcd84f1 estado: cosecha granja 2026-09-15T22:02:06Z — avance del árbol KDE 2026-09-15 22:02:07 +00:00
SergioandClaude Opus 5 24a6dd3b2f el cortafuegos puesto en la caja — y el primer baneado fui yo
`table inet tawasuyu_input` con policy drop, 15 reglas de puerto, y el set `ban4` llenandose solo:
a los pocos minutos, 27 IPs baneadas y paquetes tirados por el contador. El reemplazo de fail2ban
funcionando sobre trafico real, dentro del kernel y sin daemon.

🧨 EL PRIMER BANEADO FUI YO, EN SEGUNDOS. Aplicada la primera version: ssh y https bien, y `http`,
proxy y git-ssh muertos; al minuto la caja entera dejo de contestarme. La causa no es una regla mal
escrita: **el set @ban4 es UNO SOLO para todos los servicios** y `ip saddr @ban4 drop` esta antes
que todo, asi que pasarse de tasa en UN puerto te tira en TODOS — y el `nuevas_por_min: 5` del
puerto de administracion es exactamente lo que me pasa a mi, porque cada comando remoto es una
conexion nueva. Lo devolvio el INTERRUPTOR DE HOMBRE MUERTO (un `nft flush ruleset` programado a
180 s que sólo se desarma si la verificacion externa sale bien). Sin consola serie, esa es la
diferencia entre un susto y un rescue.

⚠ Y el tipo ya lo avisaba: `PoliticaEntrada` tiene un campo `confiables` cuyo comentario describe
palabra por palabra lo que me paso. Copie el .ron de EJEMPLO en vez de leer el TIPO. La cura fue una
linea con el hub y el worker, que se aceptan ANTES de la logica de tasa.

⚠ `nft -c` rechazo 5 reglas antes de aplicar nada: `ct count over N` es CONFIG_NFT_CONNLIMIT y este
kernel no lo trae. Quedan COMENTADAS y no borradas en el fichero que se aplica — lo que la politica
declara y el kernel no puede tiene que verse. El simbolo ya entro a la receta del kernel.

Y para que sobreviva al reinicio, `nftables` declara su [[service]] con `lifecycle = "oneshot"`:
cargar un reglaset no es un daemon, es un acto. Va PRIMERO en el genesis — entre que la red esta y
que las reglas cargan, la caja esta abierta. Probado con `nft flush ruleset` + `arjectl start`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-15 21:56:51 +00:00
SergioandClaude Opus 5 a0b3e27eff SDD 31 §4.bis: el sexto muro de rust, que la granja encontró sola
`rust` en la cola del worker llegó MUCHO más lejos: compiló las 386 crates del stage1 y murió al
empezar `Building stage1 library artifacts (stage1 -> stage1)`:

    error: error loading target specification: could not find specification for target
           "x86_64-alpine-linux-musl"

Es la OTRA MITAD del muro 4 y sólo se ve en el stage 2: usar el triple de Alpine funciona mientras
el compilador que manda es el del LAB —Alpine le PARCHEA esa especificación a su rustc— y deja de
funcionar en cuanto toma el relevo el rustc recién construido, que es upstream puro y no la conoce.
El triple que hace falta para ARRANCAR el build es el que el PRODUCTO del build no tiene.

Tres salidas, con la elegida: parchear `rustc_target` para registrar el triple (lo que hace Alpine;
queda DENTRO del compilador y vale en todos los stages). El `RUST_TARGET_PATH` + JSON es más barato
pero un target por JSON como HOST es terreno que rustc no soporta bien. Y volver al canónico no
tiene candidato a stage0: el prebuilt oficial trae esa std pero no puede compilar proc-macros.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-15 21:43:12 +00:00
SergioandClaude Opus 5 70e4c540d3 la caja arrancó con nf_tables — y la Semilla la levantó ENTERA sola
`nft list ruleset` contesta vacío y sin error: nf_tables funciona por primera vez en una máquina
takana. Comprobado con el mecanismo que reemplaza a fail2ban y no con una regla de adorno: un set
`flags dynamic` con `add @ban4 { ip saddr timeout 10s limit rate over 5/minute } drop`, aceptado por
el kernel y releído tal cual.

LO QUE EL REINICIO PROBÓ DE PASO, y vale más que el kernel: la Semilla levantó los OCHO entes sola,
todos con ↻ 0 — incluidos los DOS enjaulados en qorpa, que arrancan desde el genesis con su
newuidmap, su overlay y su harkaq igual que un binario nativo. Todo lo que se editó en
/ente/seed.card.json a lo largo del día era hasta ahora una declaración SIN PROBAR: un reinicio es
el único examen que toma. Desde fuera, al minuto: siete dominios en 200, el proxy en 407 y
`git ls-remote` por SSH:2345 devolviendo el commit. Cero intervención.

⚠ `reboot` NO reinicia una caja con arje-zero: le pide a PID 1 que lo haga y arje-zero no implementa
ese protocolo — el comando vuelve, no dice nada, y la máquina sigue arriba (`up 4 days` después de
«reiniciar»). Funciona `reboot -f`, que llama a reboot(2) saltándose al init. Falta la pieza: un
init que gobierna producción tiene que atender un apagado ordenado.

🧨 Y CASI REINICIO EL HUB POR UN BACKTICK: el aviso decía «no atiende el `reboot` normal» dentro de
comillas DOBLES, y la shell ejecutó lo de entre backticks — `reboot`, en gioser. No pasó nada porque
la sesión no es root y OpenRC contestó «you must be root». Es la TERCERA vez en el día que un
backtick dentro de un texto se ejecuta; las otras dos sólo se comieron un comentario. La regla deja
de ser de estilo: nunca un backtick dentro de comillas dobles en una shell.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-15 21:41:08 +00:00
Sergio ba202d7826 atuq: los dieciocho guardianes en UN comando — y el sello llevaba nueve horas atrás
`atuq` es `source.dir` entero, asi que el commit de la boveda de esta manana (`6f0ba974`) movio el
ArtifactHash y dejo el artefacto vigente SIN SELLAR. Durante esas nueve horas ningun guardian podia
medir: se niegan a correr contra uno que no sea el vigente, que es lo correcto. Pero un guardian que
se niega solo grita CUANDO alguien lo corre, y nadie lo corria — el repo se veia sano y la unica
senal era un `hash --check` que nadie tenia motivo para teclear.

Resellado en 3,7 s (es derivado y el firefox vigente estaba en el store) y corridos los dieciocho
sobre `b3:e556024b`: 16 en verde y 2 en ROJO, los dos por la MISMA causa —los tres verbos de la
boveda del §7.quinquies—. O sea que la unidad 12 no rompio nada de al lado, que es lo que habia que
saber antes de seguir: toco `atuq.cfg`, la politica y el empaquetado de extensiones.

`scripts/test-atuq-suite.sh` deja eso en un comando, en serie (cuatro nucleos sin swap; dos
navegadores a la vez es un OOM) y cada guardian a SU fichero, porque dos corridas sobre el mismo log
se mezclan y el resultado parece un pase. Lo primero que mira es el sello y NO corre nada si falta:
verificado contra un store vacio, sale por ahi y devuelve 1.

Dos cosas quedan escritas como control y no como adorno:

· el numero de rojos esperados va en la cabecera. Un cuadro entero en verde puede ser un producto
  sano o un runner que no corre nada, y se ven igual;
· esa cabecera decia «un rojo» —prediccion escrita antes de correr— y la primera corrida dijo dos:
  el guardian de coherencia de la boveda lleva el mismo chequeo del cable adentro.

Coste medido en gioser: ~29 min. Los cuatro primeros salen en 10 s y no tocan el navegador.
2026-09-15 21:33:05 +00:00
Sergio 4de3f2b526 estado: cosecha granja 2026-09-15T21:31:50Z — avance del árbol KDE 2026-09-15 21:31:50 +00:00
Sergio e3cf54c4b2 estado: cosecha granja 2026-09-15T21:01:50Z — avance del árbol KDE 2026-09-15 21:01:50 +00:00
Sergio a309ab488e estado: cosecha granja 2026-09-15T20:31:56Z — avance del árbol KDE 2026-09-15 20:31:56 +00:00
SergioandClaude Opus 5 4753a991f1 qdrant, act_runner y fail2ban MUEREN — y el sustituto del último no puede correr todavía
Decisión del usuario sobre lo que quedaba del censo:

· `qdrant` (:6333/:6334) — es la base vectorial de `gioser_api`, y ese backend es un FÓSIL
  (api.gioser.net da 502 permanente desde el §6.9). Una base sin consumidor no se muda. La receta
  queda en el catálogo, que no es lo mismo que la imagen, y eso NO es deuda.
· `act_runner` (:41027) — runner de CI, binario suelto que nadie provee. Si mañana hace falta CI,
  entra por receta y no por un binario copiado.
· `fail2ban` — **tawasuyu ya tiene el sustituto y es mejor**: `shared/cortafuegos` (SDD-ENTRADA §1)
  no mira logs con un daemon, usa DOS SETS DINÁMICOS DEL KERNEL (`ban4`/`ban6`, `flags dynamic`,
  `timeout`) con `limit rate over N/minute` POR IP de origen. El ban lo aplica nftables sin proceso
  y sin latencia.

🧨 PERO EL SUSTITUTO NO ARRANCA: `nft list ruleset` da «cache initialization failed: Invalid
argument». `nft 1.1.6` está instalado y sellado; lo que falta está un piso más abajo, en el
`.config` que publica el artefacto del kernel:

    CONFIG_NETFILTER=y · CONFIG_NF_CONNTRACK=y · **# CONFIG_NF_TABLES is not set**
    y la causa: **# CONFIG_NETFILTER_ADVANCED is not set** (NF_TABLES cuelga de ella)

Es la lección del §6.4 muro 1 otra vez: la receta dice lo que se CAMBIÓ; sólo el `.config` sellado
dice lo que QUEDÓ. `linux-generic` sale de `make defconfig` y defconfig apaga NETFILTER_ADVANCED.

⇒ HOY LA CAJA NO TIENE CORTAFUEGOS DE NINGUNA CLASE, con :22022, :2345, :1137, :80 y :443
públicos. Hay que decirlo entero. La cura es un kernel con NETFILTER_ADVANCED + NF_TABLES (trabajo
del SDD 22) y termina en un reinicio de producción.

MITIGACIÓN HECHA HOY, que no depende del kernel: el `sshd` ofrecía `password` y
`keyboard-interactive` —su config eran CUATRO líneas y ninguna hablaba de autenticación, así que
regía el default de OpenSSH— que es justo la puerta que fail2ban cerraba. Ahora es sólo claves, con
cuatro controles: la clave entra, :22022 y :2345 contestan `Permission denied (publickey)` sin
ofrecer contraseña, y el `git ls-remote` por SSH sigue andando.

La lección: una defensa se muda con su CAPACIDAD, no con su nombre. Decidir «fail2ban muere porque
tenemos algo mejor» es correcto Y deja un hueco abierto hasta que ese algo mejor pueda ejecutarse.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-15 20:28:29 +00:00
SergioandClaude Opus 5 0a7afebe9c la caja tiene HORA, LATIDO y ROTACIÓN — y el respaldo del gitea dejó de ser a mano
`planear.py --revisar` sobre el censo ordena lo que queda, y lo primero no eran los servicios del
usuario sino TRES CAPACIDADES que ninguna metrica reclama: el disparador periodico, la hora y la
rotacion de logs. Los tres paquetes ya estaban sellados y declarados en `perfil.servidor` desde que
ese perfil nacio — lo que faltaba era el eslabon que los ARRANCA, igual que con squid.

· `chronyd`: la caja iba **15 s atrasada** y nadie la corregia. Ahora stratum 3 contra los NTP de
  Hetzner, 0,000005 s de NTP. Sin hora no hay TLS ni firmas, y el sintoma no se parece a la causa.
· `crond`: no existia el latido. Verificado con una entrada `* * * * *` en el crontab REAL.
· `logrotate`: `/var/log/squid` sin rotar. Diario, con `squid -k rotate` (squid mantiene los
  ficheros abiertos: un rename a secas lo deja escribiendo en un inode que nadie puede leer).
· `scripts/respaldo-gitea.sh` + `17 3 * * *`: cierra el hueco del §6.15 — los 44 repos vivian en UNA
  copia y el respaldo se hacia a mano. Sube 23+21 repos (1,5 G) y un `gitea.db` de 332 M tomado con
  `.backup`, consistente con el servidor vivo. El control no es que el script salga 0: es CONTAR los
  repos de los dos lados.

`chrony` y `cronie` NO declaraban `[[service]]` — el paquete llegaba a la imagen y no lo arrancaba
nadie. Ahora lo declaran (fuera de `hash_inputs`: los hashes no se movieron) y el perfil los habilita.

TRES MEDICIONES que valen mas que el resultado:
1. **La deriva era de gioser, no de la caja**: despues de sincronizar, el HUB quedo 3 s adelantado.
   Medir «contra el otro» sin un tercero no dice quien esta mal.
2. **El spool de cronie es `/var/spool/cron/<usuario>`**, no `…/crontabs/<usuario>`: `crontab -l`
   mostraba las entradas y `crontabs/` estaba vacio. Un crontab restaurado en el sitio equivocado es
   un fichero perfecto que nadie lee.
3. 🧨 **`sqlite3` seguia INERTE en la caja** (`compressBound: symbol not found`): el ultimo de los 23
   del §6.4. Le faltaba `zlib-shared`, sellado y declarado en `perfil.base` desde el 11-09 — pero la
   caja se instalo antes. Sin el no habia snapshot consistente. Un paquete declarado no es un
   paquete instalado: en una caja viva vale lo que el `upgrade` proyecto.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-15 20:17:20 +00:00