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 1182Shell 87Rust 63Markdown 541592 Definiciones

CUTOVER: el gitea sirve desde la caja nueva — TLS válido, HTTPS y SSH, supervisado por arje

`git.gioser.net` y `git.tawasuyu.net` responden **200 con TLS válido desde 2.29.29.217**, `git clone`
funciona por HTTPS **y** por SSH:2345, y `gitea` + `caddy` corren supervisados por `arje-zero`. El
gitea de gioser está parado. Es el primer servicio real que deja la máquina que se va a borrar.

Mudanza INCREMENTAL: los DNS de gioser son casi todos `CNAME → www`, así que mover `www` habría
mudado quince dominios cuyos backends siguen allá. Se convirtió sólo `git` (las dos zonas) de CNAME
a A propio con TTL 60 — reversible en un minuto.

Cinco cosas que sólo se aprenden haciéndolo:

· **Parar el origen no es `rc-service gitea stop`**: dice «already stopped» con el proceso vivo, y
  matarlo no alcanza — lo revive arje-zero, que lo tiene como card `openrc-gitea` con Restart y
  **9001 reinicios** en el contador. Se paró con `arjectl stop openrc-gitea` (el arjectl que
  construimos hoy, hablando con el arje de gioser), y para eso hubo que extraer su card del genesis
  y escribirla en `cards.d` — que es justo el hueco del §6.12.
· **El token de `hcloud` también gestiona el DNS** (Cloud API unificada, `/v1/zones`); la API vieja
  `dns.hetzner.com/api/v1` redirige a la consola web. Y un `PUT` sobre el rrset no puede cambiar el
  TIPO: hay que DELETE del CNAME y POST del A.
· **ACME falló primero contra gioser** (502) porque el challenge salió antes de que propagara el
  DNS. Con el DNS al día, `arjectl restart caddy` → certificate obtained successfully.
· **La identidad SSH se muda con el servicio**: `REMOTE HOST IDENTIFICATION HAS CHANGED` hasta que
  se copiaron las claves de host de gioser a la caja. Así los clones existentes no notan nada; el
  precio es limpiar el known_hosts propio del `:22` de administración.
· El `sshd` del producto escucha sólo en `:22` ⇒ el git por SSH necesitó `Port 2345` y que el
  usuario `gitea` tenga shell real (su authorized_keys fuerza `command="gitea serv …"`).

`caddy` gana su `[[service]]` (con guarda del Caddyfile, HOME propio para los certificados de ACME
—o cada reinicio pediría certificados nuevos y se comería el límite de emisión— y la nota de por qué
corre como root) y el perfil lo arranca.

Consecuencia para la granja: las 23 recetas que clonan por `https://git.tawasuyu.net/…` **ya apuntan
a la caja**. Comprobado: el worker resuelve 2.29.29.217 y `git ls-remote` responde.

Revertir, si hiciera falta: `arjectl start openrc-gitea` en gioser y los dos `git` de vuelta a CNAME.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016Tf9T4vGzsMoT7eS8YzMFn

69526768b7004911adabf77b5f839e374e1dd6e8 · por Sergio · (hace 1 mes) · padre 77732edea · ver el árbol en este commit

Delta semántico

Ninguna definición cambió: el cambio es de texto, documentación o datos.

3 archivos · +76 −1

diff --git a/docs/28-servidor-de-produccion.md b/docs/28-servidor-de-produccion.md
index c2db5288..a56898d7 100644
--- a/docs/28-servidor-de-produccion.md
+++ b/docs/28-servidor-de-produccion.md
@@ -1125,6 +1125,54 @@ correcto, ficheros reales.
    hace falta. **Un hub nuevo no podría clonar el repo por HTTPS.** Arreglarlo re-sella `git`, que es
    raíz de `perfil.base` ⇒ unidad propia, no de paso.
 
+### 6.14 ✅ CUTOVER DEL GITEA — sirve desde la caja, con TLS y por SSH *(2026-09-14)*
+
+**Hecho y verificado desde fuera:** `git.gioser.net` y `git.tawasuyu.net` responden **200 con TLS
+válido desde `2.29.29.217`**, `git clone` funciona **por HTTPS y por SSH:2345**, y `gitea` y `caddy`
+corren supervisados por `arje-zero` en la caja. El gitea de gioser está **parado**.
+
+**Mudanza incremental, no big-bang.** Los DNS de gioser son casi todos `CNAME → www`, así que mover
+`www` habría mudado quince dominios cuyos backends siguen allá: 502 en todos. Se convirtió sólo
+`git` (en las dos zonas) de CNAME a **A propio con TTL 60**, que es reversible en un minuto.
+
+#### La secuencia que funcionó, en orden
+
+1. Caja actualizada: `arjectl` + `gitea` + la cuenta `gitea:969` + las cards en `genesis` y `cards.d`.
+2. Datos en dos fases: **1,66 GB / 18 047 ficheros** con el origen VIVO (17 s), y en el corte el
+   `rsync --delete` incremental + `sqlite3 ".backup"` (`integrity_check ok`).
+3. **Parar el origen de verdad.** `rc-service gitea stop` dice *«already stopped»* con el proceso
+   vivo (el `rc-status` que miente, §Los tres hechos), y matarlo **no alcanza**: lo revive
+   `arje-zero`, que lo tiene como card `openrc-gitea` con `Restart` — **9001 reinicios** acumulados
+   en el contador. Se paró con **`arjectl stop openrc-gitea`**, y para eso hubo que extraer su card
+   del `genesis` y escribirla en `/etc/arje/cards.d/` (§6.12: `SpawnCardFromDisk`/`StopCardFromDisk`
+   leen de ahí, no del genesis).
+4. DNS por la **API de Hetzner Cloud** (`/v1/zones`): el token de `hcloud` **también gestiona el
+   DNS** desde la unificación — la API vieja `dns.hetzner.com/api/v1` redirige a la consola. Y un
+   `PUT` sobre el rrset **no puede cambiar el tipo**: hay que `DELETE` del CNAME y `POST` del A.
+5. TLS: el primer intento de ACME **falló contra gioser** (`502`) porque el challenge salió antes de
+   que propagara; con el DNS ya al día, `arjectl restart caddy` y *certificate obtained successfully*.
+
+#### La identidad SSH se muda con el servicio
+
+`git clone ssh://…:2345` daba **`REMOTE HOST IDENTIFICATION HAS CHANGED`**: es otra máquina. Se
+copiaron las **claves de host** de gioser (`/etc/ssh/ssh_host_*`) a la caja ⇒ los clones existentes
+no notan nada. El precio es que cambia también la identidad del `:22` de administración, y hay que
+limpiar el `known_hosts` propio — a los clientes del servicio no les afecta, que es lo que importa.
+
+Y el `sshd` del producto escucha sólo en `:22`: el git por SSH necesitó `Port 2345` y que el usuario
+`gitea` tenga **shell real** (su `authorized_keys` fuerza `command="gitea serv …"`, y con
+`/bin/false` no se ejecuta nada).
+
+#### Lo que esto cambia para la granja
+
+Las 23 recetas que ahora clonan por `https://git.tawasuyu.net/…` **apuntan a la caja**. Comprobado
+tras el cambio: el worker resuelve `2.29.29.217` y `git ls-remote` responde. La granja depende ya
+del servidor nuevo, que es el sentido de la mudanza.
+
+#### Cómo se revierte, si hiciera falta
+
+`arjectl start openrc-gitea` en gioser y devolver los dos `git` a `CNAME → www`. Con TTL 60, minutos.
+
 ## 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 1a80fe28..22a2df08 100644
--- a/docs/state/targets.toml
+++ b/docs/state/targets.toml
@@ -1358,4 +1358,4 @@ paquetes = [
 # `gitea` en `/etc/passwd`— y sale con 78 si falta alguna. Es a propósito: las dos vienen con los
 # datos de la mudanza, y un gitea que arranca sin su config ofrece el asistente de «crear
 # administrador» a quien pase. Hasta que la mudanza traiga ambas, este servicio NO levanta y lo dice.
-servicios = ["sshd", "gitea"]
+servicios = ["sshd", "gitea", "caddy"]
diff --git a/recipes/caddy.toml b/recipes/caddy.toml
index 6e325dc9..6b2983ca 100644
--- a/recipes/caddy.toml
+++ b/recipes/caddy.toml
@@ -31,3 +31,30 @@ flags    = []
 # `go` es herramienta de BUILD, no dependencia de runtime: el binario sale autocontenido.
 [deps]
 build = ["go"]
+
+# ── EL SERVICIO QUE ESTE PAQUETE TRAE (SDD 30) ──────────────────────────────────────────────────
+# Fuera de `hash_inputs`: declarar esto NO re-hashea caddy.
+#
+# Caddy es el que pone la cara al exterior: sin él, un dominio mudado no responde aunque su backend
+# esté vivo. La guarda comprueba el Caddyfile y sale 78 nombrándolo — un caddy sin config **arranca
+# igual** y sirve una página vacía en el 80, que es peor que no arrancar: el dominio parece vivo.
+#
+# ⚠ `HOME` NO es cosmético: ahí guarda las claves y los certificados de ACME
+# (`$HOME/.local/share/caddy`). Con el HOME del padre, un reinicio pediría certificados NUEVOS a
+# Let's Encrypt y se comería el límite de emisión del dominio.
+#
+# Corre como ROOT a propósito: enlaza :80 y :443, que son privilegiados. La alternativa es
+# `setcap cap_net_bind_service`, que no se puede aplicar desde el store (el artefacto es de sólo
+# lectura y los caps no viajan en el hardlink) ⇒ sería estado fuera del árbol, que es justo lo que
+# esta distro evita. Queda anotado como decisión, no como olvido.
+#
+# Rutas ABSOLUTAS: el `sh` de busybox no despacha applets y PID 1 no garantiza `PATH`.
+[[service]]
+label      = "caddy"
+id         = "01M2EKDA00V4XCPRH5WRSGDAJP"
+exec       = "/bin/busybox"
+argv = ["sh", "-c", "test -f /etc/caddy/Caddyfile || { echo 'caddy: falta /etc/caddy/Caddyfile — es config del SITIO' >&2; exit 78; }; /bin/mkdir -p /var/lib/caddy /var/log/caddy; exec /usr/bin/caddy run --config /etc/caddy/Caddyfile --adapter caddyfile"]
+envp       = [["PATH", "/usr/bin:/bin"], ["HOME", "/var/lib/caddy"], ["XDG_DATA_HOME", "/var/lib/caddy"]]
+networking = "full"
+cgroup     = "arje.slice/caddy"
+restart    = { initial_ms = 1000, max_ms = 30000 }