el agente corre en la caja y puede construir — la condición que el usuario puso para borrar gioser

$ 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) <noreply@anthropic.com>
This commit is contained in:
Sergio
2026-09-17 17:34:56 +00:00
co-authored by Claude Opus 5
parent b3f0987677
commit eb96c9219e
3 changed files with 157 additions and 0 deletions
+70
View File
@@ -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 <receta>'
```
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:
+15
View File
@@ -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 <receta>'
#
# 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 "$@"
@@ -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]