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 815Rust 56Shell 37Markdown 291229 Definiciones

servidor: freno anti-rastreo en gitea — de load 14,78 a 0,96

La caja estaba ahogada: load 14,78 sobre 4 cores, gitea al 144 % de un core
y diez `git` hijos colgando. La causa NO era un leak ni el GOMEMLIMIT que se
le puso ayer (RSS 678 MB contra un techo de 2 GiB: no peleaba con el GC).

Medido sobre el log de saywa, 36 minutos de ventana:

  · 1.179.729 peticiones/día desde 8.945 IPs distintas ⇒ por IP no se corta
  · 99,5 % del coste en las vistas POR COMMIT: /src|commits|blame|raw
    /commit/<sha>/… y /compare — ficheros × commits y refs × refs, o sea
    espacio de rastreo INFINITO por construcción
  · las vistas POR RAMA, que es lo que usa una persona, el 0,4 %
  · 96 % de respuestas 200: se llevaban el contenido entero
  · gitea forkeaba un `git cat-file` y un `git check-attr` (detección de
    lenguaje) por cada vista de fichero ⇒ la tormenta de forks

El robots.txt ya prohibía esas rutas y los bots la ignoran — incluido
meta-webindexer, que pedía /blame/commit estando explícitamente prohibido y
se llevaba el 33,6 % del coste él solo. La capa educada estaba agotada.

La regla corta con 403 sólo a ANÓNIMOS: la cookie de sesión exime, así que
quien entra con usuario conserva los permalinks. Verificado con `saywa
explain` y en vivo que clonar, empujar, la portada, el árbol por rama,
issues, pulls, los ficheros de un PR y el robots.txt siguen en 200; y con un
`git clone` real de punta a punta.

Resultado: coste medio de 35,4 cores a 0,98 · load 14,78 → 0,96 · gitea de
144,6 % a 2,7 % de un core · cero hijos git.

Se versiona porque vivía sólo en /etc del anfitrión, que es la lección de los
enlaces del scratch y del terminfo: un arreglo que hay que recordar no es un
arreglo.

0ea7ecc357158b7971485d277d8782138b25324f · por Sergio · (hace 1 d) · padre 494b34668 · ver el árbol en este commit

Delta semántico

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

1 archivos · +2002 −0

diff --git a/scripts/servidor/saywa.ron b/scripts/servidor/saywa.ron
new file mode 100644
index 00000000..d797db3d
--- /dev/null
+++ b/scripts/servidor/saywa.ron
@@ -0,0 +1,2002 @@
+// Config DESPLEGADA de `saywa` en la caja `takana` (2.29.29.217) — el servidor de borde que
+// reemplazó a caddy el 2026-10-20. Esto es una COPIA de /etc/saywa/saywa.ron, versionada acá
+// porque antes vivía SÓLO en /etc del anfitrión: si la caja se rehace, se pierde. Mismo criterio
+// que `cortafuegos-entrada.ron`, al lado.
+//
+//     saywa check   --config scripts/servidor/saywa.ron      # valida sin arrancar
+//     saywa explain --config scripts/servidor/saywa.ron URL  # qué contestaría, y por qué
+//     # desplegar: copiar a /etc/saywa/saywa.ron y `saywa ctl reload` (en caliente, sin cortar)
+//
+// ⚠ `explain` es la herramienta que hay que usar ANTES de tocar esto: dice qué ruta coincide y
+// qué se responde, leyendo un fichero cualquiera. Evita probar en producción.
+//
+// ══ EL FRENO ANTI-RASTREO (2026-10-21) ════════════════════════════════════════════════════════
+// Los dos frentes de gitea (git.gioser.net y git.tawasuyu.net) llevan una ruta que corta con 403
+// las vistas "por commit" para visitantes ANÓNIMOS. Medido, no preventivo:
+//
+//   · 1.179.729 peticiones/día desde 8.945 IPs distintas ⇒ bloquear por IP es inútil
+//   · el 99,5 % del coste estaba en /src|commits|blame|raw /commit/<sha>/... y en /compare:
+//     son ficheros × commits y refs × refs — espacio de rastreo INFINITO por construcción
+//   · las URL "por rama", que es lo que usa una persona, eran el 0,4 %
+//   · load 14,78 en 4 cores; gitea forkeaba un `git cat-file` y un `git check-attr` por vista
+//   · tras el freno: 35,4 → 0,98 cores de coste medio, y CERO hijos `git` colgando
+//
+// El robots.txt YA prohibía esas rutas y los bots la ignoran (incluido meta-webindexer, que pedía
+// /blame/commit estando explícitamente prohibido) ⇒ la capa educada estaba agotada y había que
+// hacer cumplir la norma. La cookie de sesión EXIME, así que quien entra con usuario conserva los
+// permalinks; clonar y empujar no pasan por esas rutas y están verificados intactos.
+//
+// ⚠ Y la trampa del recargado en caliente, que casi me hizo diagnosticar mal: tras `ctl reload`
+// las conexiones HTTP/2 ya abiertas SIGUEN con la tabla de rutas vieja. Se ve en el log por el
+// campo `saywa.ruta` (el índice del proxy se corre al insertar una ruta): 707 peticiones por la
+// ruta vieja en el minuto 0, 649 en el 1, 2 en el 2, 0 en el 3. Se drena solo en ~3 minutos, pero
+// medir antes de eso dice que la regla NO funciona cuando sí funciona.
+// saywa en takana: la config de caddy (/etc/caddy/Caddyfile) absorbida con
+// `saywa import caddyfile` el 2026-10-20, más lo que ya servía saywa
+// (repo.gioser.net desde /srv/repo). Los registros siguen en los mismos
+// archivos de caddy, con la misma forma JSON y la misma rotación: lo que los
+// lee no cambia. Volver a caddy: ver /root/saywa-cambio/VOLVER.
+(
+    listen: [
+        (
+            address: "[::]:80",
+            tls: false,
+            egress: false,
+        ),
+        (
+            address: "[::]:443",
+            tls: true,
+            egress: false,
+            // HTTP/3 por UDP en el mismo puerto (SDD §17); el cortafuegos lo deja pasar.
+            http3: true,
+        ),
+    ],
+    sites: [
+        (
+            names: [
+                "gitea.gioser.net",
+            ],
+            tls: Auto,
+            log: Some("requests"),
+            routes: [
+                (
+                    actions: [
+                        Redirect(
+                            to: "https://git.gioser.net{uri}",
+                            status: 301,
+                        ),
+                    ],
+                ),
+            ],
+        ),
+        (
+            names: [
+                "git.takana-os.net",
+            ],
+            tls: Auto,
+            log: Some("requests"),
+            routes: [
+                (
+                    actions: [
+                        Redirect(
+                            to: "https://git.gioser.net{uri}",
+                            status: 301,
+                        ),
+                    ],
+                ),
+            ],
+        ),
+        (
+            names: [
+                "git.gioser.net",
+            ],
+            tls: Auto,
+            log: Some("requests"),
+            routes: [
+                (
+                    match: (
+                        header: [
+                            ("User-Agent", "*meta-externalagent*"),
+                            ("User-Agent", "*FacebookBot*"),
+                            ("User-Agent", "*GPTBot*"),
+                            ("User-Agent", "*OAI-SearchBot*"),
+                            ("User-Agent", "*ChatGPT*"),
+                            ("User-Agent", "*Amazonbot*"),
+                            ("User-Agent", "*ClaudeBot*"),
+                            ("User-Agent", "*anthropic-ai*"),
+                            ("User-Agent", "*Claude-Web*"),
+                            ("User-Agent", "*Bytespider*"),
+                            ("User-Agent", "*CCBot*"),
+                            ("User-Agent", "*Google-Extended*"),
+                            ("User-Agent", "*Applebot-Extended*"),
+                            ("User-Agent", "*PerplexityBot*"),
+                            ("User-Agent", "*Diffbot*"),
+                            ("User-Agent", "*Omgilibot*"),
+                            ("User-Agent", "*DataForSeoBot*"),
+                        ],
+                    ),
+                    actions: [
+                        Respond(
+                            status: 403,
+                            body: None,
+                            type: None,
+                        ),
+                    ],
+                ),
+                (
+                    // ⚠ FRENO ANTI-RASTREO — puesto 2026-10-21, medido, no preventivo.
+                    // El log de saywa daba 1.179.729 peticiones/día de 8.945 IPs distintas, y
+                    // el 99,5 % del coste estaba en las URL "por commit": /src|commits|blame|raw
+                    // /commit/<sha>/..., que son ficheros × commits = espacio INFINITO. Las URL
+                    // "por rama" —lo que usa una persona— eran el 0,4 %. Load 14,78 en 4 cores.
+                    // El robots.txt YA prohibía estas rutas y lo ignoran: la capa educada está
+                    // agotada, así que acá se hace cumplir. La cookie de sesión exime, o sea que
+                    // quien entra con usuario conserva los permalinks; clonar y empujar no pasan
+                    // por acá (van por /info/refs y /git-upload-pack) y no se tocan.
+                    match: (
+                        path: [
+                            "/*/*/src/commit/*",
+                            "/*/*/commits/commit/*",
+                            "/*/*/blame/commit/*",
+                            "/*/*/raw/commit/*",
+                            "/*/*/commit/*",
+                            // 2ª tanda, misma medición: con lo de arriba ya cortado, /compare se
+                            // quedó con el 96 % del coste restante (5,2 s de media por peticion).
+                            // Es refs × refs — la misma trampa combinatoria que /commit, y los
+                            // bots la recorren igual. /activity y /lastcommit son agregados caros
+                            // (vi un /activity/monthly de 42 s en UNA peticion).
+                            "/*/*/compare/*",
+                            "/*/*/activity",
+                            "/*/*/activity/*",
+                            "/*/*/lastcommit/*",
+                        ],
+                        not: [
+                            (
+                                header: [
+                                    ("Cookie", "*i_like_gitea*"),
+                                ],
+                            ),
+                        ],
+                    ),
+                    actions: [
+                        Respond(
+                            status: 403,
+                            body: Some("Esta vista por commit esta cerrada a visitantes anonimos: un rastreo la convierte en ficheros x commits y se come la maquina. Navega por rama (/src/branch/...), o entra con tu usuario. Clonar funciona siempre: git clone https://git.gioser.net/<dueno>/<repo>.git\n"),
+                            type: Some("text/plain; charset=utf-8"),
+                        ),
+                    ],
+                ),
+                (
+                    match: (
+                        path: [
+                            "/robots.txt",
+                        ],
+                    ),
+                    actions: [
+                        Files(
+                            root: "/var/www/git-robots",
+                            try: [],
+                            browse: false,
+                            index: [
+                                "index.html",
+                            ],
+                            on_missing: NotFound,
+                            hidden: false,
+                        ),
+                    ],
+                ),
+                (
+                    match: (
+                        not: [
+                            (
+                                path: [
+                                    "/robots.txt",
+                                ],
+                            ),
+                        ],
+                    ),
+                    actions: [
+                        Proxy(
+                            to: [
+                                "127.0.0.1:3002",
+                            ],
+                            policy: First,
+                        ),
+                    ],
+                ),
+            ],
+        ),
+        (
+            names: [
+                "git.tawasuyu.net",
+            ],
+            tls: Auto,
+            log: Some("requests"),
+            routes: [
+                (
+                    match: (
+                        header: [
+                            ("User-Agent", "*meta-externalagent*"),
+                            ("User-Agent", "*FacebookBot*"),
+                            ("User-Agent", "*GPTBot*"),
+                            ("User-Agent", "*OAI-SearchBot*"),
+                            ("User-Agent", "*ChatGPT*"),
+                            ("User-Agent", "*Amazonbot*"),
+                            ("User-Agent", "*ClaudeBot*"),
+                            ("User-Agent", "*anthropic-ai*"),
+                            ("User-Agent", "*Claude-Web*"),
+                            ("User-Agent", "*Bytespider*"),
+                            ("User-Agent", "*CCBot*"),
+                            ("User-Agent", "*Google-Extended*"),
+                            ("User-Agent", "*Applebot-Extended*"),
+                            ("User-Agent", "*PerplexityBot*"),
+                            ("User-Agent", "*Diffbot*"),
+                            ("User-Agent", "*Omgilibot*"),
+                            ("User-Agent", "*DataForSeoBot*"),
+                        ],
+                    ),
+                    actions: [
+                        Respond(
+                            status: 403,
+                            body: None,
+                            type: None,
+                        ),
+                    ],
+                ),
+                (
+                    // ⚠ FRENO ANTI-RASTREO — puesto 2026-10-21, medido, no preventivo.
+                    // El log de saywa daba 1.179.729 peticiones/día de 8.945 IPs distintas, y
+                    // el 99,5 % del coste estaba en las URL "por commit": /src|commits|blame|raw
+                    // /commit/<sha>/..., que son ficheros × commits = espacio INFINITO. Las URL
+                    // "por rama" —lo que usa una persona— eran el 0,4 %. Load 14,78 en 4 cores.
+                    // El robots.txt YA prohibía estas rutas y lo ignoran: la capa educada está
+                    // agotada, así que acá se hace cumplir. La cookie de sesión exime, o sea que
+                    // quien entra con usuario conserva los permalinks; clonar y empujar no pasan
+                    // por acá (van por /info/refs y /git-upload-pack) y no se tocan.
+                    match: (
+                        path: [
+                            "/*/*/src/commit/*",
+                            "/*/*/commits/commit/*",
+                            "/*/*/blame/commit/*",
+                            "/*/*/raw/commit/*",
+                            "/*/*/commit/*",
+                            // 2ª tanda, misma medición: con lo de arriba ya cortado, /compare se
+                            // quedó con el 96 % del coste restante (5,2 s de media por peticion).
+                            // Es refs × refs — la misma trampa combinatoria que /commit, y los
+                            // bots la recorren igual. /activity y /lastcommit son agregados caros
+                            // (vi un /activity/monthly de 42 s en UNA peticion).
+                            "/*/*/compare/*",
+                            "/*/*/activity",
+                            "/*/*/activity/*",
+                            "/*/*/lastcommit/*",
+                        ],
+                        not: [
+                            (
+                                header: [
+                                    ("Cookie", "*i_like_gitea*"),
+                                ],
+                            ),
+                        ],
+                    ),
+                    actions: [
+                        Respond(
+                            status: 403,
+                            body: Some("Esta vista por commit esta cerrada a visitantes anonimos: un rastreo la convierte en ficheros x commits y se come la maquina. Navega por rama (/src/branch/...), o entra con tu usuario. Clonar funciona siempre: git clone https://git.gioser.net/<dueno>/<repo>.git\n"),
+                            type: Some("text/plain; charset=utf-8"),
+                        ),
+                    ],
+                ),
+                (
+                    match: (
+                        path: [
+                            "/robots.txt",
+                        ],
+                    ),
+                    actions: [
+                        Files(
+                            root: "/var/www/git-robots",
+                            try: [],
+                            browse: false,
+                            index: [
+                                "index.html",
+                            ],
+                            on_missing: NotFound,
+                            hidden: false,
+                        ),
+                    ],
+                ),
+                (
+                    match: (
+                        not: [
+                            (
+                                path: [
+                                    "/robots.txt",
+                                ],
+                            ),
+                        ],
+                    ),
+                    actions: [
+                        Proxy(
+                            to: [
+                                "127.0.0.1:3002",
+                            ],
+                            policy: First,
+                        ),
+                    ],
+                ),
+            ],
+        ),
+        (
+            names: [
+                "takana.gioser.net",
+            ],
+            tls: Auto,
+            log: Some("requests"),
+            routes: [
+                (
+                    actions: [
+                        Redirect(
+                            to: "https://takana-os.net{uri}",
+                            status: 301,
+                        ),
+                    ],
+                ),
+            ],
+        ),
+        (
+            names: [
+                "hifas.gioser.net",
+            ],
+            tls: Auto,
+            log: Some("sitios"),
+            routes: [
+                (
+                    match: (
+                        not: [
+                            (
+                                path: [
+                                    "*.*",
+                                ],
+                            ),
+                        ],
+                    ),
+                    actions: [
+                        Header(
+                            name: "Cache-Control",
+                            value: Some("no-cache"),
+                            phase: Response,
+                        ),
+                    ],
+                ),
+                (
+                    match: (
+                        path: [
+                            "*.html",
+                            "*.json",
+                            "*.css",
+                            "*.js",
+                            "*.xml",
+                        ],
+                    ),
+                    actions: [
+                        Header(
+                            name: "Cache-Control",
+                            value: Some("no-cache"),
+                            phase: Response,
+                        ),
+                    ],
+                ),
+                (
+                    match: (
+                        file: Some((
+                            root: Dir("/work/sergio/gioser-web/hifas"),
+                            try: [
+                                "{path}",
+                            ],
+                        )),
+                    ),
+                    actions: [
+                        Rewrite(
+                            to: "{path}",
+                        ),
+                    ],
+                ),
+                (
+                    match: (
+                        not: [
+                            (
+                                file: Some((
+                                    root: Dir("/work/sergio/gioser-web/hifas"),
+                                    try: [
+                                        "{path}",
+                                    ],
+                                )),
+                            ),
+                        ],
+                        all: [
+                            (
+                                file: Some((
+                                    root: Dir("/work/sergio/gioser-web/hifas"),
+                                    try: [
+                                        "{path}/",
+                                    ],
+                                )),
+                            ),
+                        ],
+                    ),
+                    actions: [
+                        Rewrite(
+                            to: "{path}/",
+                        ),
+                    ],
+                ),
+                (
+                    match: (
+                        not: [
+                            (
+                                file: Some((
+                                    root: Dir("/work/sergio/gioser-web/hifas"),
+                                    try: [
+                                        "{path}",
+                                    ],
+                                )),
+                            ),
+                            (
+                                file: Some((
+                                    root: Dir("/work/sergio/gioser-web/hifas"),
+                                    try: [
+                                        "{path}/",
+                                    ],
+                                )),
+                            ),
+                        ],
+                        all: [
+                            (
+                                file: Some((
+                                    root: Dir("/work/sergio/gioser-web/hifas"),
+                                    try: [
+                                        "/index.html",
+                                    ],
+                                )),
+                            ),
+                        ],
+                    ),
+                    actions: [
+                        Rewrite(
+                            to: "/index.html",
+                        ),
+                    ],
+                ),
+                (
+                    actions: [
+                        Encode(
+                            with: [
+                                Gzip,
+                                Zstd,
+                            ],
+                        ),
+                    ],
+                ),
+                (
+                    actions: [
+                        Files(
+                            root: "/work/sergio/gioser-web/hifas",
+                            try: [],
+                            browse: false,
+                            index: [
+                                "index.html",
+                            ],
+                            on_missing: NotFound,
+                            hidden: false,
+                        ),
+                    ],
+                ),
+            ],
+        ),
+        (
+            names: [
+                "api.sergio.gioser.net",
+            ],
+            tls: Auto,
+            log: Some("requests"),
+            routes: [
+                (
+                    actions: [
+                        Proxy(
+                            to: [
+                                "127.0.0.1:8771",
+                            ],
+                            policy: First,
+                        ),
+                    ],
+                ),
+            ],
+        ),
+        (
+            names: [
+                "tawasuyu.net",
+                "www.tawasuyu.net",
+            ],
+            tls: Auto,
+            log: Some("sitios"),
+            routes: [
+                (
+                    match: (
+                        path: [
+                            "/pkg/*.wasm",
+                        ],
+                    ),
+                    actions: [
+                        Header(
+                            name: "Content-Type",
+                            value: Some("application/wasm"),
+                            phase: Response,
+                        ),
+                    ],
+                ),
+                (
+                    match: (
+                        path: [
+                            "*.webm",
+                        ],
+                    ),
+                    actions: [
+                        Header(
+                            name: "Content-Type",
+                            value: Some("video/webm"),
+                            phase: Response,
+                        ),
+                    ],
+                ),
+                (
+                    match: (
+                        not: [
+                            (
+                                path: [
+                                    "*.*",
+                                ],
+                            ),
+                        ],
+                    ),
+                    actions: [
+                        Header(
+                            name: "Cache-Control",
+                            value: Some("no-cache"),
+                            phase: Response,
+                        ),
+                    ],
+                ),
+                (
+                    match: (
+                        path: [
+                            "*.html",
+                            "*.json",
+                            "*.css",
+                            "*.js",
+                        ],
+                    ),
+                    actions: [
+                        Header(
+                            name: "Cache-Control",
+                            value: Some("no-cache"),
+                            phase: Response,
+                        ),
+                    ],
+                ),
+                (
+                    match: (
+                        path: [
+                            "/index.html",
+                        ],
+                    ),
+                    actions: [
+                        Rewrite(
+                            to: "/web/tawasuyu-web/index.html",
+                        ),
+                    ],
+                ),
+                (
+                    match: (
+                        not: [
+                            (
+                                path: [
+                                    "/index.html",
+                                ],
+                            ),
+                        ],
+                        all: [
+                            (
+                                path: [
+                                    "/styles.css",
+                                ],
+                            ),
+                        ],
+                    ),
+                    actions: [
+                        Rewrite(
+                            to: "/web/tawasuyu-web/styles.css",
+                        ),
+                    ],
+                ),
+                (
+                    match: (
+                        not: [
+                            (
+                                path: [
+                                    "/index.html",
+                                ],
+                            ),
+                            (
+                                path: [
+                                    "/styles.css",
+                                ],
+                            ),
+                        ],
+                        all: [
+                            (
+                                path: [
+                                    "/escritorio",
+                                ],
+                            ),
+                        ],
+                    ),
+                    actions: [
+                        Rewrite(
+                            to: "/web/tawasuyu-web/escritorio.html",
+                        ),
+                    ],
+                ),
+                (
+                    match: (
+                        not: [
+                            (
+                                path: [
+                                    "/index.html",
+                                ],
+                            ),
+                            (
+                                path: [
+                                    "/styles.css",
+                                ],
+                            ),
+                            (
+                                path: [
+                                    "/escritorio",
+                                ],
+                            ),
+                        ],
+                        all: [
+                            (
+                                path: [
+                                    "/galeria",
+                                ],
+                            ),
+                        ],
+                    ),
+                    actions: [
+                        Rewrite(
+                            to: "/web/tawasuyu-web/galeria.html",
+                        ),
+                    ],
+                ),
+                (
+                    match: (
+                        not: [
+                            (
+                                path: [
+                                    "/index.html",
+                                ],
+                            ),
+                            (
+                                path: [
+                                    "/styles.css",
+                                ],
+                            ),
+                            (
+                                path: [
+                                    "/escritorio",
+                                ],
+                            ),
+                            (
+                                path: [
+                                    "/galeria",
+                                ],
+                            ),
+                        ],
+                        all: [
+                            (
+                                path: [
+                                    "/pkg/*",
+                                ],
+                            ),
+                        ],
+                    ),
+                    actions: [
+                        Rewrite(
+                            to: "/web/tawasuyu-web{path}",
+                        ),
+                    ],
+                ),
+                (
+                    match: (
+                        not: [
+                            (
+                                path: [
+                                    "/index.html",
+                                ],
+                            ),
+                            (
+                                path: [
+                                    "/styles.css",
+                                ],
+                            ),
+                            (
+                                path: [
+                                    "/escritorio",
+                                ],
+                            ),
+                            (
+                                path: [
+                                    "/galeria",
+                                ],
+                            ),
+                            (
+                                path: [
+                                    "/pkg/*",
+                                ],
+                            ),
+                        ],
+                        all: [
+                            (
+                                path: [
+                                    "/",
+                                ],
+                            ),
+                        ],
+                    ),
+                    actions: [
+                        Rewrite(
+                            to: "/web/tawasuyu-web/index.html",
+                        ),
+                    ],
+                ),
+                (
+                    match: (
+                        not: [
+                            (
+                                path: [
+                                    "/index.html",
+                                ],
+                            ),
+                            (
+                                path: [
+                                    "/styles.css",
+                                ],
+                            ),
+                            (
+                                path: [
+                                    "/escritorio",
+                                ],
+                            ),
+                            (
+                                path: [
+                                    "/galeria",
+                                ],
+                            ),
+                            (
+                                path: [
+                                    "/pkg/*",
+                                ],
+                            ),
+                            (
+                                path: [
+                                    "/",
+                                ],
+                            ),
+                        ],
+                        all: [
+                            (
+                                not: [
+                                    (
+                                        path: [
+                                            "*.*",
+                                        ],
+                                    ),
+                                    (
+                                        path: [
+                                            "/",
+                                        ],
+                                    ),
+                                    (
+                                        path: [
+                                            "/visto",
+                                        ],
+                                    ),
+                                    (
+                                        file: Some((
+                                            root: Dir("/work/sergio/tawasuyu"),
+                                            try: [
+                                                "{path}",
+                                                "{path}/index.html",
+                                            ],
+                                        )),
+                                    ),
+                                ],
+                            ),
+                        ],
+                    ),
+                    actions: [
+                        Rewrite(
+                            to: "/web/tawasuyu-web/index.html",
+                        ),
+                    ],
+                ),
+                (
+                    actions: [
+                        Encode(
+                            with: [
+                                Gzip,
+                                Zstd,
+                            ],
+                        ),
+                    ],
+                ),
+                (
+                    match: (
+                        path: [
+                            "/visto",
+                        ],
+                    ),
+                    actions: [
+                        Respond(
+                            status: 204,
+                            body: None,
+                            type: None,
+                        ),
+                    ],
+                ),
+                (
+                    match: (
+                        path: [
+                            "/.*",
+                            "*/.*",
+                        ],
+                    ),
+                    actions: [
+                        Respond(
+                            status: 404,
+                            body: None,
+                            type: None,
+                        ),
+                    ],
+                ),
+                (
+                    actions: [
+                        Files(
+                            root: "/work/sergio/tawasuyu",
+                            try: [],
+                            browse: false,
+                            index: [
+                                "index.html",
+                            ],
+                            on_missing: NotFound,
+                            hidden: false,
+                        ),
+                    ],
+                ),
+            ],
+        ),
+        (
+            names: [
+                "gioser.net",
+                "www.gioser.net",
+            ],
+            tls: Auto,
+            log: Some("sitios"),
+            routes: [
+                (
+                    match: (
+                        not: [
+                            (
+                                path: [
+                                    "*.*",
+                                ],
+                            ),
+                        ],
+                    ),
+                    actions: [
+                        Header(
+                            name: "Cache-Control",
+                            value: Some("no-cache"),
+                            phase: Response,
+                        ),
+                    ],
+                ),
+                (
+                    match: (
+                        path: [
+                            "*.html",
+                            "*.json",
+                            "*.css",
+                            "*.js",
+                            "*.xml",
+                        ],
+                    ),
+                    actions: [
+                        Header(
+                            name: "Cache-Control",
+                            value: Some("no-cache"),
+                            phase: Response,
+                        ),
+                    ],
+                ),
+                (
+                    match: (
+                        path: [
+                            "/reencuentro/*",
+                        ],
+                    ),
+                    strip_prefix: Some("/reencuentro"),
+                    actions: [
+                        Header(
+                            name: "X-Robots-Tag",
+                            value: Some("noindex, nofollow, noarchive, noimageindex"),
+                            phase: Response,
+                        ),
+                    ],
+                ),
+                (
+                    match: (
+                        path: [
+                            "/reencuentro/*",
+                        ],
+                    ),
+                    strip_prefix: Some("/reencuentro"),
+                    actions: [
+                        Encode(
+                            with: [
+                                Gzip,
+                                Zstd,
+                            ],
+                        ),
+                    ],
+                ),
+                (
+                    match: (
+                        path: [
+                            "/reencuentro/*",
+                        ],
+                    ),
+                    strip_prefix: Some("/reencuentro"),
+                    actions: [
+                        FastCgi(
+                            to: "127.0.0.1:9000",
+                            root: "/work/www/gioser-web/reencuentro",
+                            index: "index.php",
+                        ),
+                    ],
+                ),
+                (
+                    match: (
+                        path: [
+                            "/reencuentro/*",
+                        ],
+                    ),
+                    strip_prefix: Some("/reencuentro"),
+                    actions: [
+                        Files(
+                            root: "/work/www/gioser-web/reencuentro",
+                            try: [],
+                            browse: false,
+                            index: [
+                                "index.html",
+                            ],
+                            on_missing: NotFound,
+                            hidden: false,
+                        ),
+                    ],
+                ),
+                (
+                    match: (
+                        not: [
+                            (
+                                path: [
+                                    "/reencuentro/*",
+                                ],
+                            ),
+                        ],
+                        all: [
+                            (
+                                path: [
+                                    "/humanoid/*",
+                                ],
+                            ),
+                        ],
+                    ),
+                    strip_prefix: Some("/humanoid"),
+                    actions: [
+                        Encode(
+                            with: [
+                                Gzip,
+                            ],
+                        ),
+                    ],
+                ),
+                (
+                    match: (
+                        not: [
+                            (
+                                path: [
+                                    "/reencuentro/*",
+                                ],
+                            ),
+                        ],
+                        all: [
+                            (
+                                path: [
+                                    "/humanoid/*",
+                                ],
+                            ),
+                        ],
+                    ),
+                    strip_prefix: Some("/humanoid"),
+                    actions: [
+                        Files(
+                            root: "/work/www/gioser-web/humanoid",
+                            try: [],
+                            browse: false,
+                            index: [
+                                "index.html",
+                            ],
+                            on_missing: NotFound,
+                            hidden: false,
+                        ),
+                    ],
+                ),
+                (
+                    match: (
+                        not: [
+                            (
+                                path: [
+                                    "/reencuentro/*",
+                                ],
+                            ),
+                            (
+                                path: [
+                                    "/humanoid/*",
+                                ],
+                            ),
+                        ],
+                        all: [
+                            (
+                                path: [
+                                    "/muestras/*",
+                                ],
+                            ),
+                        ],
+                    ),
+                    strip_prefix: Some("/muestras"),
+                    actions: [
+                        Encode(
+                            with: [
+                                Gzip,
+                            ],
+                        ),
+                    ],
+                ),
+                (
+                    match: (
+                        not: [
+                            (
+                                path: [
+                                    "/reencuentro/*",
+                                ],
+                            ),
+                            (
+                                path: [
+                                    "/humanoid/*",
+                                ],
+                            ),
+                        ],
+                        all: [
+                            (
+                                path: [
+                                    "/muestras/*",
+                                ],
+                            ),
+                        ],
+                    ),
+                    strip_prefix: Some("/muestras"),
+                    actions: [
+                        Files(
+                            root: "/work/www/gioser-web/muestras",
+                            try: [],
+                            browse: true,
+                            index: [
+                                "index.html",
+                            ],
+                            on_missing: NotFound,
+                            hidden: false,
+                        ),
+                    ],
+                ),
+                (
+                    match: (
+                        not: [
+                            (
+                                path: [
+                                    "/reencuentro/*",
+                                ],
+                            ),
+                            (
+                                path: [
+                                    "/humanoid/*",
+                                ],
+                            ),
+                            (
+                                path: [
+                                    "/muestras/*",
+                                ],
+                            ),
+                        ],
+                        all: [
+                            (
+                                path: [
+                                    "/vivo/*",
+                                ],
+                            ),
+                        ],
+                    ),
+                    strip_prefix: Some("/vivo"),
+                    actions: [
+                        Encode(
+                            with: [
+                                Gzip,
+                            ],
+                        ),
+                    ],
+                ),
+                (
+                    match: (
+                        not: [
+                            (
+                                path: [
+                                    "/reencuentro/*",
+                                ],
+                            ),
+                            (
+                                path: [
+                                    "/humanoid/*",
+                                ],
+                            ),
+                            (
+                                path: [
+                                    "/muestras/*",
+                                ],
+                            ),
+                        ],
+                        all: [
+                            (
+                                path: [
+                                    "/vivo/*",
+                                ],
+                            ),
+                        ],
+                    ),
+                    strip_prefix: Some("/vivo"),
+                    actions: [
+                        Files(
+                            root: "/work/www/gioser-web/vivo",
+                            try: [],
+                            browse: true,
+                            index: [
+                                "index.html",
+                            ],
+                            on_missing: NotFound,
+                            hidden: false,
+                        ),
+                    ],
+                ),
+                (
+                    match: (
+                        not: [
+                            (
+                                path: [
+                                    "/reencuentro/*",
+                                ],
+                            ),
+                            (
+                                path: [
+                                    "/humanoid/*",
+                                ],
+                            ),
+                            (
+                                path: [
+                                    "/muestras/*",
+                                ],
+                            ),
+                            (
+                                path: [
+                                    "/vivo/*",
+                                ],
+                            ),
+                        ],
+                        all: [
+                            (
+                                path: [
+                                    "/ende/*",
+                                ],
+                            ),
+                        ],
+                    ),
+                    strip_prefix: Some("/ende"),
+                    actions: [
+                        Encode(
+                            with: [
+                                Gzip,
+                                Zstd,
+                            ],
+                        ),
+                    ],
+                ),
+                (
+                    match: (
+                        not: [
+                            (
+                                path: [
+                                    "/reencuentro/*",
+                                ],
+                            ),
+                            (
+                                path: [
+                                    "/humanoid/*",
+                                ],
+                            ),
+                            (
+                                path: [
+                                    "/muestras/*",
+                                ],
+                            ),
+                            (
+                                path: [
+                                    "/vivo/*",
+                                ],
+                            ),
+                        ],
+                        all: [
+                            (
+                                path: [
+                                    "/ende/*",
+                                ],
+                            ),
+                        ],
+                    ),
+                    strip_prefix: Some("/ende"),
+                    actions: [
+                        Files(
+                            root: "/work/www/gioser-web/ende",
+                            try: [],
+                            browse: true,
+                            index: [
+                                "index.html",
+                            ],
+                            on_missing: NotFound,
+                            hidden: false,
+                        ),
+                    ],
+                ),
+                (
+                    match: (
+                        not: [
+                            (
+                                path: [
+                                    "/reencuentro/*",
+                                ],
+                            ),
+                            (
+                                path: [
+                                    "/humanoid/*",
+                                ],
+                            ),
+                            (
+                                path: [
+                                    "/muestras/*",
+                                ],
+                            ),
+                            (
+                                path: [
+                                    "/vivo/*",
+                                ],
+                            ),
+                            (
+                                path: [
+                                    "/ende/*",
+                                ],
+                            ),
+                        ],
+                        all: [
+                            (
+                                path: [
+                                    "/visto",
+                                ],
+                            ),
+                        ],
+                    ),
+                    actions: [
+                        Respond(
+                            status: 204,
+                            body: None,
+                            type: None,
+                        ),
+                    ],
+                ),
+                (
+                    match: (
+                        not: [
+                            (
+                                path: [
+                                    "/reencuentro/*",
+                                ],
+                            ),
+                            (
+                                path: [
+                                    "/humanoid/*",
+                                ],
+                            ),
+                            (
+                                path: [
+                                    "/muestras/*",
+                                ],
+                            ),
+                            (
+                                path: [
+                                    "/vivo/*",
+                                ],
+                            ),
+                            (
+                                path: [
+                                    "/ende/*",
+                                ],
+                            ),
+                            (
+                                path: [
+                                    "/visto",
+                                ],
+                            ),
+                        ],
+                        all: [
+                            (
+                                path: [
+                                    "/scripts",
+                                    "/scripts/*",
+                                    "/.*",
+                                    "/.*/*",
+                                ],
+                            ),
+                        ],
+                    ),
+                    actions: [
+                        Respond(
+                            status: 404,
+                            body: None,
+                            type: None,
+                        ),
+                    ],
+                ),
+                (
+                    match: (
+                        not: [
+                            (
+                                path: [
+                                    "/reencuentro/*",
+                                ],
+                            ),
+                            (
+                                path: [
+                                    "/humanoid/*",
+                                ],
+                            ),
+                            (
+                                path: [
+                                    "/muestras/*",
+                                ],
+                            ),
+                            (
+                                path: [
+                                    "/vivo/*",
+                                ],
+                            ),
+                            (
+                                path: [
+                                    "/ende/*",
+                                ],
+                            ),
+                            (
+                                path: [
+                                    "/visto",
+                                ],
+                            ),
+                            (
+                                path: [
+                                    "/scripts",
+                                    "/scripts/*",
+                                    "/.*",
+                                    "/.*/*",
+                                ],
+                            ),
+                        ],
+                    ),
+                    actions: [
+                        Encode(
+                            with: [
+                                Gzip,
+                                Zstd,
+                            ],
+                        ),
+                    ],
+                ),
+                (
+                    match: (
+                        not: [
+                            (
+                                path: [
+                                    "/reencuentro/*",
+                                ],
+                            ),
+                            (
+                                path: [
+                                    "/humanoid/*",
+                                ],
+                            ),
+                            (
+                                path: [
+                                    "/muestras/*",
+                                ],
+                            ),
+                            (
+                                path: [
+                                    "/vivo/*",
+                                ],
+                            ),
+                            (
+                                path: [
+                                    "/ende/*",
+                                ],
+                            ),
+                            (
+                                path: [
+                                    "/visto",
+                                ],
+                            ),
+                            (
+                                path: [
+                                    "/scripts",
+                                    "/scripts/*",
+                                    "/.*",
+                                    "/.*/*",
+                                ],
+                            ),
+                        ],
+                    ),
+                    actions: [
+                        Files(
+                            root: "/work/sergio/gioser-web/",
+                            try: [],
+                            browse: false,
+                            index: [
+                                "index.html",
+                            ],
+                            on_missing: NotFound,
+                            hidden: false,
+                        ),
+                    ],
+                ),
+            ],
+        ),
+        (
+            names: [
+                "sergio.gioser.net",
+            ],
+            tls: Auto,
+            log: Some("requests"),
+            routes: [
+                (
+                    match: (
+                        path: [
+                            "/shuma/*",
+                        ],
+                    ),
+                    strip_prefix: Some("/shuma"),
+                    actions: [
+                        Proxy(
+                            to: [
+                                "127.0.0.1:7378",
+                            ],
+                            policy: First,
+                        ),
+                    ],
+                ),
+                (
+                    match: (
+                        all: [
+                            (
+                                not: [
+                                    (
+                                        path: [
+                                            "/shuma/*",
+                                        ],
+                                    ),
+                                ],
+                            ),
+                            (
+                                file: Some((
+                                    root: Dir("/work/www/sergioh"),
+                                    try: [
+                                        "{path}",
+                                    ],
+                                )),
+                            ),
+                        ],
+                    ),
+                    actions: [
+                        Rewrite(
+                            to: "{path}",
+                        ),
+                    ],
+                ),
+                (
+                    match: (
+                        not: [
+                            (
+                                file: Some((
+                                    root: Dir("/work/www/sergioh"),
+                                    try: [
+                                        "{path}",
+                                    ],
+                                )),
+                            ),
+                        ],
+                        all: [
+                            (
+                                all: [
+                                    (
+                                        not: [
+                                            (
+                                                path: [
+                                                    "/shuma/*",
+                                                ],
+                                            ),
+                                        ],
+                                    ),
+                                    (
+                                        file: Some((
+                                            root: Dir("/work/www/sergioh"),
+                                            try: [
+                                                "{path}/",
+                                            ],
+                                        )),
+                                    ),
+                                ],
+                            ),
+                        ],
+                    ),
+                    actions: [
+                        Rewrite(
+                            to: "{path}/",
+                        ),
+                    ],
+                ),
+                (
+                    match: (
+                        not: [
+                            (
+                                file: Some((
+                                    root: Dir("/work/www/sergioh"),
+                                    try: [
+                                        "{path}",
+                                    ],
+                                )),
+                            ),
+                            (
+                                file: Some((
+                                    root: Dir("/work/www/sergioh"),
+                                    try: [
+                                        "{path}/",
+                                    ],
+                                )),
+                            ),
+                        ],
+                        all: [
+                            (
+                                all: [
+                                    (
+                                        not: [
+                                            (
+                                                path: [
+                                                    "/shuma/*",
+                                                ],
+                                            ),
+                                        ],
+                                    ),
+                                    (
+                                        file: Some((
+                                            root: Dir("/work/www/sergioh"),
+                                            try: [
+                                                "/index.html",
+                                            ],
+                                        )),
+                                    ),
+                                ],
+                            ),
+                        ],
+                    ),
+                    actions: [
+                        Rewrite(
+                            to: "/index.html",
+                        ),
+                    ],
+                ),
+                (
+                    match: (
+                        not: [
+                            (
+                                path: [
+                                    "/shuma/*",
+                                ],
+                            ),
+                        ],
+                    ),
+                    actions: [
+                        Encode(
+                            with: [
+                                Gzip,
+                            ],
+                        ),
+                    ],
+                ),
+                (
+                    match: (
+                        not: [
+                            (
+                                path: [
+                                    "/shuma/*",
+                                ],
+                            ),
+                        ],
+                    ),
+                    actions: [
+                        Files(
+                            root: "/work/www/sergioh",
+                            try: [],
+                            browse: false,
+                            index: [
+                                "index.html",
+                            ],
+                            on_missing: NotFound,
+                            hidden: false,
+                        ),
+                    ],
+                ),
+            ],
+        ),
+        (
+            names: [
+                "repo.gioser.net",
+            ],
+            tls: Auto,
+            log: Some("repo"),
+            routes: [
+                (actions: [Files(root: "/srv/repo", browse: true)]),
+            ],
+        ),
+        (
+            names: [
+                "takana-os.net",
+                "www.takana-os.net",
+            ],
+            tls: Auto,
+            log: Some("sitios"),
+            routes: [
+                (
+                    match: (
+                        not: [
+                            (
+                                path: [
+                                    "*.*",
+                                ],
+                            ),
+                        ],
+                    ),
+                    actions: [
+                        Header(
+                            name: "Cache-Control",
+                            value: Some("no-cache"),
+                            phase: Response,
+                        ),
+                    ],
+                ),
+                (
+                    match: (
+                        path: [
+                            "*.html",
+                            "*.json",
+                            "*.css",
+                            "*.js",
+                            "*.xml",
+                        ],
+                    ),
+                    actions: [
+                        Header(
+                            name: "Cache-Control",
+                            value: Some("no-cache"),
+                            phase: Response,
+                        ),
+                    ],
+                ),
+                (
+                    match: (
+                        path: [
+                            "/descargas",
+                        ],
+                    ),
+                    actions: [
+                        Redirect(
+                            to: "/descargas/",
+                            status: 301,
+                        ),
+                    ],
+                ),
+                (
+                    match: (
+                        path: [
+                            "/repo",
+                        ],
+                    ),
+                    actions: [
+                        Redirect(
+                            to: "/repo/",
+                            status: 301,
+                        ),
+                    ],
+                ),
+                (
+                    match: (
+                        not: [
+                            (
+                                path: [
+                                    "/descargas/*",
+                                ],
+                            ),
+                        ],
+                    ),
+                    actions: [
+                        Encode(
+                            with: [
+                                Gzip,
+                                Zstd,
+                            ],
+                        ),
+                    ],
+                ),
+                (
+                    match: (
+                        path: [
+                            "/descargas/*",
+                        ],
+                    ),
+                    strip_prefix: Some("/descargas"),
+                    actions: [
+                        Header(
+                            name: "Content-Disposition",
+                            value: Some("attachment"),
+                            phase: Response,
+                        ),
+                    ],
+                ),
+                (
+                    match: (
+                        path: [
+                            "/descargas/*",
+                        ],
+                    ),
+                    strip_prefix: Some("/descargas"),
+                    actions: [
+                        Files(
+                            root: "/work/sergio/descargas",
+                            try: [],
+                            browse: true,
+                            index: [
+                                "index.html",
+                            ],
+                            on_missing: NotFound,
+                            hidden: false,
+                        ),
+                    ],
+                ),
+                (
+                    match: (
+                        not: [
+                            (
+                                path: [
+                                    "/descargas/*",
+                                ],
+                            ),
+                        ],
+                        all: [
+                            (
+                                path: [
+                                    "/repo/*",
+                                ],
+                            ),
+                        ],
+                    ),
+                    strip_prefix: Some("/repo"),
+                    actions: [
+                        Files(
+                            root: "/srv/repo",
+                            try: [],
+                            browse: true,
+                            index: [
+                                "index.html",
+                            ],
+                            on_missing: NotFound,
+                            hidden: false,
+                        ),
+                    ],
+                ),
+                (
+                    match: (
+                        not: [
+                            (
+                                path: [
+                                    "/descargas/*",
+                                ],
+                            ),
+                            (
+                                path: [
+                                    "/repo/*",
+                                ],
+                            ),
+                        ],
+                        all: [
+                            (
+                                path: [
+                                    "/visto",
+                                ],
+                            ),
+                        ],
+                    ),
+                    actions: [
+                        Respond(
+                            status: 204,
+                            body: None,
+                            type: None,
+                        ),
+                    ],
+                ),
+                (
+                    match: (
+                        all: [
+                            (
+                                not: [
+                                    (
+                                        path: [
+                                            "/descargas/*",
+                                        ],
+                                    ),
+                                    (
+                                        path: [
+                                            "/repo/*",
+                                        ],
+                                    ),
+                                    (
+                                        path: [
+                                            "/visto",
+                                        ],
+                                    ),
+                                ],
+                            ),
+                            (
+                                path: [
+                                    "/.*",
+                                    "*/.*",
+                                    "/README.md",
+                                ],
+                            ),
+                        ],
+                    ),
+                    actions: [
+                        Respond(
+                            status: 404,
+                            body: None,
+                            type: None,
+                        ),
+                    ],
+                ),
+                (
+                    match: (
+                        not: [
+                            (
+                                path: [
+                                    "/descargas/*",
+                                ],
+                            ),
+                            (
+                                path: [
+                                    "/repo/*",
+                                ],
+                            ),
+                            (
+                                path: [
+                                    "/visto",
+                                ],
+                            ),
+                        ],
+                    ),
+                    actions: [
+                        Files(
+                            root: "/work/sergio/takana/web/takana-os.net",
+                            try: [],
+                            browse: false,
+                            index: [
+                                "index.html",
+                            ],
+                            on_missing: NotFound,
+                            hidden: false,
+                        ),
+                    ],
+                ),
+            ],
+        ),
+    ],
+    snippets: {},
+    egress: None,
+    tls: (
+        state_dir: "/var/lib/saywa",
+        acme_directory: "https://acme-v02.api.letsencrypt.org/directory",
+        email: None,
+        renew_days: 30,
+        challenge: Http01,
+    ),
+    logs: [
+        (name: "repo", file: "/var/log/saywa/repo.json", ip: Truncated),
+        (
+            name: "requests",
+            file: "/var/log/caddy/requests.json",
+            ip: Full,
+            roll_mb: 50,
+            roll_keep: 14,
+            roll_keep_days: 30,
+            format: Json,
+        ),
+        (
+            name: "sitios",
+            file: "/var/log/caddy/sitios.json",
+            ip: Full,
+            roll_mb: 20,
+            roll_keep: 60,
+            roll_keep_days: 90,
+            format: Json,
+        ),
+    ],
+    limits: (
+        max_header_bytes: 65536,
+        max_body_bytes: 10485760,
+        header_timeout: 10,
+        idle_timeout: 120,
+        max_conns_per_ip: 256,
+        max_h2_streams: 128,
+        drain_timeout: 30,
+        rate_per_ip: 0,
+        body_timeout: 20,
+        min_body_rate: 500,
+        max_inflight: 4096,
+        ban_after: 200,
+        ban_secs: 600,
+        ban_file: Some("/var/lib/saywa/castigo.txt"),
+    ),
+    trusted_proxies: [],
+    store: None,
+    trees: {},
+    functions: None,
+)
\ No newline at end of file