Arquitectura: qué corre, y cómo comprobarlo
Math Challenge corre enteramente sobre Cloudflare. Esta página separa lo que ya está corriendo de lo que solo está diseñado, y da el comando que comprueba cada afirmación.
Lo que está corriendo hoy
math-challenge-web— Un Worker con Astro y Static Assets, que sirve los siete locales y las rutas que hay detrás.math-challenge-ingest— Un segundo Worker sin ninguna ruta pública. Solo se alcanza por service binding desde el primero.math-challenge-db— Una base D1 en la región WNAM. La jurisdicción se fija al crear la base y ya no se puede cambiar — una puerta que se cerró el día que se creó, y que está escrita en vez de descubrirse después.- Dos espacios de KV: los tokens de sesión de corta vida, y las banderas de función con el caché del catálogo de contenido.
- Dos cubetas de R2: los medios, y el archivo frío donde van a escribirse las exportaciones de datos personales.
- Analytics Engine, con su binding declarado y reservado para la telemetría de intentos y el rendimiento de campo. Todavía nadie le escribe: un conjunto de datos de Analytics Engine nace apenas la primera vez que un Worker le escribe.
Todo lo demás del inventario está diseñado y no creado. Está listado más abajo, por nombre, porque una página que solo enseña las piezas que existen es un folleto.
HTTP/3, QUIC y 0-RTT
HTTP/3 sobre QUIC está encendido en la zona, y también la reanudación 0-RTT, que le permite a un visitante que regresa mandar la primera petición sin pagar un saludo nuevo. Ninguna de las dos se afirma a partir de una página de documentación: los dos renglones de abajo se leyeron directo de producción, con la misma comprobación que corre después de cada despliegue. Un interruptor que alguien apaga en un tablero no deja rastro en el repositorio: el código no se enteraría.
alt-svc: h3=":443"; ma=86400
TLS 1.3 · Max Early Data: 14336 Lo que esto no arregla: HTTP/3 acelera el transporte, no el trabajo. Un paquete pesado de JavaScript bloquea el hilo principal sobre QUIC exactamente igual que sobre TCP. Por eso el presupuesto de peso de más abajo importa más que el protocolo.
RPC nativo entre Workers
Los dos Workers se hablan por un service binding, con llamadas a métodos tipados en vez de peticiones HTTP. Cloudflare describe lo que eso cuesta:
When you use Service Bindings, there is zero overhead or added latency. By default, both Workers run on the same thread of the same Cloudflare server.
RPC to another Worker (over a Service Binding) usually does not even cross a network. In fact, the other Worker usually runs in the very same thread as the caller, reducing latency to zero.
Un RPC que no cruza la red no puede ser superado por uno que sí, por eficiente que sea su serialización. El camino completo —primer Worker, segundo Worker, D1— responde en un endpoint público de salud, así que se puede comprobar en lugar de creerse.
Por qué no gRPC
gRPC se pidió por su nombre. La investigación lo descartó por tres hechos independientes, cada uno suficiente por sí solo:
- Workers no puede hacer llamadas gRPC salientes. El runtime no tiene streaming bidireccional de HTTP/2, y hay un issue abierto en el repositorio del propio runtime de Cloudflare que lo sigue.
- El navegador no habla gRPC. El cliente web implementa un protocolo distinto, cae a HTTP/1.1 —lo cual cancela algunas de las ventajas— y no soporta ni streaming de cliente ni llamadas bidireccionales.
- Los trailers de HTTP, que gRPC necesita para llevar el estado, tienen soporte limitado en el proxy de borde de Cloudflare. Lo documentó Cloudflare mismo.
Lo que quedó no es un premio de consolación. Adoptar gRPC aquí habría sido adoptar la generación anterior con más trabajo y menos resultado. La petición y la evidencia que la revirtió están las dos en la bitácora pública de decisiones: para eso sirve tener una.
Cero peticiones a terceros
Sin CDN de fuentes, sin script de analítica, sin marco embebido, sin hoja de estilo externa. Ni una petición sale hacia un servidor que no sea nuestro. Esto se comprueba contra el HTML que producción sirve de verdad, no contra nuestras intenciones.
Raleway está autoalojada como fuente variable, en dos archivos: latin y latin-ext. El segundo no es opcional: sin él, la ñ, la ç, la ã, la õ y la ü de cinco de nuestros locales caen a otra tipografía, justo en los caracteres que los hacen esos locales. Y la razón que pesa más que el rendimiento: cargar una fuente de un tercero le entrega a ese tercero la dirección y la firma del navegador de cada visitante, en las páginas públicas de un producto para niños.
La inferencia corre dentro de la red
Los 23 auditores adversariales ya corren sobre Workers AI, y el tutor va para allá también: la inferencia se queda en Cloudflare en vez de irse a una API externa. La llamada ocurre dentro del Worker, sin viaje externo, y el dispositivo de referencia es un teléfono Android de gama baja sobre datos móviles lentos, donde la latencia cuesta tanto como el precio.
Lo que eso cede está escrito en vez de maquillado. El modo JSON de Workers AI es de mejor esfuerzo, así que la validación de esquema nos toca a nosotros. Un veredicto que no valida cuenta como auditor fallido, jamás como auditor limpio: el modo de falla barato es volver a correrlo, y el caro es una violación que nadie vio.
Los intentos no van a D1
D1 topa en 10 GB por base de datos. Un renglón por intento llegaría a esa pared por error de diseño y no por crecimiento: el primer límite duro de esta arquitectura se alcanza equivocándose, no teniendo éxito. Los intentos crudos van a Analytics Engine; D1 se queda con las cuentas, los metadatos de contenido y los acumulados. Un auditor determinista bloquea el commit que se le olvide.
Medimos nosotros, y nunca sobre un niño
El rendimiento de campo lo medimos nosotros y nunca un tercero: un script en línea de unos 600 bytes que reporta a un endpoint nuestro, el cual escribe a Analytics Engine — jamás a D1 y jamás a un servicio de fuera. Nada de lo que manda identifica a una persona, y nunca se carga en la pantalla de un niño: el endpoint también rechaza esas bandas, así que los dos extremos fallan cerrados. Un auditor vigila el código; la comprobación de producción vigila la otra mitad, porque la plataforma puede inyectar una baliza propia a nivel de zona y eso no deja archivo que mirar.
Lo que pesan las páginas
Página más pesada: 9.5 KB comprimidos con gzip, que es como viajan. Todo el JavaScript de cliente del sitio: 2.6 KB. Todo el CSS: 1.5 KB. El presupuesto que bloquea un commit es de 12 KB por página y 60 KB para todo el JavaScript de cliente junto.
El presupuesto de rendimiento, y lo que todavía no afirmamos
El presupuesto es INP por debajo de 150 ms, LCP por debajo de 2.5 s y CLS por debajo de 0.1. 150 y no los 200 de costumbre, porque esto es un juego de alta frecuencia de interacción: un niño toca muchas veces por sesión y cada toque se mide. Es el perfil exacto donde INP se rompe.
Dicho sin adornos: eso son presupuestos, no resultados. Ninguna medición de campo lleva el tiempo suficiente para que el auditor que hace cumplir los umbrales tenga algo que hacer cumplir. Hasta entonces, esta página no afirma ningún resultado de rendimiento.
La flota de auditores
8 auditores deterministas corren en cada commit, en milisegundos, y lo bloquean. 23 auditores adversariales con modelos de lenguaje corren antes de abrir un PR, instruidos para encontrar la violación y no para aprobar. En total hay 39 planeados; 8 siguen esperando la fase que los habilita, y están listados como pendientes en vez de desaparecidos en silencio.
Dos reglas los hacen servir en vez de estorbar. Cada auditor tiene que citar la decisión o el documento de investigación que hace cumplir: uno que no pueda señalar un identificador real está opinando, y su veredicto no bloquea. Y anular a un auditor exige escribir por qué, en un archivo que se commitea junto al cambio que necesitó la anulación.
Lo que todavía no existe
El inventario se escribe antes de que los objetos existan, para que quien cree uno anote su renglón en la bitácora en el mismo PR. Un recurso creado y no documentado es un recurso que nadie va a poder borrar dentro de un año. Esto está diseñado y no creado:
- Durable Objects para ligas, salones y el modelo adaptativo por niño — uno por liga y uno por salón, nunca uno global.
- Queues y Workflows para la calificación asíncrona y la generación de explicaciones.
- Un índice de Vectorize para recuperar sobre el banco de pistas curadas, acotado a contenido curado y nunca a vectores por niño.
- El banco de ítems mismo, que es el producto y no un conjunto de datos.
Quién opera esto, y quién lo provee
Ignia opera la infraestructura. Cada objeto que esta página nombra vive en una cuenta de Cloudflare que opera Ignia — operar cuentas de Cloudflare, para sus propios productos y para sus clientes, es parte de lo que Ignia hace. Math Challenge es un proyecto de Ignia, patrocinado por Ignia.
Cloudflare provee la plataforma. Workers, D1, KV, R2, Analytics Engine y la red sobre la que corren son productos de Cloudflare, operados por Cloudflare. Ignia opera; Cloudflare provee. Dos verbos, dos empresas, separados a propósito — y la razón de separarlos es la misma sobre la que está armada toda esta página: el lector de una página de arquitectura es el que comprueba.
dig +short NS math.kilowatto.com
alberto.ns.cloudflare.com.
colette.ns.cloudflare.com. La otra mitad no se puede consultar en ningún lado, y lo decimos. Que este dominio lo sirva Cloudflare cualquiera lo confirma. Que Ignia opere la cuenta es una afirmación nuestra: no hay registro público donde ir a comprobarla. Decirlo así vale más que adornarlo, porque basta una sola afirmación que no se pueda comprobar para que el lector deje de comprobar las demás.
Lo que esta página no afirma, a propósito: que Ignia sea partner de Cloudflare. No nombramos ningún nivel de partner ni enseñamos ninguna insignia, porque no hay directorio público donde pudieras verificarlo. En una página cuya promesa entera es «y así se comprueba», una afirmación que no se puede comprobar cuesta más de lo que vale.
Ignia, en sus propias palabras: ignia.cloud
Cómo comprobar cualquiera de estas cosas
- el anuncio de HTTP/3 en las cabeceras de respuesta
curl -sI https://math.kilowatto.com/en/ | grep alt-svc- 0-RTT: el ticket de sesión de TLS 1.3 y su permiso de datos tempranos
openssl s_client -connect math.kilowatto.com:443 -tls1_3 -sess_out s.pem openssl sess_id -in s.pem -text | grep "Max Early Data"- el camino de RPC: primer Worker, segundo Worker, D1
curl -s https://math.kilowatto.com/api/health- los pesos citados arriba, desde el repositorio
node audits/bundle-budget.mjs
Fuentes
- About Service bindings — Cloudflare Workers docs
- We've added JavaScript-native RPC to Cloudflare Workers
- Support HTTP/2 bidirectional streaming (gRPC) in Workers/Durable Objects — cloudflare/workerd issue #6455
- gRPC Web (PROTOCOL-WEB) — gRPC core documentation
- Road to gRPC — Cloudflare blog
- HTTP/3 (with QUIC) — Cloudflare Speed docs
- D1 Platform limits — Cloudflare docs
- JSON Mode — Cloudflare Workers AI docs
Math Challenge es un proyecto de Ignia, y corre sobre Cloudflare. Son dos afirmaciones y se mantienen separadas a propósito: decir que Ignia provee el stack sería desmentible con una sola consulta de DNS, y el lector de una página de arquitectura es exactamente quien la haría. ignia.cloud