Saltar a contenido

Registro de trabajo

2026-07-27 — registro del trabajo con el que se levantó esta plataforma documental, se cambió el modelo de propiedad de la configuración del edge, se cerraron los puntos débiles operativos y se tradujo toda la documentación a siete idiomas.

El propósito de esta página no es anotar qué se hizo, sino por qué se decidió así. El razonamiento es la información que envejece más despacio: el código puede cambiar, pero la justificación de una decisión permanece.

Alcance

Los detalles operativos — direcciones de host, rutas, valores secretos de configuración — no forman parte de este sitio público; viven en un runbook cerrado dentro del repositorio correspondiente.

Cinco líneas de trabajo

# Trabajo Resultado
1 Levantar la plataforma documental docs.gerege.mn entró en servicio
2 Descomponer la propiedad de la configuración del edge Cada dominio pasó a su propio repositorio
3 Cerrar los puntos débiles operativos Eliminados el borrado accidental y los puntos ciegos
4 Traducir la documentación a siete idiomas Mongol + los seis idiomas oficiales de la ONU, cobertura completa
5 Dejar constancia del paso a Nexus El nuevo modelo del ecosistema documentado en siete idiomas

1. La plataforma documental

Qué se hizo

Un portal en MkDocs Material que reúne en un solo lugar la documentación a nivel de ecosistema de Gerege: 25 páginas, mongol por defecto, diagramas Mermaid, CSS de marca. La cobertura lingüística se amplió después a siete idiomas — véase la sección 4.

El contenido se recopiló de los README, los directorios docs/ y los documentos de arquitectura de los repositorios del ecosistema, y se ordenó en cuatro partes: capas · plataformas · estándares · operación.

Decisiones clave

Servir el sitio estático desde un contenedor aparte. Añadir un mount nuevo al contenedor del edge nginx obliga a recrearlo, y en ese momento todos los dominios caen un instante. Sirviéndolo desde su propio contenedor pequeño, al edge le basta con una adición de configuración y un reload.

Desplegar in situ, con rsync. El directorio del sitio se monta por bind en el contenedor. Sustituir el directorio entero (mv) deja al contenedor mirando todavía al inode antiguo, y el contenido nuevo no aparece nunca. rsync actualiza los ficheros en su sitio, así que el mount sigue siendo válido.

Anclas para títulos en cirílico. El slugify estándar de la extensión toc de Python-Markdown elimina los caracteres no ASCII: el título ## Танилт recibe un id vacío y los enlaces internos se rompen en silencio. Se pasó al pymdownx.slugs.slugify, que conserva el unicode.

validation.anchors activado. En MkDocs, la comprobación de anclas viene desactivada por defecto. Sin ella, un enlace roto que apunta a #sección supera la construcción y llega a producción. Ahora hace fallar --strict.

Todo lo que entra en docs/ se hace público. Por eso los detalles operativos se colocaron aparte, en un directorio que no forma parte del sitio.


2. La propiedad de la configuración del edge

Fue el mayor cambio arquitectónico.

Cómo era antes

Los vhosts de todos los dominios vivían en un único fichero central. La consecuencia: cambiar un ajuste pequeño de docs.gerege.mn obligaba a enviar un PR a otro repositorio y esperar al despliegue de otro equipo. Propiedad difusa y cambios lentos.

Cómo es ahora

el directorio de configuración del edge nginx
├── (ficheros compartidos)    ← los posee el repositorio de la stack combinada
│     sso · dan · gsign · xyp
├── docs.gerege.mn.conf       ← docs-gerege-mn
├── developer.gerege.mn.conf  ← developer-gerege-mn
└── template.gerege.mn.conf   ← template-gerege-mn

El vhost de cada dominio vive ahora en el repositorio de su servicio, y lo instala su propio despliegue. Un cambio se completa dentro de un solo repositorio.

Por qué funciona

El directorio de configuración está físicamente dentro de la copia de trabajo de otro repositorio, y el despliegue de ese repositorio ejecuta git reset --hard. Pero git reset --hard solo restaura los ficheros con seguimiento: no toca los untracked. Así que el fichero instalado desde un repositorio ajeno sobrevive.

Tres condiciones para ser plenamente autónomo

Un vhost no debe depender de los ficheros compartidos en nada:

Condición Por qué
Su propia limit_req_zone Recurrir a un fichero de zonas compartido crea dependencia
Su propio bloque listen 80 (ACME + redirección) Así la renovación del certificado funciona sola
Reinstalación en cada despliegue Si el fichero se pierde, se restablece por sí mismo

Un orden de migración sin interrupciones

  1. Primero instalar el fichero nuevo. En ese momento un mismo dominio queda definido en dos sitios, pero nginx lo trata solo como un aviso conflicting server name: el comportamiento no cambia, ambos apuntan al mismo upstream.
  2. Después quitarlo del fichero central. La duplicidad desaparece y el fichero nuevo entra en vigor.

Si se hace al revés, el dominio cae entre los dos pasos.

Sin rutas del host en un repositorio de código abierto

template-gerege-mn es de código abierto, y la convención vigente era guardar los datos del servidor solo en los secrets de la CI. Por eso el instalador deduce él mismo la ruta del directorio de configuración a partir de los mounts del contenedor edge:

docker inspect <edge> --format \
  '{{range .Mounts}}{{if eq .Destination "/etc/nginx/conf.d"}}{{.Source}}{{end}}{{end}}'

Esto es más fiable que codificar la ruta a mano, así que después se adoptó en los tres repositorios: sigue funcionando cuando cambia el host o la ruta.

Un requisito previo de seguridad

PRIMERO el certificado, después el vhost. Añadir un vhost HTTPS sin certificado hace fallar nginx -t, y en ese momento todos los dominios quedan en riesgo. El desafío ACME funciona a través del default server general del puerto 80, así que obtener el certificado no exige cambiar la configuración.

Antes del push, la configuración final se verificó con nginx -t en un contenedor temporal, con la red y los certificados reales.


3. Endurecimiento operativo

Protección frente al borrado accidental

Había ficheros que sostienen producción y no estaban con seguimiento en ningún repositorio. git reset --hard no los toca, pero git clean -fd los borra.

Fichero Si se pierde Solución
Los vhosts de tres dominios 3 dominios caen a la vez .gitignore
El override de compose del host El contenedor se desengancha de la red edge → 502 .gitignore + un fichero de ejemplo

Por qué lo resuelve .gitignore: sin -x, git clean omite los ficheros ignorados. Eso los protege sin necesidad de darles seguimiento.

El override de compose no debe llevar seguimiento: compose lo lee automáticamente, así que la configuración de producción se impondría en el entorno local de cada desarrollador. Por eso el fichero vivo permanece untracked en el host y en el repositorio solo se guarda un ejemplo de restauración. Que el ejemplo produce el mismo resultado que la configuración viva se confirmó comparando la salida de docker compose config.

Monitorización de salud

Ya existía antes un script de monitorización en el host, pero coincidieron dos defectos: no estaba registrado en cron en absoluto, y algunos de los contenedores que nombraba habían cambiado de nombre y ya no existían. El script omite en silencio un contenedor ausente, así que nadie sabía que la monitorización se había detenido por completo.

Decisiones clave de la nueva versión:

El healthcheck propio del contenedor va primero. Su interval y sus retries ya están ajustados a ese servicio concreto.

El probe HTTP se hace desde dentro del contenedor. Comprobarlo todo a través del edge haría que, al caer el edge, todos los servicios parecieran caídos, lo que provocaría un reinicio masivo y ocultaría el fallo real. Ahora cada comprobación es independiente.

Umbral de fallos consecutivos más cooldown. Una demora transitoria (despliegue, GC, carga) no provoca reinicio, y un servicio realmente roto no se apaga y enciende en bucle.

No reiniciar automáticamente la infraestructura stateful. Las bases de datos y la caché solo se vigilan y se registran. Un reinicio no arregla una causa real como un disco lleno; corta las transacciones de muchas stacks y solo añade daño. En tal caso decide una persona.

Un contenedor ausente se registra como error, para no repetir el defecto principal de la versión anterior.

Umbral · cooldown · política sobre stateful · recuperación · contenedor ausente: los cinco comportamientos se verificaron de verdad en un contenedor de prueba aislado.

Renovación de certificados

Ver una entrada en cron no basta: que la renovación funcione de verdad se probó con --dry-run, confirmando que todos los dominios se renuevan con éxito. Es el tipo de riesgo que permanece callado hasta la fecha de caducidad.


4. Cobertura en siete idiomas

Qué se hizo

Todas las páginas del sitio se tradujeron a mongol más los seis idiomas oficiales de las Naciones Unidas: العربية · 中文 · English · Français · Русский · Español. La traducción inglesa era antes parcial (portada, introducción, capas, listado de plataformas, autenticación); se completó y se añadieron cinco idiomas más.

Decisiones clave

El mongol sigue siendo la fuente. Los otros seis son traducciones: el original se escribe en mongol y desde ahí se convierte. Con dos idiomas «fuente», el contenido empieza a divergir en silencio.

Traducir por páginas, no por idiomas. Convertir un mismo documento a seis idiomas a la vez mantiene idénticas la terminología, las filas de las tablas y la estructura. Al revés — «primero todas las páginas en inglés» — los idiomas traducidos después irían persiguiendo un texto de origen ya modificado.

La notación técnica no se tradujo. Dominios, nombres de repositorio, código, YAML, rutas de endpoints y nombres de estándares (OIDC · PKCE · RFC 3161) quedan tal cual en todos los idiomas. Traducirlos los volvería imposibles de copiar y ejecutar.

Los nombres de fichero no se traducen. Es platforms/sso.ru.md, no платформы/sso.md. Así la ruta de la URL no cambia al pasar de idioma, y los enlaces profundos que llegan de otros repositorios siguen funcionando.

El RTL árabe no se hizo a mano. Material reconoce el locale ar, pone <html dir="rtl"> y voltea por sí mismo el menú y el flujo del contenido. Los bloques de código y los diagramas ASCII se quedan en LTR — y es lo correcto, porque los comandos y las URL pierden su sentido si se invierte la dirección.

El slugify unicode es ahora tres veces más importante. El pymdownx.slugs.slugify que se introdujo por los títulos en cirílico sostiene ahora también las anclas de los títulos árabes, chinos y rusos. Con el slugify estándar se habrían roto en silencio todos los enlaces internos de seis locales.

fallback_to_default sigue activado. Ahora todas las páginas están traducidas, así que el fallback no se dispara; pero queda como garantía de que el sitio seguirá completo cuando se añada una página y su traducción vaya con retraso.

Alcance

Esta política se aplica a la documentación a nivel de ecosistema, es decir, solo a este sitio. La documentación técnica profunda de cada plataforma (esquemas de endpoints, referencia de SDK) se mantiene en MN + EN dentro de su propio repositorio: quien la lee es un ingeniero que ya trabaja en ese repositorio, de modo que ampliar la cobertura aportaría poco.


5. Dejar constancia del paso a Nexus (2026-08-07)

Qué ocurrió

El repositorio open-gerege-nexus se creó el 2026-08-05 y, el 08-07, la plataforma pasó a llamarse Gerege Nexus y se trasladó a nexus.gerege.mn. Después llegaron dos forks: sso-gerege-nexus (Gerege SSO) y eduge-mn-nexus (eduge.mn). Como esto cambia el modelo de distribución del ecosistema, la documentación se puso al día en los siete idiomas.

Decisiones clave

No se eliminó ninguna página de plataforma existente. Template, Gerege Platform, SSO y Kiosk siguen en producción. Se añadió una página nueva y las existentes llevan un aviso que indica qué estado rige. Borrar una página habría borrado la documentación de un sistema en marcha.

La capa 3 se dividió en dos generaciones. Nexus no sustituye a la Template: ambas ocupan la capa 3 a la vez. Añadir una capa nueva habría vaciado de sentido la propia regla de capas («nunca saltarse una capa»).

El proveedor OIDC propio de Nexus NO es la capa 2. Nexus lleva un proveedor OAuth2/OIDC, pero sirve a los inquilinos y clientes de terceros de ese despliegue. La vía del ecosistema para identificar a un ciudadano sigue siendo Gerege SSO. Sin decirlo, el lector concluiría que «Nexus ha sustituido al SSO».

Cada dominio se comprobó a mano. Se confirmó que nexus.gerege.mn, eduge.mn y geregekiosk.mn están activos mediante DNS, HTTP y sus certificados TLS. De ahí salieron dos cosas: geregekiosk.mn sirve ahora Nexus, y open.gerege.mn ha sido retirado del certificado de ese host, así que HTTPS falla por discrepancia de nombre.

Se corrigió una promesa caducada. La página de Template afirmaba que el autosync lleva los cambios aguas abajo a diario; esa automatización se detuvo en toda la flota el 2026-08-06. Una promesa falsa es peor que un dato ausente: el ingeniero espera que el arreglo viaje solo.

Se cerraron dos lagunas de traducción. La sección «Dominios de marca propios» del mapa de dominios faltaba por completo en las seis traducciones; ahora las siete están al mismo nivel.

Resumen de decisiones

Decisión Justificación
Sitio estático en su propio contenedor Recrear el edge tumba todos los dominios
rsync, no mv Un bind mount se queda en el inode antiguo
Slugify unicode El slugify estándar destruye las anclas de los títulos cirílicos
Activar validation.anchors Si no, los enlaces rotos llegan a producción
El vhost en el repositorio del servicio Un cambio se completa dentro de un repositorio
Un vhost totalmente autónomo Depender de un fichero compartido deja la separación sin sentido
Instalar antes, quitar después Al revés, el dominio se cae
Deducir la ruta automáticamente Más fiable que fijarla a mano; no deja rutas en un repo abierto
Proteger con .gitignore git clean omite los ficheros ignorados
No dar seguimiento al override Compose lo lee solo — la config de prod se aplicaría en local
Probe desde dentro del contenedor Evita un reinicio masivo cuando cae el edge
No reiniciar lo stateful Un reinicio no arregla la causa, añade daño
El mongol como única lengua de origen Con dos «fuentes», el contenido diverge en silencio
Traducir por página, no por idioma Terminología y estructura quedan idénticas en seis idiomas
No traducir código, dominios ni nombres de repos Traducidos, dejan de poder copiarse y ejecutarse
No traducir los nombres de fichero La ruta de la URL no cambia y los enlaces profundos funcionan
Cada idioma en una subruta (/ar/) Con subdominios crecerían de golpe SAN · vhost · hreflang

Deliberadamente no hecho

No se separaron los dominios sso · dan · gsign · xyp. Su código vive dentro del repositorio de la stack combinada, así que el fichero central ya es su propio repositorio. No hay motivo para separarlos.

No se añadió un mount aparte al contenedor del edge. Eso habría exigido modificar el fichero compose de la stack combinada y, además, recrear el contenedor, tumbando un instante todos los dominios.

No se forzó un índice de búsqueda en mongol. lunr.js no admite el mongol, así que la búsqueda en el locale por defecto funciona con tokenización estándar. Escribir un stemmer propio costaría más de lo que hoy aporta.

Ni dominio ni subdominio aparte por idioma. Rutas del tipo /ar/ y /zh/ se resuelven con un certificado, un vhost y un despliegue. Pasar a subdominios habría aumentado de golpe el SAN del certificado, la configuración del edge y el hreflang.

Riesgo residual

Los vhosts de los tres dominios y los ficheros de override del host están protegidos con .gitignore, pero si alguien ejecuta git clean -fdx (que sí incluye los ficheros ignorados) se borrarán. La forma de arreglarlo es volver a ejecutar el script de instalación del repositorio correspondiente; queda anotado en el runbook de cada uno de los tres repositorios.