SDD 28: el servidor de producción — y perfil.servidor declarando la deuda que no se ve
El frente arranca con el inventario MEDIDO de gioser, no con un plan: PID 1 es `arje-zero` (nuestro init ya gobierna un servidor real, aunque sobre userland Artix), Hetzner Cloud arranca por **BIOS** —que es justo el layout que `takana-install.sh` ya escribe— y el kernel propio trae VIRTIO/EXT4 `=y` con `netup` haciendo DHCPv4, que es exactamente el caso de una caja hcloud. Dos hallazgos que cambian el trabajo: - **La secuencia de arranque de gioser no está escrita en ningún lado.** `rc-status` dice `stopped` para caddy, gitea, sshd, cronie y act-runner y los cinco están VIVOS colgando de PID 1. Un `arje-absorb --from openrc` produciría una Semilla perfecta que no levanta el servidor. - **La mitad del Caddyfile describe un servidor que ya no existe**: 4 de 19 dominios sin DNS y sin ficheros en `/var/www`, uno en 502. Una mudanza fiel copia también la basura ⇒ la regla queda escrita: no se muda lo que no responde. `[perfil.servidor]` hereda `cli` (SDD 27 §7.1) y declara 5 raíces que existen (openssh, caddy, curl, wget, tmux) y **5 que NO** (takana, chrony, nftables, cronie, logrotate). Quedan `wanted`, y `wanted` no es `debt` ⇒ `drenaje.json` seguirá diciendo `deuda=0`. Se declaran igual: sin ellas el perfil saldría N/N —completo y verde— describiendo un servidor sin hora, sin cortafuegos y sin latido. Es la lección de `foot` en escritorio-sway, pagada por adelantado. Cuenta esperada: 5, verificada resolviendo cada nombre contra el campo `name` de las 869 recetas. La peor de esas cinco: **takana no tiene receta**. El corpus construye 869 y no la suya, así que el host no puede instalar takana desde el repo de takana — el bloqueante de que el servidor se sirva a sí mismo. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RomoxEGZUhaT4pob1QSX5x
This commit is contained in:
@@ -0,0 +1,351 @@
|
||||
# SDD 28 — El servidor de producción: instalar takana puro en una caja remota y mudarse a ella
|
||||
|
||||
Escrito 2026-09-09 a pedido del usuario («qué tan lejos estamos de montar un server takana en
|
||||
Hetzner y que sea producción, y mover allá todo lo que tengo acá, y de una vez mantenerlo por repo
|
||||
takana»). Todo número marcado *(medido)* salió de un comando corrido ese día sobre `gioser`.
|
||||
|
||||
Continúa [SDD 19](19-lanzamiento-publico.md) (lanzamiento público) y
|
||||
[SDD 27](27-perfiles-instalables-y-el-instalador.md) (metapaquetes e instalador). **No repite el
|
||||
instalador**: SDD 27 decide QUÉ se instala y con qué verbo; esto decide DÓNDE, y cómo se llega.
|
||||
|
||||
---
|
||||
|
||||
## 0. La corrección de encuadre: ya estamos en Hetzner
|
||||
|
||||
`gioser` **es** un servidor Hetzner (hcloud id `126324694`, hel1, 4 cores / 7,6 GiB) *(medido)*. La
|
||||
pregunta no era «cómo llegar a Hetzner» sino **cómo tener una caja de takana** en vez de ser un
|
||||
inquilino más de una máquina compartida que:
|
||||
|
||||
- sirve **19 dominios** por Caddy,
|
||||
- tiene `/mnt/vvv` al **89 %** (27 G libres) *(medido)*,
|
||||
- es **fija y sin backup** por pedido explícito del usuario (blindaje anti-borrado en `deadman.sh` y
|
||||
`farm-down.sh`, ver [ADR 0014](adr/0014-distribucion-multiorigen.md) y la memoria del proyecto),
|
||||
- y **no publica nada de takana**: cero entradas de takana en el Caddyfile *(medido)*.
|
||||
|
||||
### 0.1 El hallazgo que cambia el tamaño del problema
|
||||
|
||||
```
|
||||
$ ps -p 1 -o comm=,args=
|
||||
arje-zero /usr/local/sbin/arje-zero
|
||||
$ cat /proc/cmdline
|
||||
BOOT_IMAGE=/vmlinuz-linux root=UUID=54de62df-… rw net.ifnames=0 splash \
|
||||
init=/usr/local/sbin/arje-zero loglevel=4
|
||||
```
|
||||
|
||||
**El init de takana ya gobierna un servidor de producción real.** Caddy, gitea, sshd, qdrant y
|
||||
act-runner cuelgan directo de PID 1 *(medido: `ps -eo pid,ppid,comm | awk '$2==1'`)*.
|
||||
|
||||
Pero **eso no es takana puro**: es Artix con nuestro init inyectado por `init=` en el cmdline. El
|
||||
kernel, la libc y los 19 servicios son ajenos. Se probó la mitad difícil sobre userland prestado.
|
||||
|
||||
### 0.2 ⚠ La declaración del arranque no existe en ningún sitio legible
|
||||
|
||||
`rc-status` de OpenRC dice `stopped` para **caddy, gitea, sshd, cronie y act-runner**, y los cinco
|
||||
están **vivos** *(medido, los dos comandos)*. `/etc/arje/cards.d` tiene **4 tarjetas** (`acpid`,
|
||||
`openrc-openclaw`, `squid`, `zram-swap`) y ninguna de ellas los levanta.
|
||||
|
||||
⇒ **La secuencia de arranque de esta caja no está escrita.** Es lo menos reproducible de toda la
|
||||
mudanza y lo primero que hay que capturar — ver §4.
|
||||
|
||||
---
|
||||
|
||||
## 1. Inventario de lo que hay que mudar *(medido 2026-09-09)*
|
||||
|
||||
### Datos en `/mnt/vvv` (217 G de 255)
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| `tawasuyu` | **124 G** |
|
||||
| `takana` | **91 G** — incluye el store de 55 G bind-mounteado desde el otro volumen ⇒ ~36 G propios |
|
||||
| `rustup` | 29 G |
|
||||
| `act-runner` · `gioserv` · `_respaldo` | 6,5 · 5,4 · 4,1 G |
|
||||
| ~20 dirs `*-standalone` | agora, card, chasqui, cosmos, dominium, khipu, minga, mirada, nakui, nahual, pata, pineal, pluma, shuma, takiy, llimphi… |
|
||||
|
||||
### El volumen `harkaq-cosecha` (250 G, 148 G usados)
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| `store` | **55 G**, **1327 artefactos** — lo irreemplazable |
|
||||
| `work-sources` + `cargo-home` + `gopath` | ~85 G — **caché regenerable**, no se muda |
|
||||
| `work-tarballs` · `devfs-cache` · `work-out` | 3,2 G · 4,5 G · 397 M |
|
||||
|
||||
**El store no se copia por red.** Los dos volúmenes están en `hel1`; un volumen hcloud se desengancha
|
||||
de una caja y se engancha a otra en la misma ubicación. La mudanza del store son dos comandos.
|
||||
|
||||
### Servicios vivos, todos hijos de arje-zero
|
||||
|
||||
`caddy` :80/:443 · `gitea` :3002 · `sshd` :2345 · `act_runner` (el CI de tawasuyu) · `qdrant`
|
||||
:6333/:6334 · `openclaw` · `zeroclaw` · `shuma-gateway` :7378 · `tejido` :4102 y :33097 ·
|
||||
`puerta-f6e393ff` · `uvicorn` :8771 · `python3` :8770 · `squid` :1137 · `fail2ban` · `glances` ·
|
||||
`php-fpm` · `metalog` · **`adb` :5037**.
|
||||
|
||||
### Lo que no está en ningún repo
|
||||
|
||||
`/var/lib/gitea` (**2,2 G**: SQLite + los repos de git de todo, **incluido el `origin` de takana**) ·
|
||||
`/var/www` (287 M) · `/home/sergio/gioser-web/` · `/etc/caddy/Caddyfile` **+ 20 ficheros `.bak`** ·
|
||||
los certificados de letsencrypt · los crontabs · `/etc/arje/cards.d` · las 3 credenciales de
|
||||
`~/.config/hammer/*.env` · las llaves `~/.ssh/{github5,sergio}` · el token hcloud · y **la memoria
|
||||
del agente en `~/.claude/`**.
|
||||
|
||||
### 1.1 La basura, contada — porque una mudanza fiel copia también la basura
|
||||
|
||||
De los 19 dominios del Caddyfile *(probados uno por uno desde la propia caja)*:
|
||||
|
||||
| responden 200 | rotos |
|
||||
|---|---|
|
||||
| gioser.net · tawasuyu.net · git.gioser.net · summa.gioser.net · sergio.gioser.net | **api.gioser.net → 502** (backend caído) |
|
||||
| | **aura · sigma · kosmofono · terapeuta.ec → sin DNS** |
|
||||
|
||||
Y sus raíces **no existen en disco**: `/var/www` sólo tiene `terapeuta`, `goaccess`, `ogisoer`,
|
||||
`git-tawasuyu`, `letsencrypt` y un `.bak` — no hay `aura_frontend`, ni `sigma`, ni `summa/frontend`,
|
||||
ni `kosmofono`. **La mitad del Caddyfile describe un servidor que ya no existe.**
|
||||
|
||||
⇒ **Regla de la mudanza: no se muda lo que no responde.** Lo que no tiene DNS ni ficheros se anota y
|
||||
se deja morir con la caja vieja. La utilidad del §4 tiene que saber decir *declarado / vivo / fósil*.
|
||||
|
||||
---
|
||||
|
||||
## 2. La caja: qué, cuánto y por qué
|
||||
|
||||
Precios consultados a la API de hcloud el 2026-09-09 (hel1, x86, tipos no deprecados) *(medido)*:
|
||||
|
||||
| tipo | recursos | €/mes |
|
||||
|---|---|---:|
|
||||
| **`cx53`** | **16 c / 32 G / 320 G** | **29,49** |
|
||||
| `cpx41` | 8 c / 16 G / 240 G | 32,49 |
|
||||
| `ccx23` (dedicado) | 4 c / 16 G / 160 G | 85,99 |
|
||||
|
||||
**Elegido `cx53`**: es el mejor punto de la tabla y entra holgado el conjunto tawasuyu + takana +
|
||||
rustup (~244 G) si algún día se muda todo.
|
||||
|
||||
**Decisiones del usuario (2026-09-09):**
|
||||
|
||||
1. **La caja PUBLICA; el LXC prestado sigue moliendo.** `dev.gioser.net` cuesta €0 y es la máquina
|
||||
con más RAM (6 c / 16 G + 8 G swap). Separar *servir* de *compilar* mantiene la caja chica y
|
||||
barata. La caja nueva puede además compilar, con el LXC enchufado como segundo worker.
|
||||
2. **«Producción» = operativo pero privado.** Sin descarga pública todavía ⇒ **no** se activan los
|
||||
bloqueantes legales del [SDD 19 §2](19-lanzamiento-publico.md) (espejo de fuentes público por
|
||||
HTTP, marca de terceros). Lo que sí hace falta igual es la **clave de release estable** (§5).
|
||||
3. **gioser se BORRA** cuando la mudanza esté comprobada. Eso convierte esto en un experimento con
|
||||
fecha de caducidad, y cambia el diseño entero: ver §6.
|
||||
|
||||
⚠ **El blindaje anti-borrado de gioser es deliberado y hay que levantarlo a mano y a propósito.**
|
||||
`deadman.sh` y `farm-down.sh` lo protegen por lista negra de nombre **y** por ausencia del label
|
||||
`role=hammer-worker`. Ninguna automatización debe poder borrarlo: cuando llegue el día, se borra a
|
||||
mano, con la comprobación del §6 en verde y no antes.
|
||||
|
||||
---
|
||||
|
||||
## 3. Instalar takana puro en una caja remota
|
||||
|
||||
### 3.1 Lo que ya está, y por qué está más cerca de lo que parece
|
||||
|
||||
**El dato que lo destraba: Hetzner Cloud arranca por BIOS, no por EFI.** *(medido en gioser: no
|
||||
existe `/sys/firmware/efi`; GPT + GRUB; `/boot` es vfat de 1 G.)* Y `scripts/takana-install.sh`
|
||||
escribe **exactamente ese layout**: GPT + GRUB BIOS, `SeaBIOS → GRUB → kernel → arje-zero PID1`.
|
||||
El camino EFI —que fue el que más trabajo costó ([ADR 0010](adr/0010-arranque-grafo-mirada.md))— es
|
||||
el que Hetzner **no** necesita.
|
||||
|
||||
| pieza | estado *(medido)* |
|
||||
|---|---|
|
||||
| kernel con virtio | `recipes/linux.toml` compila `VIRTIO`, `VIRTIO_PCI`, `VIRTIO_BLK`, `VIRTIO_NET`, `EXT4_FS` **`=y`** ⇒ sin initramfs |
|
||||
| red | `crates/netup` = cliente **DHCPv4 propio** en Rust sobre netlink, autodetecta interfaz y espera carrier. gioser toma su IP por **DHCP en `eth0`** (`/32`, `net.ifnames=0`, DNS `185.12.64.2`) ⇒ es justo su caso |
|
||||
| acceso | `recipes/openssh.toml` sellado; el `product-rootfs` vigente **ya trae `sshd`**; `takana-install.sh` acepta `AUTHKEYS` |
|
||||
| servir | `recipes/caddy.toml` existe |
|
||||
| escritura a disco | `scripts/takana-install.sh` (no interactivo) y `scripts/takana-live-install.sh` (TUI), probado por `scripts/install-tui-test.sh` |
|
||||
|
||||
### 3.2 El camino remoto: rescue → `dd` → reboot
|
||||
|
||||
Hetzner Cloud tiene un **sistema de rescate** (Debian por SSH) que se activa desde la API sin tocar
|
||||
la caja. Desde ahí se escribe la imagen al disco y se reinicia. **No hace falta ISO, ni consola, ni
|
||||
medio físico** — que es justo lo que hace innecesario el 90 % del trabajo de ISO/USB para este caso.
|
||||
|
||||
### 3.3 Los cuatro huecos, nombrados
|
||||
|
||||
1. **`perfil.servidor`** — **HECHO hoy** en `docs/state/targets.toml`. Ver §3.4.
|
||||
2. **`console=ttyS0` en el cmdline** — el único salvavidas si el arranque falla y no hay teclado.
|
||||
Hetzner da consola web; sin serial en el kernel, esa consola no muestra nada útil.
|
||||
3. **IPv6** — `netup` hace DHCPv4 y Hetzner entrega un `/64` **estático**. La caja arrancaría con
|
||||
IPv4 sola. Aceptable para empezar, pero hay que saberlo **antes**, no descubrirlo.
|
||||
4. **Un `.img` del perfil servidor** que el rescue pueda escribir. Es `hydrate-profile.py` +
|
||||
`install-image.sh`, los dos ya existentes — ver §7.
|
||||
|
||||
### 3.4 El perfil, y la deuda que declara *(hecho 2026-09-09)*
|
||||
|
||||
`[perfil.servidor]` hereda `cli` (que hereda `base`, [SDD 27 §7.1](27-perfiles-instalables-y-el-instalador.md))
|
||||
y añade **5 raíces que existen** (`openssh`, `caddy`, `curl`, `wget`, `tmux`) y **5 que NO**:
|
||||
|
||||
| raíz que no resuelve | por qué está declarada igual |
|
||||
|---|---|
|
||||
| **`takana`** | **el propio takana no tiene receta.** El corpus construye 869 recetas y no la suya ⇒ el binario sólo existe como `cargo build` sobre un clon. Es EL bloqueante del §5 |
|
||||
| `chrony` | sin NTP, un reloj que deriva rompe TLS y las firmas — y falla diciendo «firma inválida», no «la hora» |
|
||||
| `nftables` | `dev.gioser.net` está hoy expuesto con fuerza bruta a root en el journal; una caja nueva no debería nacer así |
|
||||
| `cronie` | el latido son tres líneas de crontab, y sin systemd no hay timers: o hay cron, o el latido es una tarjeta de arje-zero. **Decisión pendiente** |
|
||||
| `logrotate` | en una caja que sirve, el log de acceso crece hasta llenar el disco |
|
||||
|
||||
Quedan **`wanted`** en el grafo, y **`wanted` no es `debt`** ⇒ `drenaje.json` seguirá diciendo
|
||||
`deuda=0`. Se declaran igual porque **una deuda escrita se ve y una omitida no**: sin ellas el perfil
|
||||
saldría N/N —completo y verde— describiendo un servidor sin hora, sin cortafuegos y sin latido. Es
|
||||
la lección de `foot` en `escritorio-sway` (121/121 sellado y sin emulador de terminal), pagada por
|
||||
adelantado. **Cuenta esperada: 5.** Un sexto `wanted` es un error de tipeo, no una receta nueva.
|
||||
|
||||
---
|
||||
|
||||
## 4. La utilidad de migración: absorber lo VIVO, no lo declarado
|
||||
|
||||
`arje-absorb` ya existe, está sellado (`recipes/arje-absorb.toml`) y su `--help` dice: lee
|
||||
`sysvinit | runit | dinit | openrc | auto` y **emite una Tarjeta Semilla** con cada servicio del init
|
||||
ajeno como hija de arje-zero. Tiene parsers para los cinco (`systemd.rs`, `openrc.rs`, `runit.rs`,
|
||||
`dinit.rs`, `sysvinit.rs`).
|
||||
|
||||
**Pero lee la declaración, y en gioser la declaración miente** (§0.2). Un `arje-absorb --from openrc`
|
||||
sobre esta caja produciría una Semilla que **no levanta el servidor**, y lo haría en silencio: la
|
||||
Semilla sería sintácticamente perfecta.
|
||||
|
||||
⇒ Lo que falta es un **segundo lector**: absorber del **proceso vivo** (`/proc`: `ppid==1`, `cmdline`,
|
||||
`cwd`, entorno, fds en LISTEN) y **reconciliar** ambas lecturas marcando dónde discrepan. Eso es lo
|
||||
que el usuario nombró como «lo aprendido de matilda» — absorber lo que hay, no lo que está escrito.
|
||||
|
||||
La utilidad debe clasificar en tres, no en dos:
|
||||
|
||||
| clase | criterio | qué se hace |
|
||||
|---|---|---|
|
||||
| **vivo** | proceso corriendo **y** declarado | se muda |
|
||||
| **declarado, muerto** | en la config, sin proceso ni ficheros (los 4 dominios sin DNS) | **fósil: se anota y se deja morir** |
|
||||
| **vivo, no declarado** | proceso con `ppid==1` que ningún init declara (caddy, gitea, sshd, act-runner) | **se escribe la tarjeta que faltaba** — es el trabajo real |
|
||||
|
||||
**Se construye al final**, estrenándose con la migración gioser → caja nueva, con el caso del
|
||||
`rc-status` que miente como test de aceptación. Su especificación es el §1 de este documento.
|
||||
|
||||
---
|
||||
|
||||
## 5. Que el servidor sirva sus propios paquetes a su propio host
|
||||
|
||||
Existe el camino entero: `scripts/build-repo.sh` (corpus → `.swm` firmados + índice),
|
||||
`takana install --repo` que **ya acepta lista HTTP multi-origen** y verifica cada paquete contra el
|
||||
`digest` del índice firmado ([ADR 0014](adr/0014-distribucion-multiorigen.md)), `takana upgrade` con
|
||||
rollback, y caddy para servir `dist/repo` con un `file_server`.
|
||||
|
||||
Lo que falta **no es servir**, son dos cosas:
|
||||
|
||||
1. **La clave.** `build-repo.sh` firma hoy con una **clave efímera** por defecto (`keygen release` si
|
||||
no se le pasa `KEY=`) *(medido, línea 33)*. Sin clave estable, «firmado» no significa nada. Es el
|
||||
[SDD 19 §3.2](19-lanzamiento-publico.md) y es requisito aunque el repo sea privado.
|
||||
2. **La receta de `takana`** (§3.4). El host necesita `takana` instalado para consumir los paquetes,
|
||||
y hoy no puede instalarlo desde el repo porque takana no está en el repo.
|
||||
|
||||
⚠ **Y una caja que se sirve a sí misma es un solo origen** — justo lo que el ADR 0014 existe para no
|
||||
tener. El camino normal puede ser el local, pero la lista de orígenes del host debe incluir también
|
||||
el Storage Box o gioser. Si no, el primer origen roto es el último.
|
||||
|
||||
**Lo que esto vale**: cerrar el bucle es el ensayo de «actualización en sitio probada de verdad»
|
||||
([SDD 19 §5.2](19-lanzamiento-publico.md)), hecho sobre una máquina que importa pero que **no es el
|
||||
laptop de nadie**.
|
||||
|
||||
---
|
||||
|
||||
## 6. La mudanza como experimento: el criterio de borrado
|
||||
|
||||
El usuario decidió que **gioser se borra** al final. Eso no es un detalle de calendario: es lo que
|
||||
convierte la mudanza en un experimento con criterio de aceptación, y hay que escribirlo **antes**.
|
||||
|
||||
**Regla base**: un ausente falla ruidosamente; un vacío llega hasta el final diciendo que todo fue
|
||||
bien. Aplicado acá: *«lo copié»* no es prueba de nada. **La prueba es que el servicio responde en la
|
||||
caja nueva y que el dato se verifica por contenido.**
|
||||
|
||||
### 6.1 Las puertas, en orden. Ninguna se salta.
|
||||
|
||||
| # | puerta | cómo se comprueba |
|
||||
|---|---|---|
|
||||
| 1 | la caja nueva arranca **takana puro** y se entra por SSH | `ssh` a la IP nueva tras el reboot del rescue |
|
||||
| 2 | el **store** está entero | `1327` artefactos **y ninguno vacío** — `respaldo-storagebox.sh --listar` separa los vacíos; un directorio vacío **no** es un artefacto |
|
||||
| 3 | los **grafos** salen idénticos | `build-state.py` en las dos máquinas ⇒ los cuatro grafos **byte a byte**. Es la prueba de que un hub está bien montado, no que «funcione» |
|
||||
| 4 | la **granja** late en la caja nueva | una cosecha completa con el LXC enchufado por `.fleet` y artefactos que vuelven |
|
||||
| 5 | el **repo** se sirve y el host se actualiza de sí mismo | §5 en verde, con la clave estable |
|
||||
| 6 | cada dominio **vivo** responde 200 desde fuera | la tabla del §1.1, repetida contra la IP nueva |
|
||||
| 7 | el **respaldo** al Storage Box corre desde la caja nueva | una corrida completa, no un `--seco` |
|
||||
| 8 | lo **fósil** está anotado y decidido | lista del §1.1 con «muere» o «revive» al lado de cada uno |
|
||||
|
||||
⚠ **Puerta 3 tiene una trampa conocida**: `sealed_remoto` dependía de la máquina (laptop 700, gioser
|
||||
751 sobre el MISMO corpus) y por eso se sacó del JSON. Si los grafos difieren, mirar primero si la
|
||||
diferencia es «cuánto de esto tengo en disco» y no «qué existe».
|
||||
|
||||
### 6.2 Lo que la mudanza tiene que producir, además de la mudanza
|
||||
|
||||
El usuario lo pidió explícito: **que este experimento saque recetas y las pruebe**. La caja vieja es
|
||||
un catálogo de software real en producción, y cada servicio que hoy corre sobre binarios de Artix es
|
||||
una receta candidata con un caso de uso comprobado detrás:
|
||||
|
||||
| lo que corre hoy en Artix | receta |
|
||||
|---|---|
|
||||
| `caddy` | ✅ existe (`recipes/caddy.toml`) |
|
||||
| `openssh` | ✅ existe |
|
||||
| `gitea` | ✗ — y es la que más peso tiene: aloja el `origin` de takana |
|
||||
| `qdrant`, `squid`, `fail2ban`, `php-fpm`, `metalog`, `glances` | ✗ |
|
||||
| `chrony`, `nftables`, `cronie`, `logrotate` | ✗ — ya declaradas `wanted` en `perfil.servidor` |
|
||||
|
||||
**Cada una que se cierre se prueba sola**: el servicio o levanta en la caja nueva o no, y eso se ve
|
||||
el mismo día. Es la diferencia entre una receta «sellada» y una receta *usada*.
|
||||
|
||||
---
|
||||
|
||||
## 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:
|
||||
|
||||
| para | script existente | qué le falta |
|
||||
|---|---|---|
|
||||
| armar el rootfs de un perfil | `scripts/hydrate-profile.py` | nada — es el camino bueno (los `hydrate-*.sh` por escritorio **divergen** de `targets.toml`) |
|
||||
| producir el `.img` | `scripts/install-image.sh` / `scripts/product-image.sh` | apuntar al `perfil.servidor` |
|
||||
| escribir a disco | `scripts/takana-install.sh` | el envoltorio de rescue del §3.2 |
|
||||
| instalador con TUI | `scripts/takana-live-install.sh` | ser el **mismo motor** que el remoto, con las respuestas por fichero |
|
||||
| provisionar un worker | `scripts/farm/vps-setup.sh` | ya soporta apt **y** dnf; le falta la rama «takana puro» (sin gestor de paquetes ajeno) |
|
||||
| enchufar a la granja | `scripts/farm/.fleet` | **está en `.gitignore`** ⇒ un hub recién clonado nace con la flota vacía **en silencio** |
|
||||
| latido | `scripts/farm/cosecha-cron.sh` | decidir cron vs tarjeta de arje (§3.4) |
|
||||
| respaldo | `scripts/respaldo-storagebox.sh` | nada — es interrumpible y continuable |
|
||||
| absorber el init ajeno | `arje-absorb` | el lector de procesos vivos (§4) |
|
||||
|
||||
⚠ **Un motor, tres frentes.** La tentación es escribir el instalador remoto aparte del TUI. Ahí es
|
||||
donde divergen: ya pasó con los `hydrate-*.sh` contra `targets.toml`, y con la unidad systemd del
|
||||
worker que quedó vieja mientras el repo estaba al día. La instalación remota tiene que ser **la misma
|
||||
que hace la persona**, grabada en un fichero de respuestas.
|
||||
|
||||
---
|
||||
|
||||
## 8. `churay` como instalador centralizado — la dirección, no la decisión
|
||||
|
||||
El usuario lo planteó el 2026-09-09: **un instalador único, con CLI y TUI**, que sirva para
|
||||
|
||||
- las **apps de tawasuyu**,
|
||||
- **paquetes sueltos en cualquier distro**, al estilo nix (instalar sin ser root, sin tocar el
|
||||
sistema anfitrión),
|
||||
- **takana** mismo,
|
||||
- y **`llimphi`**.
|
||||
|
||||
Encaja con lo que ya hay y por eso vale escribirlo: `takana install` ya resuelve por nombre contra un
|
||||
índice firmado y **ya acepta múltiples orígenes**; `qorpa` ([ADR 0015](adr/0015-imagenes-ajenas.md))
|
||||
ya corre imágenes ajenas enjauladas sobre el mismo kernel; y el vigía de espejos de churay ya corre
|
||||
en cron desde gioser.
|
||||
|
||||
**No se decide acá.** Es un ADR propio, y tiene al menos tres preguntas abiertas antes de escribir
|
||||
una línea: (1) si el instalador «en cualquier distro» hidrata binarios o reproduce desde fuente —la
|
||||
misma pregunta del [SDD 27 §4](27-perfiles-instalables-y-el-instalador.md), y la respuesta puede no
|
||||
ser la misma fuera de takana; (2) quién es el dueño del verbo, si `takana install` crece o si
|
||||
`churay` lo envuelve; (3) qué pasa con un host glibc, que es exactamente lo que el ADR 0015 aparta
|
||||
del store a propósito. Queda anotado para que se **decida**, no para que se descubra a mitad.
|
||||
|
||||
---
|
||||
|
||||
## 9. Estado y orden
|
||||
|
||||
| paso | estado |
|
||||
|---|---|
|
||||
| **0.** `perfil.servidor` + este documento + el renombre `hammer-*` de los instaladores | ✅ **2026-09-09** |
|
||||
| **1.** caja `cx53` de sacrificio: rescue → `dd` → arranca takana puro (§3) | pendiente |
|
||||
| **2.** el servidor se sirve sus paquetes y se actualiza a sí mismo (§5) | pendiente — requiere clave estable + receta de `takana` |
|
||||
| **3.** mudanza con las 8 puertas del §6, y recetas nuevas del §6.2 | pendiente |
|
||||
| **4.** la utilidad de absorción (§4), estrenándose con esta migración | pendiente |
|
||||
| **5.** `churay` como instalador centralizado (§8) | ADR propio, sin decidir |
|
||||
|
||||
**El volumen del store NO se engancha a la caja nueva hasta que pase la puerta 1.** Una caja de
|
||||
sacrificio con el store dentro deja de ser de sacrificio.
|
||||
@@ -864,3 +864,62 @@ paquetes = [
|
||||
# pedirlo es promover uno al corpus. Queda escrito para que se elija, no para que se descubra.
|
||||
"hicolor-icon-theme",
|
||||
]
|
||||
|
||||
[perfil.servidor]
|
||||
descripcion = "la caja que SIRVE: sshd, servidor web y las herramientas de operar una máquina remota sin pantalla"
|
||||
hereda = ["cli"]
|
||||
# Nace el 2026-09-09 del frente «servidor de producción» (SDD 27). Existe porque las cuatro cosas que
|
||||
# ese frente quiere —instalar takana puro en una caja remota, servir el repo, que la caja se actualice
|
||||
# a sí misma, y migrar gioser— empiezan todas por la misma pregunta: QUÉ se instala. Hasta hoy eso no
|
||||
# estaba declarado en ningún lado. El `sshd` del producto, por ejemplo, entra por
|
||||
# `product-userland-from-repo.sh` y NO por este manifiesto: dos listas para lo mismo y una sola
|
||||
# versionada como destino, que es exactamente lo que este fichero existe para no tener.
|
||||
#
|
||||
# ⚠ LA LECCIÓN DE `foot`, APLICADA ANTES DE PAGARLA: `escritorio-sway` daba 121/121 sellado y era
|
||||
# inusable porque a la declaración le faltaba el emulador de terminal. La métrica mide la clausura de
|
||||
# lo DECLARADO y no puede ver lo que falta en la declaración. Un servidor tiene su propia versión de
|
||||
# ese agujero, y no es el software de servir: es la HORA (sin reloj, TLS y las firmas fallan), el
|
||||
# CORTAFUEGOS y el DISPARADOR PERIÓDICO. Por eso van declarados abajo aunque todavía no existan.
|
||||
#
|
||||
# ⚠ LAS CUATRO ÚLTIMAS RAÍCES NO RESUELVEN A NINGUNA RECETA — a propósito, y hay que saberlo.
|
||||
# Quedan `wanted` en el grafo, y `wanted` NO es `debt`: `drenaje.json` seguirá diciendo `deuda=0`
|
||||
# ([[gnome-sin-fuentes]] trampa 1). Se declaran igual porque una deuda escrita y contada se ve, y una
|
||||
# omitida no: un `perfil.servidor` sin ellas saldría N/N —completo y verde— describiendo un servidor
|
||||
# sin hora, sin cortafuegos y sin latido. Cuenta esperada al escribirlas: **5 `wanted`**. Si al mirar
|
||||
# `build-state.json` aparece un sexto, es un error de tipeo en un nombre, no una receta nueva.
|
||||
paquetes = [
|
||||
# ── lo que existe y está sellado ──────────────────────────────────────────────────────────────
|
||||
# `openssh`: el acceso. Es la raíz que convierte «la caja arrancó» en «puedo entrar»; sin ella una
|
||||
# instalación remota deja una máquina viva e inalcanzable, que es peor que una que no arrancó.
|
||||
"openssh",
|
||||
# `caddy`: sirve `dist/repo` (índice firmado + los `.tkn`/`.swm`). TLS automático, un binario, cero
|
||||
# dependencias — y es el mismo que ya sirve los 19 dominios de gioser, así que la migración no
|
||||
# cambia de servidor web además de cambiar de sistema.
|
||||
"caddy",
|
||||
# `curl`/`wget`: bajar del mirror y del repo. `takana install --repo https://…` los necesita del
|
||||
# lado del cliente, y son también la única forma de diagnosticar un origen caído desde la caja.
|
||||
"curl", "wget",
|
||||
# `tmux`: un build largo por SSH que muere con la sesión es un build perdido. En una caja sin
|
||||
# pantalla es infraestructura, no comodidad.
|
||||
"tmux",
|
||||
|
||||
# ── declarado y NO existe todavía: la deuda del perfil, contada ───────────────────────────────
|
||||
# `takana`: el propio takana NO tiene receta. El corpus construye 869 recetas y no la suya, así que
|
||||
# hoy el binario sólo existe como `cargo build --release` sobre un clon del repo. Es EL bloqueante
|
||||
# de «que el servidor sirva sus paquetes a su propio host»: el host necesita `takana` instalado para
|
||||
# consumirlos, y no puede instalarlo desde el repo porque no está en el repo.
|
||||
"takana",
|
||||
# `chrony`: la hora. Sin NTP, un reloj que deriva rompe la validación de certificados TLS y la de
|
||||
# firmas con expiración — y falla con errores que no dicen «la hora», dicen «firma inválida».
|
||||
"chrony",
|
||||
# `nftables`: el cortafuegos. `dev.gioser.net` está hoy expuesto a internet con fuerza bruta a root
|
||||
# en el journal; una caja nueva no debería nacer así.
|
||||
"nftables",
|
||||
# `cronie`: el disparador periódico. El latido de la granja son tres líneas de crontab, y en una
|
||||
# distro sin systemd no hay timers: o hay cron, o el latido se vuelve una tarjeta de arje-zero. Es
|
||||
# una decisión pendiente, y se declara para que se decida en vez de descubrirse.
|
||||
"cronie",
|
||||
# `logrotate`: en una caja que sirve, el log de acceso crece hasta llenar el disco. Caddy rota el
|
||||
# suyo por configuración, pero nada más lo hace.
|
||||
"logrotate",
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user