From eb96c9219e07051d088884e038f86b63921e72c0 Mon Sep 17 00:00:00 2001 From: Sergio Date: Thu, 17 Sep 2026 17:34:55 +0000 Subject: [PATCH] =?UTF-8?q?el=20agente=20corre=20en=20la=20caja=20y=20pued?= =?UTF-8?q?e=20construir=20=E2=80=94=20la=20condici=C3=B3n=20que=20el=20us?= =?UTF-8?q?uario=20puso=20para=20borrar=20gioser?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit $ claude -p "Decí exactamente: CLAUDE CORRIENDO EN LA CAJA" CLAUDE CORRIENDO EN LA CAJA Turno real —API, credenciales, respuesta— desde 2.29.29.217. El CLI es un ELF glibc de 360 M y la caja es musl, así que va enjaulado (ADR 0015, el caso de sergioh-api). La instancia declara red, nesting y SEIS directorios: /opt/takana, /work/dev-fs, /store, /root/.claude, /root/.ssh (ro) y el CLI (ro). Es ANCHA y está dicho: un agente que construye, commitea y empuja necesita lo mismo que un humano; lo que la jaula compra es que el alcance esté escrito y acotado. ⚠ El `takana` que construye NO se instala adentro: sale del store, que ya está concedido — es estático musl y corre igual dentro de una imagen glibc. LA CAJA CONSTRUYE: sellados hoy `intel-ucode` (156 signatures) y `sof-firmware` (2 firmware + 8 topologías). Faltaba una pieza que el lab no traía: `.dev-fs/tools` (zig 0.13/0.16 y go, 1 G) — la imagen del lab empaqueta `alpine/` y nada más, y el primer intento murió con «zig no encontrado». 🧱 Y lo que NO anda, con su rodeo: `takana build` dentro de la jaula muere con «bwrap: Can't mount proc on /proc» aunque `nesting = true`. No es el anidamiento —un `bwrap --proc /proc` a mano SÍ funciona adentro— sino el userns anidado del sandbox del build; probado también con `root = false`, que es la sospecha que el propio código documenta, y falla igual. El rodeo que sí anda, escrito en el lanzador: pedirle el build al ANFITRIÓN por ssh a 127.0.0.1:22022. Así se selló sof-firmware desde dentro de la jaula. Manifiesto y lanzador versionados en scripts/servidor/: una instancia que sólo existe en la caja se pierde con la caja. ⚠ Pendiente menor: la memoria del agente está en `-mnt-vvv-takana` y allá el repo vive en /opt/takana ⇒ el índice POR RUTA no coincide y arranca sin memoria del proyecto aunque los 733 ficheros estén en disco. Co-Authored-By: Claude Opus 5 (1M context) --- docs/28-servidor-de-produccion.md | 70 ++++++++++++++++++++ scripts/servidor/claude-caja.sh | 15 +++++ scripts/servidor/qorpa-claude.instance.toml | 72 +++++++++++++++++++++ 3 files changed, 157 insertions(+) create mode 100755 scripts/servidor/claude-caja.sh create mode 100644 scripts/servidor/qorpa-claude.instance.toml diff --git a/docs/28-servidor-de-produccion.md b/docs/28-servidor-de-produccion.md index c5acb772..16355a8e 100644 --- a/docs/28-servidor-de-produccion.md +++ b/docs/28-servidor-de-produccion.md @@ -2842,6 +2842,76 @@ faltan en el destino**. `api/main.py` se publica es del dueño del repo, no de la mudanza. El `LEEME.txt` del paquete lleva el comando exacto para reapuntarlo. +### 6.50 🤖 El agente corre en la caja — y puede construir *(2026-09-17)* + +Pedido del usuario, y es la condición que él mismo puso para borrar gioser: *«no borramos gioser +hasta tenerte corriendo de aquel lado, con capacidad de compilar tawasuyu y las recetas de takana»*. + +``` + $ claude -p "Decí exactamente: CLAUDE CORRIENDO EN LA CAJA" + CLAUDE CORRIENDO EN LA CAJA +``` + +Eso es un turno REAL —API, credenciales, respuesta— desde `2.29.29.217`. + +#### Por qué va enjaulado, y qué se le concedió + +El CLI es un **ELF glibc de 360 M** (`interpreter /lib64/ld-linux-x86-64.so.2`, `NEEDED libc.so.6`) +y la caja es musl: no hay camino nativo. Es el caso del ADR 0015, el mismo de `sergioh-api`. La +instancia `claude` declara, y **conviene decir que es ANCHA en vez de disimularlo**: red, `nesting`, +y seis directorios —`/opt/takana` (el trabajo), `/work/dev-fs` (lab + zig/go), `/store`, +`/root/.claude` (memoria y credenciales), `/root/.ssh` (ro) y el CLI (ro)—. Un agente que construye, +commitea y empuja necesita lo mismo que un humano haciendo ese trabajo; lo que la jaula compra es +que ese alcance esté **escrito y acotado**, no que sea pequeño. + +⚠ El `takana` que construye **no se instala adentro**: sale del store, que ya está concedido. Es +estático musl y corre igual dentro de una imagen glibc — comprobado (`takana 0.0.1`). + +#### La caja construye: dos recetas selladas hoy + +Antes faltaba una pieza que el lab no traía: **`.dev-fs/tools`** (zig 0.13/0.16 y go, 1 G). El +primer intento murió con `zig no encontrado en .dev-fs/tools/zig/zig` — la imagen del lab empaqueta +`alpine/` y **nada más**. Copiado eso: + +``` + intel-ucode → sellado: 156 signatures + GenuineIntel.bin (17 MB) + sof-firmware → sellado: 2 firmware + 8 topologías (IPC3 v2.2, TigerLake) +``` + +#### 🧱 Lo que NO funciona, y su rodeo + +**`takana build` NO corre DENTRO de la jaula**, aunque la instancia declare `nesting = true`: + +``` + bwrap: Can't mount proc on /proc: Operation not permitted +``` + +Y no es que el anidamiento esté roto: **un `bwrap --dev-bind / / --proc /proc` a mano SÍ funciona +adentro**, con y sin `--unshare-pid`. Lo que falla es el userns anidado que arma el sandbox del +build. Probado también con `root = false` —la sospecha que el propio código documenta (§`grants.root` +⚠ del 2026-09-03)— y falla igual: no es el remapeo a root. + +El rodeo que sí anda, y quedó escrito en el lanzador para que el agente lo use: + +```sh + ssh -p 22022 -i /root/.ssh/github5 root@127.0.0.1 \ + 'cd /opt/takana && flock -o work/.farm-build.lock takana --store /store build ' +``` + +Con eso, **desde dentro de la jaula se selló `sof-firmware`**. El agente vive enjaulado y le pide +los builds al anfitrión, que es donde el sandbox del build tiene los permisos que necesita. + +#### Cómo se entra + +`claude` en el PATH de la caja (`/usr/bin/claude`), que envuelve +`takana qorpa run claude -- env HOME=/root …`. El manifiesto y el lanzador están versionados en +`scripts/servidor/`, porque una instancia que sólo existe en la caja se pierde con la caja. + +⚠ Pendiente menor: la memoria del agente está en `/root/.claude/projects/-mnt-vvv-takana/`, y allá el +repo vive en `/opt/takana` ⇒ **el índice por ruta no coincide**. Se resuelve renombrando el +directorio a `-opt-takana` o dándole al repo la misma ruta; hasta entonces el agente arranca sin su +memoria del proyecto aunque los 733 ficheros estén en disco. + ## 7. Reusar los scripts que ya existen, y no escribir de nuevo Pedido explícito del usuario. El inventario de lo que ya hace el trabajo: diff --git a/scripts/servidor/claude-caja.sh b/scripts/servidor/claude-caja.sh new file mode 100755 index 00000000..3f9c848d --- /dev/null +++ b/scripts/servidor/claude-caja.sh @@ -0,0 +1,15 @@ +#!/bin/sh +# claude — entrar al agente en esta caja. El CLI es glibc y la caja es musl: corre enjaulado +# (ADR 0015), con el repo, el store, el lab, la memoria y las llaves concedidos y nada más. +# +# ⚠ LOS BUILDS NO CORREN DENTRO DE LA JAULA. `takana build` levanta su propio bwrap y ahí falla con +# «Can't mount proc on /proc» aunque la instancia declare `nesting = true` (un `bwrap --proc /proc` +# a mano SÍ funciona adentro: lo que falla es el userns anidado que arma el sandbox del build). +# El camino que sí anda —y el que el agente debe usar— es pedírselo al ANFITRIÓN por ssh: +# +# ssh -p 22022 -i /root/.ssh/github5 root@127.0.0.1 \ +# 'cd /opt/takana && flock -o work/.farm-build.lock takana --store /store build ' +# +# Comprobado el 2026-09-17: así se selló `sof-firmware` desde dentro de la jaula. +exec takana qorpa run claude -- env HOME=/root TERM="${TERM:-xterm-256color}" \ + /work/claude-cli/2.1.240 "$@" diff --git a/scripts/servidor/qorpa-claude.instance.toml b/scripts/servidor/qorpa-claude.instance.toml new file mode 100644 index 00000000..afb8af96 --- /dev/null +++ b/scripts/servidor/qorpa-claude.instance.toml @@ -0,0 +1,72 @@ +# Manifiesto de la instancia — ESTO es la verdad, `upper/` es caché descartable (ADR 0015 D3). +# +# ── POR QUÉ EXISTE ESTA JAULA (2026-09-17) ────────────────────────────────────────────────────── +# Pedido del usuario: «no borramos gioser hasta tenerte corriendo de aquel lado, con capacidad de +# compilar tawasuyu y las recetas de takana». El CLI de Claude Code es un **ELF glibc de 360 M** +# (`interpreter /lib64/ld-linux-x86-64.so.2`, NEEDED libc.so.6) y esta caja es musl: no hay camino +# nativo. Es el caso del ADR 0015, igual que `sergioh-api`. +base = "sha256:895661bdf6c64e91b7725874165fd05dd30c438d3ffec661671ab5cfb261ca58" +distro = "arch" + +# Lo que hace falta ADENTRO para trabajar el repo: git para commitear y empujar, openssh para +# llegar al gitea y al worker, y las CA para TLS. El CLI trae su propio node y su propio ripgrep. +# `bubblewrap`: `takana build` levanta SU PROPIO sandbox adentro. Sin esto la jaula lee recetas y no +# construye ninguna, que es la mitad del pedido. El `takana` que las construye NO se instala acá: +# sale del STORE, que ya está concedido — es estático musl y corre igual dentro de una imagen +# glibc (comprobado: `takana 0.0.1` desde /store dentro de la jaula). +packages = ["git", "openssh", "ca-certificates", "bubblewrap"] + +[grants] +# ⚠ ESTA JAULA ES ANCHA A PROPÓSITO, Y CONVIENE DECIRLO EN VEZ DE DISIMULARLO. Un agente que +# construye recetas, commitea y empuja necesita exactamente lo que un humano haciendo ese trabajo: +# el repo, el store, el lab y las llaves. Lo que la jaula sigue comprando es que ese alcance esté +# ESCRITO y acotado a estos directorios — no que sea pequeño. +network = true +# `nesting`: `takana build` levanta su sandbox con bwrap ADENTRO. Sin esto, la jaula puede leer las +# recetas y no puede construir ninguna, que es justo la mitad del pedido. +nesting = true +root = true +wayland_raw = false +seal_image = false + +# El repo: es el trabajo. +[[grants.dirs]] +host = "/opt/takana" +inside = "/opt/takana" +mode = "rw" + +# El lab y el toolchain (zig, go). `.dev-fs` del repo es un symlink acá, así que se concede el +# destino real o el build no encuentra nada. +[[grants.dirs]] +host = "/work/dev-fs" +inside = "/work/dev-fs" +mode = "rw" + +# El store: sin esto no hay cache-hit, ni sellado, ni hash comparable. +[[grants.dirs]] +host = "/store" +inside = "/store" +mode = "rw" + +# La memoria del agente y sus credenciales. 733 ficheros de memoria y un `.credentials.json` de +# 509 B: sin lo primero el agente arranca sin saber nada del proyecto; sin lo segundo, no arranca. +[[grants.dirs]] +host = "/root/.claude" +inside = "/root/.claude" +mode = "rw" + +# Las llaves: el gitea (push), el worker de la granja y el Storage Box. Sólo lectura. +[[grants.dirs]] +host = "/root/.ssh" +inside = "/root/.ssh" +mode = "ro" + +# El propio CLI, que vive fuera de la imagen a propósito: la imagen es ajena y pineada, y el CLI se +# actualiza solo. Mezclarlos haría que `recreate` borrara el binario o que la imagen dejara de ser +# la que dice ser. +[[grants.dirs]] +host = "/work/claude-cli" +inside = "/work/claude-cli" +mode = "ro" + +[export]