El catálogo
El catálogo es donde Airframe guarda su intención: un repositorio git de pequeños archivos YAML que dicen qué productos existen, quién es responsable de ellos y qué necesita cada uno. Todo lo demás que construye la plataforma, desde los proyectos en la nube hasta los paneles, se deriva de estos archivos. Por eso, cambiar la organización significa cambiar un archivo, mediante un merge request que aprueban las personas correctas.
Por qué la intención vive en archivos
Sección titulada «Por qué la intención vive en archivos»La mayoría de las organizaciones guarda esta información en varios lugares a la vez: una planilla de servicios, un formulario de un portal, las membresías de una docena de herramientas. Esas copias se van separando, y nadie puede decir cuál es la correcta.
Airframe guarda una sola copia. Como el catálogo es un repositorio git, cada cambio es un merge request: se revisa antes de ocurrir, queda registrado con quién lo hizo y por qué, y es fácil de deshacer. No hay un inventario aparte que mantener sincronizado, porque el catálogo es el inventario.
Cómo se divide la organización
Sección titulada «Cómo se divide la organización»El catálogo describe la organización como un árbol de cuatro niveles:
- un recorrido es un área amplia del negocio; la organización de demostración tiene tres:
shop,fulfilysettle; - un dominio está dentro de un recorrido, como
storeenshop; - un contexto está dentro de un dominio, como
orderingenstore: un grupo de productos muy relacionados y los equipos que los construyen; - un producto está dentro de un contexto, como
ordersenordering.
Cada nivel es un archivo con el nombre de su ruta completa: journeys/shop.yaml, domains/shop.store.yaml, contexts/shop.store.ordering.yaml y products/shop.store.ordering.orders.yaml. Un archivo nombra a los líderes de su nivel, las personas que aprueban algunos tipos de cambio debajo de él. Un nombre solo tiene que ser único entre sus hermanos, porque la ruta es la identidad, y las verificaciones del catálogo rechazan un archivo cuyo padre no existe.
Otros dos tipos de archivo describen a las personas. Un archivo de equipo, como teams/store-core.yaml, dice en qué contexto vive el equipo, quién lo lidera, quiénes son sus miembros y qué rol tiene cada uno (desarrollador, líder de equipo, guardia), y cómo funciona su rotación de guardias. Un archivo de persona tiene un nombre, una dirección de correo en el dominio de la organización y un estado: activa, pendiente (todavía sin cuenta) o desvinculada.
Qué contiene el archivo de un producto
Sección titulada «Qué contiene el archivo de un producto»Estos son los campos principales del archivo del producto de pedidos, tal como están en el catálogo de demostración:
product: ordersjourney: shopdomain: storecontext: orderingteam: store-corecriticality: 2data_classification: internalrto: 1hrpo: 15mmonthly_budget: 5stack: nodeslo: { availability: 99.5, latency_p95_ms: 400 }sandbox: truelifecycle: { status: retiring, retire_after: "2099-12-31" }En palabras simples:
teames el equipo responsable del producto; sus miembros pueden aprobar cambios en él.criticalitydice cuánto sufre el negocio cuando el producto falla, de 1 (detiene el flujo de pedidos) a 3 (herramientas internas). Define, por ejemplo, el nivel de la base de datos y los umbrales de las alertas.data_classificationdice qué tan sensibles son sus datos, depublicarestricted; un producto restringido recibe un repositorio privado en una parte restringida de GitLab.rtoyrposon sus objetivos de recuperación: qué tan rápido debe volver después de una falla, y cuántos datos recientes puede perder. Definen las copias de seguridad de su base de datos.monthly_budgetestá en dólares estadounidenses, producción y no producción juntas. Es un límite, no una nota: un merge request cuyos planes con precio lo superan no se puede integrar.stackelige la aplicación que genera el golden path: Node, Python o Java.sloes su objetivo de confiabilidad, que usan sus reglas de alerta.sandboxpide una copia estable de no producción contra la que otros productos pueden probar.lifecycledice si el producto está activo, en retiro o eliminado.
Qué se deriva de él
Sección titulada «Qué se deriva de él»Cuando se integra el archivo de un producto, los reconciliadores crean, entre otras cosas:
- el repositorio
projectairframe/shop/store/ordering/orders, con su primer commit generado desde la plantilla del golden path; - dos proyectos de Google Cloud,
lk-store-orders-<rnd>ylk-store-orders-np-<rnd>, con una identidad de plan y una de apply en cada uno; - el grupo de Google de los usuarios del producto, que es el público que su nombre de host admite detrás del inicio de sesión único;
- sus paneles y reglas de alerta en Grafana;
- una vez que sus proyectos tienen facturación, un presupuesto de Google Cloud en cada uno, 70 por ciento del presupuesto mensual para producción y 30 para no producción.
La facturación en sí no está en el catálogo: los proyectos de un producto se facturan al agregarlo a una lista en el repositorio landing-zone, en un merge request aparte. La página de reconciliadores explica la cadena que hace este trabajo.
Por qué dos proyectos por producto
Sección titulada «Por qué dos proyectos por producto»Cada producto recibe dos proyectos de Google Cloud: uno para producción y otro para todo lo que no es producción, como sus vistas previas y su sandbox. Es el diseño, no un paso hacia compartirlos: la fábrica de proyectos del dominio del producto crea ambos a partir de la entrada del catálogo, para cada producto.
La razón es el límite de permisos. Las identidades de plan y de apply de un producto tienen sus roles en los proyectos del propio producto, así que la identidad que aplica el Terraform de un producto puede cambiar ese producto y nada más. Un error en ese código, o un token de pipeline robado, puede dañar como mucho un producto. Un proyecto compartido por varios productos no puede ofrecer eso: una identidad capaz de cambiar uno de ellos podría cambiarlos todos. Las cuotas y los registros de auditoría se llevan por proyecto, y la fábrica da a cada proyecto con facturación su propio presupuesto, así que cada producto tiene los suyos. El segundo proyecto deja las vistas previas, sus identidades y sus datos de prueba fuera del límite de producción.
Lo que cuestan más proyectos, y lo que no:
- Google Cloud no cobra nada por un proyecto. Lo que sí se multiplica con los productos es lo que cada producto tiene para sí, como su propia instancia de Cloud SQL, y eso viene de mantener separados los productos, no del número de proyectos.
- Nadie crea ni administra a mano el proyecto de un producto. La fábrica crea cada uno con sus identidades, permisos y etiquetas, y le da un presupuesto una vez que tiene facturación; los reconciliadores conectan a la red compartida los que tienen facturación. El equipo de plataforma mantiene entradas del catálogo, no proyectos.
- La mayoría de las cuotas de Google Cloud se aplican por proyecto, como el número de instancias de Cloud SQL y de cuentas de servicio y la tasa de solicitudes a las API. Proyectos separados dan a cada producto sus propios límites, así que el pipeline muy activo de un equipo no puede agotar la cuota de API de otro equipo.
Dos cosas sí crecen con el número de proyectos, y conviene revisarlas antes de adoptar Airframe:
- Proyectos por cuenta de facturación. Una cuenta de facturación solo puede vincular a la facturación un número limitado de proyectos, y cada producto con facturación ocupa dos. La de Airframe podía vincular cinco al principio. Subir el límite es una solicitud a Google, como explican los requisitos previos.
- Herramientas con precio por proyecto. Si tu organización paga una herramienta según el número de proyectos de Google Cloud, como cobran algunos productos de seguridad y de monitoreo, cuenta dos por cada producto.
La palanca es qué cuenta como producto. La unidad es el límite de una carga de trabajo, no cada aplicación. Varias herramientas pequeñas de un equipo, con la misma criticidad y la misma clasificación de datos, pueden ser un producto con varios servicios: un par de proyectos y un conjunto de identidades de pipeline. El diseño agrega dos refinamientos, y ninguno está construido. Un producto con una parte que difiere en criticidad o clasificación de datos podría declarar esa parte como un componente, y solo el componente recibiría un par de proyectos propio: el catálogo acepta el campo components, y ninguna fábrica de proyectos lo lee todavía (diseñado). Y los productos podrían compartir cómputo, en espacios de nombres de un clúster de GKE por entorno, mientras sus datos, identidades, secretos y estado se quedan en sus propios proyectos: todavía no existe ningún clúster (diseñado). Las páginas de campos del catálogo y de traer un sistema existente dicen más.
El acceso no se otorga proyecto por proyecto. El acceso de lectura de un equipo a los proyectos de sus productos y a sus registros viene del catálogo: la fábrica da esos roles en cada proyecto de los productos del equipo al grupo de Google del equipo, y la raíz de grupos llena el grupo desde el archivo del equipo. La concesión administrativa que una persona puede pedir durante un incidente, el nivel 2, se define una vez, en la carpeta de la organización de demostración, así que cubre todos los proyectos de productos, también los nuevos. Identidades y acceso y las concesiones explican ambas cosas.
Quién aprueba qué
Sección titulada «Quién aprueba qué»El catálogo también dice quién debe aprobar cada cambio, en un archivo: ownership/protected-paths.yaml, el modelo de responsables. Para los cambios al propio catálogo, dice:
- el archivo de un producto lo aprueba el equipo del producto, o los líderes de su contexto o de su dominio;
- subir el presupuesto de un producto, o permitirle usar un servicio nuevo de Google Cloud, necesita a un líder del dominio;
- un cambio de estado del ciclo de vida necesita dos aprobadores: uno del equipo del producto y otro de los líderes del dominio o del equipo de plataforma, y el merge request debe llevar la etiqueta
lifecycle; - el archivo de un equipo lo aprueban sus líderes o los líderes de su contexto;
- el perfil propio de una persona necesita a esa persona: su aprobación, o un merge request que ella abrió y cuyos commits son todos suyos; el resto de su archivo necesita a los líderes de sus equipos;
- la estructura, es decir, los recorridos, dominios y contextos, el modelo de responsables, el esquema y los servicios aprobados, necesita a dos miembros del equipo de plataforma.
El autor de un cambio y los autores de sus commits nunca cuentan como aprobadores. Una persona que cambia su propio perfil igual necesita la aprobación de otra persona con nombre, que la política de aprobación de todo el grupo exige en cada merge. En la demostración, los líderes ficticios no tienen cuenta en GitLab, así que el equipo de plataforma los reemplaza.
El mismo archivo también cubre el repositorio de cada producto: por ejemplo, su directorio infra/ necesita la aprobación del equipo, y su archivo de pipeline una sola aprobación, del equipo o del equipo de la plataforma. La compuerta de aprobación lo lee directamente en cada merge request, siempre desde la rama main protegida del catálogo, nunca desde el cambio que juzga. Cada repositorio también tiene un archivo CODEOWNERS. El de un producto se escribe una sola vez, cuando se crea el repositorio, y nombra al grupo de su contexto, y los archivos de los repositorios de la plataforma no se comparan con el modelo. El del propio catálogo se genera a partir del modelo con el mismo generador de la compuerta, y el trabajo catalog:codeowners de control-plane compara los dos después de cada merge al catálogo, donde una diferencia genera una advertencia, y cada día, donde una diferencia falla y un issue queda abierto hasta que un merge request en el catálogo vuelve a generar el archivo. En todos los casos es la compuerta, no CODEOWNERS, la que aplica el modelo.
Ciclo de vida: activo, en retiro, eliminado
Sección titulada «Ciclo de vida: activo, en retiro, eliminado»Un producto nunca se borra sin más. Pasa por tres estados, y cada uno es un cambio en su archivo:
- active: el producto funciona.
- retiring: sus servicios salen del borde compartido y bajan a cero instancias, y se conservan todos los proyectos, bases de datos y buckets.
- removed: permitido solo después de la fecha
retire_afterde la entrada. Se eliminan los proyectos y el repositorio del producto.
Un producto eliminado deja una lápida: un archivo en retired-names/, como retired-names/shop.store.returns.yaml para un producto eliminado el 2026-09-14. Las verificaciones del catálogo rechazan cualquier producto nuevo con ese nombre en todo el dominio, y rechazan cualquier cambio en una lápida, así que un nombre nunca se reutiliza.
Dónde está
En el repositorio catalog:
- products/: un archivo por producto
- teams/: un archivo por equipo
- ownership/protected-paths.yaml: el modelo de responsables
- schema/: los JSON Schemas contra los que se revisa cada archivo
- retired-names/: las lápidas
- scripts/lint.mjs: las verificaciones de coherencia del catálogo
En el repositorio landing-zone:
