tawasuyu

sergio/takana

> Una distribución Linux construida con un laboratorio funcional y hermético en el > sótano, y una terminal mutable, clásica y anárquica en el piso de arriba — diseñada > desde el suelo para que una IA programadora entienda, modifique y co…

TOML 1411Shell 141Markdown 85Rust 842918 Definitions

CUTOVER de `sergio.gioser.net`: el último vhost sale de gioser — ya no queda ninguno

`https://sergio.gioser.net/` y `/shuma/` contestan 200 contra 2.29.29.217, con TLS público.
**Ningún dominio resuelve ya a gioser.**

⚠ Y el 200 de `/shuma/` había que mirarlo dos veces: el bloque termina en `try_files … /index.html`,
así que CUALQUIER ruta inexistente devuelve 200 con la SPA — un `curl -o /dev/null` habría dado el
mismo verde con el proxy mal puesto. Lo que decide es el cuerpo (17 bytes: «shuma-gateway ok») y el
control es compararlo con lo que el origen viejo sigue sirviendo. Igual con `/shuma/rpc`: las dos
máquinas contestan el mismo 400 con el mismo JSON.

Lo construido, en orden: las dos recetas sellaron en el worker con los hashes anticipados y salen
`statically linked` (nada de cargador, que era lo que le faltaba al binario glibc de gioser) · la
cuenta `shuma` declarada con `[[user]]`, uid 967, y el descenso con `setuidgid` en el argv de la
Card porque el payload Native de arje NO tiene campo de usuario · la config que no se regenera
(`gateway-token`, `identity.x25519`) instalada, y la guarda de la Card la EXIGE en vez de crearla:
un token nuevo deja fuera a los clientes ya emparejados · las dos Cards en `cards.d` Y en el genesis
de la semilla · las dos recetas y los dos labels declarados en `perfil.servidor`.

⚠ LA SHELL DE LA CUENTA NO ES UN DETALLE: el default de `[[user]]` es `/bin/false` —correcto para
gitea o squid, exactamente lo contrario para un demonio que abre PTYs—. Con la shell inerte el
servicio arranca, se supervisa, contesta 200 y cada pestaña muere al instante: un fallo que no se ve
al desplegar, se ve al usarlo. Va `shell = "/bin/sh"`.

`[[user]]` y `[[service]]` están fuera de `hash_inputs`: declarar todo eso no movió ningún
ArtifactHash, comprobado antes y después en los dos.

Y una corrección al §6.25: para cambiar el VALOR de un rrset no hace falta DELETE+POST (que deja una
ventana sin registro). La API tiene
`POST /v1/zones/<id>/rrsets/<n>/<t>/actions/set_records`, que es atómica. Con TTL 60 la resolución
pública cambió en menos de 8 segundos.

El origen de gioser sigue corriendo a propósito, como en §6.26: volver atrás es una llamada.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

f32b28c63e0f0b95a21cb1a7bdebbfea84712a45 · by Sergio · (1 month ago) · parent 15157f8cf · browse the tree at this commit

Semantic delta

No definition changed: this change is text, documentation or data.

4 files · +135 −1

diff --git a/docs/28-servidor-de-produccion.md b/docs/28-servidor-de-produccion.md
index 43df6e35..cc601bc0 100644
--- a/docs/28-servidor-de-produccion.md
+++ b/docs/28-servidor-de-produccion.md
@@ -2153,6 +2153,73 @@ por criterio ajeno.
 De `/var/lib` (8,1 G) se movió sólo el estado de los daemons que viven: **6,1 G son swap** y 1,9 G
 el gitea, que ya está mudado desde el §6.14.
 
+### 6.37 ✅ CUTOVER DE `sergio.gioser.net` — el último vhost, y gioser deja de servir *(2026-09-16)*
+
+**Ya no queda ningún dominio sirviéndose desde gioser.** El último era éste, y lo que lo ataba no
+era el sitio sino su consola: `/shuma/*`. Verificado desde fuera, contra la IP nueva:
+
+```
+  https://sergio.gioser.net/           → 200   408 bytes, la SPA
+  https://sergio.gioser.net/shuma/     → 200   «shuma-gateway ok»   ← el gateway, no la SPA
+  https://sergio.gioser.net/ruta-inventada → 200  la SPA (el fallback, que es lo correcto)
+```
+
+⚠ **El `200` de `/shuma/` había que mirarlo dos veces.** El bloque termina en
+`try_files {path} {path}/ /index.html`, así que **cualquier ruta inexistente devuelve 200 con la
+SPA**: un `curl -o /dev/null` que sólo mira el código habría dado el mismo verde con el proxy mal
+puesto. Lo que decide es el CUERPO — 17 bytes que dicen `shuma-gateway ok` — y el control es
+compararlo con lo que el origen viejo sigue sirviendo, byte por byte. Igual con `/shuma/rpc`: las
+dos máquinas contestan el MISMO `400 {"error":"bad json: …"}`.
+
+#### Lo que hubo que construir, en orden
+
+1. **Las dos recetas** (§6.34) sellaron en el worker: `b3:107dfb54…-shuma-gateway` (6,3 M) y
+   `b3:f9b92acc…-shuma-daemon` (5,6 M) — los hashes que `takana hash` había anticipado antes de
+   encolarlas. `file` dice **`statically linked`**: nada de cargador, que era exactamente lo que le
+   faltaba al binario glibc de gioser (y cuyo inodo, además, ya estaba borrado del disco).
+2. **La cuenta, declarada y sin privilegio.** `[[user]] shuma` (uid 967) en la receta del daemon, y
+   el descenso con `setuidgid` dentro del `argv` de la Card, igual que gitea — porque el payload
+   `Native` de arje **no tiene campo de usuario**. Declararlo «como estaba» lo habría puesto de
+   root, y esto es un demonio que abre PTYs y lanza shells.
+3. **⚠ Y la shell de la cuenta NO es un detalle.** El default de `[[user]]` es `/bin/false`, que es
+   lo correcto para gitea o squid y **exactamente lo contrario** para éste: con la shell inerte el
+   servicio arranca, se supervisa, contesta 200… y cada pestaña que alguien abra muere al instante.
+   Un fallo que no se ve al desplegar: se ve al usarlo. Va `shell = "/bin/sh"`.
+4. **La config que no se regenera.** El `gateway-token` (32 B) y `keys/identity.x25519` viajaron a
+   `/var/lib/shuma/.config/shuma/`: generar unos nuevos deja fuera a los clientes ya emparejados.
+   Por eso la guarda de la Card **exige** el token y no lo crea, y dice de dónde sale.
+5. **Las dos Cards** entran en `/etc/arje/cards.d/` **y** en el `genesis` de `/ente/seed.card.json`
+   (son dos preguntas distintas: qué se puede encarnar a pedido y qué arranca solo). La semilla
+   quedó con 13 entradas y vuelve a parsear.
+6. **Declarado en `perfil.servidor`**: los dos paquetes y los dos labels en `servicios`. Entran como
+   PAR — el gateway sin el daemon es un puente a ninguna parte.
+
+`[[user]]` y `[[service]]` están **fuera de `hash_inputs`**, así que declarar todo eso no movió
+ningún ArtifactHash: comprobado en los dos, antes y después.
+
+#### 🔁 El DNS: la API tiene una acción para esto y el §6.25 no la había encontrado
+
+Aquel cutover dejó escrito «un `PUT` sobre el rrset no puede cambiar el tipo: hay que `DELETE` del
+CNAME y `POST` del A». Es cierto para un cambio de TIPO. Para cambiar el VALOR, el `PUT` tampoco
+sirve —`can't update records with this endpoint`— pero existe una acción que lo hace **atómica**:
+
+```
+POST /v1/zones/<id>/rrsets/<nombre>/<tipo>/actions/set_records   {"records":[{"value":"…"}]}
+```
+
+Sin ventana sin registro, que es lo que sí tiene el `DELETE`+`POST`. Con TTL 60 la resolución
+pública cambió **en menos de 8 segundos**, y volver atrás es la misma llamada con la IP vieja.
+
+#### El origen sigue vivo, a propósito
+
+Como en §6.26: en gioser `shuma-daemon`, `shuma-gateway` y su bloque de caddy **siguen corriendo**.
+Mudar el nombre y dejar el origen encendido permite volver en un minuto, y no cuesta nada mientras
+la máquina exista. Lo que ya no existe es la dependencia: **ningún dominio resuelve a gioser**.
+
+⚠ Pendiente menor, anotado donde se va a leer: el daemon avisa al arrancar que no encuentra
+`shuma-askpass` («sudo/ssh pedirán la clave por el TTY»). Es otro crate del mismo workspace y es
+otra receta — no bloquea la consola.
+
 ## 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/docs/state/targets.toml b/docs/state/targets.toml
index 43764965..c889dd1f 100644
--- a/docs/state/targets.toml
+++ b/docs/state/targets.toml
@@ -1325,6 +1325,10 @@ paquetes = [
   # copiar un directorio — lo que NO es trivial es el binario, ver el encabezado de `recipes/gitea.toml`.
   # Su `[[service]]` está en la receta y el perfil lo arranca (abajo, en `servicios`).
   "gitea",
+  # `shuma-daemon` / `shuma-gateway` (2026-09-16): la consola remota que sirve `/shuma/*` en
+  # `sergio.gioser.net`. Construidos desde el monorepo de tawasuyu por decisión del usuario, en vez
+  # de mudar el binario glibc de gioser —cuyo inodo, además, ya estaba BORRADO del disco.
+  "shuma-daemon", "shuma-gateway",
   # `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",
@@ -1428,4 +1432,10 @@ paquetes = [
 # métrica reclama: el latido y la hora. Sus paquetes ya estaban declarados arriba y sellados —lo que
 # faltaba era el eslabón que los ARRANCA, que es este. Una caja con `cronie` instalado y sin `crond`
 # corriendo se ve idéntica a una con latido, y no rota un log ni respalda nada.
-servicios = ["sshd", "gitea", "caddy", "minga", "squid", "crond", "chronyd"]
+# ⚠ `shuma-daemon` y `shuma-gateway` (2026-09-16): la consola de `sergio.gioser.net`, que es el
+# ÚLTIMO vhost que quedaba atado a gioser (SDD 28 §6.34). Entran como PAR: el gateway sin el daemon
+# es un puente a ninguna parte, y por eso los dos labels van juntos o no va ninguno.
+# La cuenta `shuma` la declara `recipes/shuma-daemon.toml` con `[[user]]`, sin privilegio y con
+# `shell = /bin/sh` —no `/bin/false`—, porque este demonio existe para abrir PTYs: con la shell
+# inerte el servicio arranca, se supervisa, y cada pestaña muere al instante.
+servicios = ["sshd", "gitea", "caddy", "minga", "squid", "crond", "chronyd", "shuma-daemon", "shuma-gateway"]
diff --git a/recipes/shuma-daemon.toml b/recipes/shuma-daemon.toml
index f77d7263..a91d81b0 100644
--- a/recipes/shuma-daemon.toml
+++ b/recipes/shuma-daemon.toml
@@ -24,3 +24,39 @@ compiler = "zig-cc"
 target   = "x86_64-linux-musl"
 link     = "static"
 flags    = ["-p", "shuma-daemon", "--bin", "shuma-daemon"]
+
+# ── LA CUENTA (SDD 30) ──────────────────────────────────────────────────────────────────────────
+# Fuera de `hash_inputs`: declarar esto NO re-hashea shuma-daemon (control corrido, el hash no se
+# movió).
+#
+# ⚠ EN GIOSER CORRE COMO `sergio`, Y ESO NO SE PUEDE TRADUCIR SOLO. El payload `Native` de arje no
+# tiene campo de usuario (SDD 29 §4.0 quinquies), así que una tarjeta escrita «como estaba» lo
+# pondría de ROOT — y esto es un demonio que abre PTYs y ejecuta shells: correrlo de root no es
+# perder una opción, es entregar la máquina. La cuenta es propia y sin privilegio, y el descenso se
+# hace en el comando con `setuidgid`, igual que gitea.
+[[user]]
+name = "shuma"
+uid  = 967
+home = "/var/lib/shuma"
+# ⚠ Y LA SHELL NO ES UN DETALLE ACÁ. El default de `[[user]]` es `/bin/false`, que es lo correcto
+# para un demonio (gitea, squid) y **lo exactamente contrario** para éste: shuma-daemon existe para
+# abrir PTYs y lanzar shells. Con `/bin/false` el servicio arranca, se supervisa, contesta… y cada
+# pestaña que alguien abra muere al instante. Un fallo que no se ve al desplegar: se ve al usarlo.
+shell = "/bin/sh"
+
+# ── EL SERVICIO ─────────────────────────────────────────────────────────────────────────────────
+# Las guardas salen 78 NOMBRANDO EL ARREGLO. Es lo que hace la diferencia entre un servicio que
+# reintenta para siempre con «unknown user» y uno que dice qué falta: medido con gitea, cuya imagen
+# arrancaba perfecta y sin el servicio por el que existe.
+#
+# `XDG_RUNTIME_DIR` no es decoración: es donde el daemon pone su socket, y es por ese socket que el
+# gateway le habla. En gioser vale `/run/shuma`, medido en el entorno del proceso vivo.
+[[service]]
+label      = "shuma-daemon"
+id         = "01M2EKDA00SH0MADAEM0N7X4K2"
+exec       = "/bin/busybox"
+argv = ["sh", "-c", "/bin/grep -q '^shuma:' /etc/passwd || { echo 'shuma-daemon: no existe el usuario `shuma` en /etc/passwd — la receta lo declara con [[user]]; la imagen lo compone con `takana users`' >&2; exit 78; }; mkdir -p /run/shuma /var/lib/shuma || exit 78; chown shuma:shuma /run/shuma /var/lib/shuma || exit 78; cd /var/lib/shuma || exit 78; exec /usr/bin/setuidgid shuma /usr/bin/shuma-daemon"]
+envp       = [["PATH", "/usr/bin:/bin"], ["HOME", "/var/lib/shuma"], ["USER", "shuma"], ["XDG_RUNTIME_DIR", "/run/shuma"]]
+networking = "full"
+cgroup     = "arje.slice/shuma-daemon"
+restart    = { initial_ms = 1000, max_ms = 30000 }
diff --git a/recipes/shuma-gateway.toml b/recipes/shuma-gateway.toml
index 39f85cd1..efd2dbb8 100644
--- a/recipes/shuma-gateway.toml
+++ b/recipes/shuma-gateway.toml
@@ -32,3 +32,24 @@ target   = "x86_64-linux-musl"
 # exactamente lo que le falta hoy al binario de gioser, que es ELF dinámico contra glibc.
 link     = "static"
 flags    = ["-p", "shuma-gateway", "--bin", "shuma-gateway"]
+
+# ── EL SERVICIO (SDD 30) ────────────────────────────────────────────────────────────────────────
+# Fuera de `hash_inputs`: declarar esto NO re-hashea el gateway.
+#
+# La cuenta la declara `shuma-daemon` —es la misma— y por eso acá no va otro `[[user]]`: dos recetas
+# declarando la misma cuenta con distinto uid abortan la imagen a propósito (`takana users --merge`).
+#
+# ⚠ DOS GUARDAS, Y LAS DOS SALEN 78 DICIENDO EL ARREGLO:
+#   1. sin el usuario, esto correría de root — y es el puente HTTP hacia un demonio que abre shells;
+#   2. sin `gateway-token` no autentica a nadie. El fichero son 32 bytes que en gioser viven en
+#      `~/.config/shuma/gateway-token`; si se genera uno nuevo, los clientes ya emparejados dejan de
+#      entrar. Por eso la guarda no lo crea: lo EXIGE y nombra de dónde sale.
+[[service]]
+label      = "shuma-gateway"
+id         = "01M2EKDA00SH0MAGATEWAY4K21"
+exec       = "/bin/busybox"
+argv = ["sh", "-c", "/bin/grep -q '^shuma:' /etc/passwd || { echo 'shuma-gateway: falta el usuario `shuma` — lo declara recipes/shuma-daemon.toml' >&2; exit 78; }; test -s /var/lib/shuma/.config/shuma/gateway-token || { echo 'shuma-gateway: falta /var/lib/shuma/.config/shuma/gateway-token (32 B) — viene de ~/.config/shuma/gateway-token del origen; si generás uno nuevo, los clientes emparejados dejan de entrar' >&2; exit 78; }; cd /var/lib/shuma || exit 78; exec /usr/bin/setuidgid shuma /usr/bin/shuma-gateway"]
+envp       = [["PATH", "/usr/bin:/bin"], ["HOME", "/var/lib/shuma"], ["USER", "shuma"], ["XDG_RUNTIME_DIR", "/run/shuma"]]
+networking = "full"
+cgroup     = "arje.slice/shuma-gateway"
+restart    = { initial_ms = 1000, max_ms = 30000 }