gioser

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 1389Shell 137Markdown 80Rust 802602 Sutichaykuna

⎇ 3d3d160e01f83bf5a671272af2de2d6bfeddbb87 web/takana-os.net/README.md Markdown · 226 siq'ikuna · 11.9 KB Ñawpa willay ch'uya

tres variantes —base, mirada, servidor, full— en sus dos medios 3d3d160e0 · Sergio ·

1# `takana-os.net` — el sitio del proyecto
2
3Una sola página, autocontenida, en dos idiomas y dos temas. **No le pide nada a terceros:** las
4tipografías de la marca (Chivo e IBM Plex, las dos SIL OFL) viajan acá en `fuentes/`, subconjuntos
5`latin` y `latin-ext`, 154 KB en total. Un sitio sobre no depender de binarios ajenos que cargara
6las fuentes de Google en cada visita sería una contradicción barata.
7
8| | |
9|---|---|
10| `index.html` | la página entera: CSS y JS adentro, el símbolo como `<symbol>` SVG inline |
11| `fuentes.css` + `fuentes/` | las ocho caras `@font-face`, servidas de acá |
12| `simbolo.svg` | el favicon — el martillo tocapu 7×7, derivado de `docs/marca/simbolo-fondo-claro.svg` |
13
14## La sección «las decisiones»
15
16Diez fichas, cada una con tres lecturas de lo mismo: contra qué se decide, qué implica por dentro,
17y qué quiere decir sin jerga. **No son opinión: cada una sale de un ADR o de una decisión ya tomada
18en `docs/`** — fuentes clavadas (ADR 0006), `zig cc` (0003), `arje` de init (0007) y el arranque por
19grafo (0010), multi-origen con índice firmado (0014), imágenes ajenas enjauladas (0015), y las de
20Wayland-only y PipeWire, que viven en los frentes de escritorio y audio.
21
22⚠ **Si una decisión cambia, esta página miente hasta que alguien la toque.** Es el precio de
23explicar el diseño en una landing y no en un doc generado: se gana claridad y se paga en
24mantenimiento. Las fichas se armaron con un guion de un solo uso — la página es la fuente, no hay
25generador que mantener.
26
27La paleta y la tipografía salen de **`docs/marca/README.md`** y no se inventan acá. La única
28desviación, deliberada: en tema claro el texto de acento usa un brasa oscurecido (`#9E3F16`), porque
29el `#DF6B2A` de la marca sobre alpaca da 2,9:1 y no se lee. Las combinaciones de la página están
30medidas: ninguna baja de 4,5:1.
31
32## El instalador: `/descargas/` vive FUERA del repo
33
34`https://takana-os.net/descargas/takana-instalador.run` es **un fichero que trae todo adentro**:
35cabecera `sh` + un `tar.gz` pegado detrás con el binario `takana`, el instalador que corre en RAM, su
36kernel y el censo del terreno. Se baja, se le da permiso y se corre; no baja nada más.
37
38⚠ **No está en el repo, y no puede estarlo.** Son 137 MiB: en git serían 137 MiB para siempre en
39cada clon, y además **el espejo de GitHub rechaza cualquier fichero de más de 100 MiB** — o sea que
40el `push` se rompería y nadie relacionaría la causa con el sitio. Vive en `/work/sergio/descargas`,
41que está en `sdc` (255 G) y no en la raíz de 5,8 G, que se llena y tira la caja entera.
42
43```sh
44scripts/instalador-paquete.sh          # arma el paquete (y comprueba su coherencia)
45scripts/instalador-autoextraible.sh    # lo convierte en UN fichero, y lo desempaqueta para probarlo
46cp work/variantes/*.run work/variantes/*.img /work/sergio/descargas/
47( cd /work/sergio/descargas && for f in takana-instalador-*; do case "$f" in *.sha256) ;; *) sha256sum "$f" > "$f.sha256";; esac; done )
48```
49
50El `.sha256` que se publica al lado es **el del fichero entero**, para comprobar la descarga
51(`sha256sum -c takana-instalador.run.sha256`). Es distinto del que el `.run` lleva adentro, que es
52el **del payload** y lo verifica él solo antes de desempaquetar (`./takana-instalador.run --sha256`
53lo imprime).
54
55⚠ **El hash NO se escribe en `index.html`, y es deliberado.** La primera versión lo traía pegado en
56la página y habría empezado a mentir en el primer rebuild — que es el modo de fallo que este mismo
57README ya avisa para la sección «las decisiones». La página muestra el comando; el número lo sirve
58el fichero de al lado, que se regenera junto al `.run`.
59
60⚠ `/descargas` queda **fuera del `encode`** del vhost: el `.run` ya viene comprimido, y volver a
61comprimirlo por HTTP gasta CPU en cada descarga sin ahorrar un byte.
62
63### Las variantes: qué sistema se instala
64
65`scripts/instalador-variantes.sh [servidor|mirada|full]` arma las tres, en sus dos medios.
66La diferencia es **qué rootfs se copia al disco**, no cómo: el instalador y el censo son los mismos.
67
68| variante | composición | .run | USB |
69|---|---|---|---|
70| `base` | sólo el piso de arranque | 137 MiB | 519 MiB |
71| `mirada` | + `[perfil.base]` + `[perfil.escritorio-mirada]` | 524 MiB | 1,6 GiB |
72| `servidor` | + `[perfil.base]` + `[perfil.servidor]` | 1,2 GiB | 3,3 GiB |
73| `full` | + `base` + `cli` + `escritorio-mirada` + `servidor` | **no hay** | 3,5 GiB |
74
75⚠ **«base» son DOS cosas con el mismo nombre y confundirlas da un rootfs que no arranca.** El
76*piso de arranque* es `product-rootfs` —arje-zero como PID 1 + busybox— y **no está en ningún
77perfil**; el *userland base* es `[perfil.base]`, 359 binarios, y **no tiene ni busybox ni
78arje-zero adentro** (medido: `ls base/bin`). Cada variante es `product-rootfs ∪ perfil.base ∪ …`.
79El [SDD 27 §1](../../docs/27-perfiles-instalables-y-el-instalador.md) ya había medido la otra
80mitad del mismo hueco: los escritorios no heredan `base`.
81
82⚠ **Cada perfil se hidrata en su propio árbol y después se funden**, y no uno encima de otro: la
83auditoría del bit de privilegio corre contra las declaraciones `[[setuid]]` del perfil que se está
84hidratando, así que al poner `escritorio-mirada` sobre `base` veía el `doas` 4755 que dejó `base`
85—declarado y legítimo— y lo denunciaba como un bit que nadie declaró.
86
87⚠ **`full` no tiene `.run`, y es física.** El fichero único carga el instalador entero en memoria
88—es lo que le permite soltar el disco— y ahí viven a la vez el initramfs comprimido y su contenido
89desempaquetado. Con 3,2 GiB de rootfs eso pide ~8 GiB de RAM en la máquina donde se instale. El USB
90no tiene ese límite: corre desde el propio medio. La guarda que lo detecta **mide la RAM de la
91máquina que construye, no la de la que instala** — acá acierta por casualidad (el hub tiene 7,6 GiB,
92parecido a un portátil) y queda anotado sin barrer.
93
94### La imagen USB, para cuando el salto no se puede
95
96`takana-instalador-usb.img` (519 MiB) es **el mismo instalador por otro camino**: mismo árbol, mismo
97censo, mismas guardas. Se graba con `dd` y arranca por la ruta de medios removibles del firmware.
98
99⚠ **No lleva el instalador en la ESP, y no es un capricho:** el cuelgue del primer viaje a metal fue
100un initrd de ~100 MiB cargado por el EFI-stub ([ADR 0010](../../docs/adr/0010-arranque-grafo-mirada.md)
101paso 2), y el del instalador son ~122 MiB. El USB usa el patrón que **sí** está probado —el de un
102disco instalado—: la ESP lleva el kernel y un pivote de 736 KiB, y el pivote encuentra la ext4 por
103`LABEL=takana-medio` y hace `switch_root`. La etiqueta NO es `hammer-root` a propósito: si el medio
104se llamara igual que la raíz de un sistema instalado, el pivote podría montar el disco del usuario
105en vez del USB, y el que pierde es el disco.
106
107```sh
108scripts/instalador-usb.sh                   # arma work/takana-instalador-usb.img
109scripts/instalador-usb-test.sh --instalar   # lo arranca en OVMF y lo contesta por el serial
110cp work/takana-instalador-usb.img /work/sergio/descargas/
111( cd /work/sergio/descargas && sha256sum takana-instalador-usb.img > takana-instalador-usb.img.sha256 )
112```
113
114**Está probado de punta a punta, no sólo servido:** `VIA_RUN=1 scripts/metal-salto-test.sh` mete el
115fichero publicado en una VM con `linux-metal-kexec`, lo corre, y comprueba sobre el DISCO que se
116verificó solo, se desempaquetó, saltó e instaló — 13 comprobaciones, con el `bootmgfw.efi` del
117vecino byte a byte igual y la tabla de particiones sin cambiar.
118
119Y el USB, `scripts/instalador-usb-test.sh --instalar`: **arranca por firmware de verdad** (OVMF por
120`pflash`, sin `-kernel`, que es la única forma de probar la tabla, la ESP, el stub y el nombre
121`\initramfs.cpio.gz` que el cmdline horneado exige), **contesta el menú tecleando por el serial** y
122comprueba el disco destino. 12 en verde.
123
124⚠ El arnés corre con `-vga none`, y eso decide qué rama del código se prueba: sin pantalla el
125instalador detecta el `vtconsole` `dummy` y se va al serial. **Con** pantalla —el caso del usuario—
126la pregunta sale por `tty0`. Las dos ramas existen porque el cmdline HORNEADO del kernel pone `tty0`
127primero y `ttyS0` último, o sea que `/dev/console` es el serial: en un portátil sin puerto serie, sin
128esta detección, el censo y la pregunta se irían a un sitio que no existe y el arranque parecería
129colgado.
130
131## Las dos direcciones que publica
132
133- **código** → `https://git.gioser.net/sergio/takana` (público; `git clone` por HTTPS)
134- **paquetes** → `https://takana-os.net/repo/`, que es el MISMO `/srv/repo` firmado que sirve
135  `repo.gioser.net`. Comprobado: `takana install age --repo https://repo.gioser.net --trust ./trust`
136  responde `release: trusted (by release)`.
137
138Y **desde el 2026-09-21 se puede mirar el repo sin construir nada**, que antes no se podía:
139`repo list` y `repo verify` aceptan la misma lista de orígenes que `install`. Hasta ese día sólo
140miraban directorios locales y ante una URL respondían «repo vacío» **con salida 0** — 173 paquetes
141leídos como ninguno.
142
143```sh
144takana repo list   --repo https://takana-os.net/repo                  # 173 paquete(s)
145takana repo verify --repo https://takana-os.net/repo --trust ./trust  # release: trusted
146```
147
148## Cómo se sirve
149
150Caddy, en la caja `takana`, vhost `takana-os.net, www.takana-os.net`. La raíz es
151**`/work/sergio/takana/web/takana-os.net`**, el árbol de trabajo vivo: *publicar es guardar el
152fichero*. No lo refresca `publicar-webs.sh` a propósito — un `pull` sobre el árbol compartido mueve
153HEAD bajo los pies de otro agente (CLAUDE.md §2 ter).
154
155Este README **no se sirve**: son notas de operación, y el matcher del vhost las 404ea.
156
157Los bloques, por si hay que rehacer la caja (`caddy validate` → `arjectl restart caddy`, **nunca**
158`caddy reload`: el Caddyfile lleva `admin off`, SDD 28 §6.14):
159
160```caddyfile
161takana-os.net, www.takana-os.net {
162	import acceso
163	@comprimible not path /descargas/*
164	encode @comprimible gzip zstd
165
166	redir /descargas /descargas/ permanent
167	handle_path /descargas/* {
168		root * /work/sergio/descargas
169		file_server browse
170	}
171
172	redir /repo /repo/ permanent
173	handle_path /repo/* {
174		root * /srv/repo
175		file_server browse
176	}
177
178	handle {
179		root * /work/sergio/takana/web/takana-os.net
180		@oculto path /.* */.* /README.md
181		respond @oculto 404
182		file_server
183	}
184}
185
186git.takana-os.net {
187	import acceso
188	redir https://git.gioser.net{uri} permanent
189}
190
191takana.gioser.net {
192	import acceso
193	redir https://takana-os.net{uri} permanent
194}
195```
196
197`scripts/servidor/publicar-webs.sh` comprueba los cuatro: los dos que sirven ficheros por sha
198contra el disco, y los dos que redirigen por destino.
199
200## Los nombres
201
202| nombre | qué hace | estado |
203|---|---|---|
204| `takana-os.net`, `www` | el sitio | ✅ con certificado de Let's Encrypt (2026-09-21) |
205| `takana-os.net/repo/` | el repo de paquetes, el mismo `/srv/repo` | ✅ |
206| `takana-os.net/descargas/` | el instalador en UN fichero, y la imagen USB (`/work/sergio/descargas`) | ✅ (2026-09-23) |
207| `takana.gioser.net` | **301 → `takana-os.net`** | ✅ |
208| `git.takana-os.net` | **301 → `git.gioser.net`** | ⏳ vhost puesto, **falta el registro A** |
209
210`git.takana-os.net` sólo redirige, y es deliberado: gitea tiene UN `ROOT_URL` con el que arma cada
211enlace y cada URL de clon que muestra, así que colgarlo de un segundo nombre daría un sitio que se
212contradice —entrás por uno y te ofrece clonar del otro— y moverlo de verdad rompe los clones y el
213CI que ya apuntan al viejo.
214
215Los registros van a `2.29.29.217`, la IP de la caja:
216
217```
218takana-os.net.       A  2.29.29.217   ✅ puesto
219www.takana-os.net.   A  2.29.29.217   ✅ puesto
220git.takana-os.net.   A  2.29.29.217   ⏳ falta
221```
222
223El certificado sale solo en la primera petición tras el DNS — y **tarda unos segundos más que el
224primer `curl`**: si da error, reintentar antes de ir a mirar logs (SDD 28 §6.14). Si el nombre ya
225resolvía cuando Caddy arrancó pero el certificado no llegó, **reiniciar caddy fuerza el reintento**
226sin esperar el backoff: fue lo que hizo falta acá.

README.md

takana-os.net — el sitio del proyecto

Una sola página, autocontenida, en dos idiomas y dos temas. No le pide nada a terceros: las tipografías de la marca (Chivo e IBM Plex, las dos SIL OFL) viajan acá en fuentes/, subconjuntos latin y latin-ext, 154 KB en total. Un sitio sobre no depender de binarios ajenos que cargara las fuentes de Google en cada visita sería una contradicción barata.

index.htmlla página entera: CSS y JS adentro, el símbolo como <symbol> SVG inline
fuentes.css + fuentes/las ocho caras @font-face, servidas de acá
simbolo.svgel favicon — el martillo tocapu 7×7, derivado de docs/marca/simbolo-fondo-claro.svg

La sección «las decisiones»

Diez fichas, cada una con tres lecturas de lo mismo: contra qué se decide, qué implica por dentro, y qué quiere decir sin jerga. No son opinión: cada una sale de un ADR o de una decisión ya tomada en docs/ — fuentes clavadas (ADR 0006), zig cc (0003), arje de init (0007) y el arranque por grafo (0010), multi-origen con índice firmado (0014), imágenes ajenas enjauladas (0015), y las de Wayland-only y PipeWire, que viven en los frentes de escritorio y audio.

⚠ Si una decisión cambia, esta página miente hasta que alguien la toque. Es el precio de explicar el diseño en una landing y no en un doc generado: se gana claridad y se paga en mantenimiento. Las fichas se armaron con un guion de un solo uso — la página es la fuente, no hay generador que mantener.

La paleta y la tipografía salen de docs/marca/README.md y no se inventan acá. La única desviación, deliberada: en tema claro el texto de acento usa un brasa oscurecido (#9E3F16), porque el #DF6B2A de la marca sobre alpaca da 2,9:1 y no se lee. Las combinaciones de la página están medidas: ninguna baja de 4,5:1.

El instalador: /descargas/ vive FUERA del repo

https://takana-os.net/descargas/takana-instalador.run es un fichero que trae todo adentro: cabecera sh + un tar.gz pegado detrás con el binario takana, el instalador que corre en RAM, su kernel y el censo del terreno. Se baja, se le da permiso y se corre; no baja nada más.

⚠ No está en el repo, y no puede estarlo. Son 137 MiB: en git serían 137 MiB para siempre en cada clon, y además el espejo de GitHub rechaza cualquier fichero de más de 100 MiB — o sea que el push se rompería y nadie relacionaría la causa con el sitio. Vive en /work/sergio/descargas, que está en sdc (255 G) y no en la raíz de 5,8 G, que se llena y tira la caja entera.

scripts/instalador-paquete.sh          # arma el paquete (y comprueba su coherencia)
scripts/instalador-autoextraible.sh    # lo convierte en UN fichero, y lo desempaqueta para probarlo
cp work/variantes/*.run work/variantes/*.img /work/sergio/descargas/
( cd /work/sergio/descargas && for f in takana-instalador-*; do case "$f" in *.sha256) ;; *) sha256sum "$f" > "$f.sha256";; esac; done )

El .sha256 que se publica al lado es el del fichero entero, para comprobar la descarga (sha256sum -c takana-instalador.run.sha256). Es distinto del que el .run lleva adentro, que es el del payload y lo verifica él solo antes de desempaquetar (./takana-instalador.run --sha256 lo imprime).

⚠ El hash NO se escribe en index.html, y es deliberado. La primera versión lo traía pegado en la página y habría empezado a mentir en el primer rebuild — que es el modo de fallo que este mismo README ya avisa para la sección «las decisiones». La página muestra el comando; el número lo sirve el fichero de al lado, que se regenera junto al .run.

⚠ /descargas queda fuera del encode del vhost: el .run ya viene comprimido, y volver a comprimirlo por HTTP gasta CPU en cada descarga sin ahorrar un byte.

Las variantes: qué sistema se instala

scripts/instalador-variantes.sh [servidor|mirada|full] arma las tres, en sus dos medios. La diferencia es qué rootfs se copia al disco, no cómo: el instalador y el censo son los mismos.

variantecomposición.runUSB
basesólo el piso de arranque137 MiB519 MiB
mirada+ [perfil.base] + [perfil.escritorio-mirada]524 MiB1,6 GiB
servidor+ [perfil.base] + [perfil.servidor]1,2 GiB3,3 GiB
full+ base + cli + escritorio-mirada + servidorno hay3,5 GiB

⚠ «base» son DOS cosas con el mismo nombre y confundirlas da un rootfs que no arranca. El piso de arranque es product-rootfs —arje-zero como PID 1 + busybox— y no está en ningún perfil; el userland base es [perfil.base], 359 binarios, y no tiene ni busybox ni arje-zero adentro (medido: ls base/bin). Cada variante es product-rootfs ∪ perfil.base ∪ …. El SDD 27 §1 ya había medido la otra mitad del mismo hueco: los escritorios no heredan base.

⚠ Cada perfil se hidrata en su propio árbol y después se funden, y no uno encima de otro: la auditoría del bit de privilegio corre contra las declaraciones [[setuid]] del perfil que se está hidratando, así que al poner escritorio-mirada sobre base veía el doas 4755 que dejó base —declarado y legítimo— y lo denunciaba como un bit que nadie declaró.

⚠ full no tiene .run, y es física. El fichero único carga el instalador entero en memoria —es lo que le permite soltar el disco— y ahí viven a la vez el initramfs comprimido y su contenido desempaquetado. Con 3,2 GiB de rootfs eso pide ~8 GiB de RAM en la máquina donde se instale. El USB no tiene ese límite: corre desde el propio medio. La guarda que lo detecta mide la RAM de la máquina que construye, no la de la que instala — acá acierta por casualidad (el hub tiene 7,6 GiB, parecido a un portátil) y queda anotado sin barrer.

La imagen USB, para cuando el salto no se puede

takana-instalador-usb.img (519 MiB) es el mismo instalador por otro camino: mismo árbol, mismo censo, mismas guardas. Se graba con dd y arranca por la ruta de medios removibles del firmware.

⚠ No lleva el instalador en la ESP, y no es un capricho: el cuelgue del primer viaje a metal fue un initrd de ~100 MiB cargado por el EFI-stub (ADR 0010 paso 2), y el del instalador son ~122 MiB. El USB usa el patrón que sí está probado —el de un disco instalado—: la ESP lleva el kernel y un pivote de 736 KiB, y el pivote encuentra la ext4 por LABEL=takana-medio y hace switch_root. La etiqueta NO es hammer-root a propósito: si el medio se llamara igual que la raíz de un sistema instalado, el pivote podría montar el disco del usuario en vez del USB, y el que pierde es el disco.

scripts/instalador-usb.sh                   # arma work/takana-instalador-usb.img
scripts/instalador-usb-test.sh --instalar   # lo arranca en OVMF y lo contesta por el serial
cp work/takana-instalador-usb.img /work/sergio/descargas/
( cd /work/sergio/descargas && sha256sum takana-instalador-usb.img > takana-instalador-usb.img.sha256 )

Está probado de punta a punta, no sólo servido: VIA_RUN=1 scripts/metal-salto-test.sh mete el fichero publicado en una VM con linux-metal-kexec, lo corre, y comprueba sobre el DISCO que se verificó solo, se desempaquetó, saltó e instaló — 13 comprobaciones, con el bootmgfw.efi del vecino byte a byte igual y la tabla de particiones sin cambiar.

Y el USB, scripts/instalador-usb-test.sh --instalar: arranca por firmware de verdad (OVMF por pflash, sin -kernel, que es la única forma de probar la tabla, la ESP, el stub y el nombre \initramfs.cpio.gz que el cmdline horneado exige), contesta el menú tecleando por el serial y comprueba el disco destino. 12 en verde.

⚠ El arnés corre con -vga none, y eso decide qué rama del código se prueba: sin pantalla el instalador detecta el vtconsole dummy y se va al serial. Con pantalla —el caso del usuario— la pregunta sale por tty0. Las dos ramas existen porque el cmdline HORNEADO del kernel pone tty0 primero y ttyS0 último, o sea que /dev/console es el serial: en un portátil sin puerto serie, sin esta detección, el censo y la pregunta se irían a un sitio que no existe y el arranque parecería colgado.

Las dos direcciones que publica

Y desde el 2026-09-21 se puede mirar el repo sin construir nada, que antes no se podía: repo list y repo verify aceptan la misma lista de orígenes que install. Hasta ese día sólo miraban directorios locales y ante una URL respondían «repo vacío» con salida 0 — 173 paquetes leídos como ninguno.

takana repo list   --repo https://takana-os.net/repo                  # 173 paquete(s)
takana repo verify --repo https://takana-os.net/repo --trust ./trust  # release: trusted

Cómo se sirve

Caddy, en la caja takana, vhost takana-os.net, www.takana-os.net. La raíz es /work/sergio/takana/web/takana-os.net, el árbol de trabajo vivo: publicar es guardar el fichero. No lo refresca publicar-webs.sh a propósito — un pull sobre el árbol compartido mueve HEAD bajo los pies de otro agente (CLAUDE.md §2 ter).

Este README no se sirve: son notas de operación, y el matcher del vhost las 404ea.

Los bloques, por si hay que rehacer la caja (caddy validate → arjectl restart caddy, nunca caddy reload: el Caddyfile lleva admin off, SDD 28 §6.14):

takana-os.net, www.takana-os.net {
	import acceso
	@comprimible not path /descargas/*
	encode @comprimible gzip zstd

	redir /descargas /descargas/ permanent
	handle_path /descargas/* {
		root * /work/sergio/descargas
		file_server browse
	}

	redir /repo /repo/ permanent
	handle_path /repo/* {
		root * /srv/repo
		file_server browse
	}

	handle {
		root * /work/sergio/takana/web/takana-os.net
		@oculto path /.* */.* /README.md
		respond @oculto 404
		file_server
	}
}

git.takana-os.net {
	import acceso
	redir https://git.gioser.net{uri} permanent
}

takana.gioser.net {
	import acceso
	redir https://takana-os.net{uri} permanent
}

scripts/servidor/publicar-webs.sh comprueba los cuatro: los dos que sirven ficheros por sha contra el disco, y los dos que redirigen por destino.

Los nombres

nombrequé haceestado
takana-os.net, wwwel sitio✅ con certificado de Let's Encrypt (2026-09-21)
takana-os.net/repo/el repo de paquetes, el mismo /srv/repo✅
takana-os.net/descargas/el instalador en UN fichero, y la imagen USB (/work/sergio/descargas)✅ (2026-09-23)
takana.gioser.net301 → takana-os.net✅
git.takana-os.net301 → git.gioser.net⏳ vhost puesto, falta el registro A

git.takana-os.net sólo redirige, y es deliberado: gitea tiene UN ROOT_URL con el que arma cada enlace y cada URL de clon que muestra, así que colgarlo de un segundo nombre daría un sitio que se contradice —entrás por uno y te ofrece clonar del otro— y moverlo de verdad rompe los clones y el CI que ya apuntan al viejo.

Los registros van a 2.29.29.217, la IP de la caja:

takana-os.net.       A  2.29.29.217   ✅ puesto
www.takana-os.net.   A  2.29.29.217   ✅ puesto
git.takana-os.net.   A  2.29.29.217   ⏳ falta

El certificado sale solo en la primera petición tras el DNS — y tarda unos segundos más que el primer curl: si da error, reintentar antes de ir a mirar logs (SDD 28 §6.14). Si el nombre ya resolvía cuando Caddy arrancó pero el certificado no llegó, reiniciar caddy fuerza el reintento sin esperar el backoff: fue lo que hizo falta acá.