Architecture : ce qui tourne, et comment le vérifier
Math Challenge fonctionne entièrement sur Cloudflare. Cette page sépare ce qui tourne aujourd’hui de ce qui n’est que conçu, et donne la commande qui prouve chaque affirmation.
Ce qui tourne aujourd’hui
math-challenge-web— Un Worker qui exécute Astro avec Static Assets, et sert les sept locales ainsi que les routes qui se trouvent derrière.math-challenge-ingest— Un second Worker sans aucune route publique. On ne l’atteint que par un service binding depuis le premier.math-challenge-db— Une base D1 dans la région WNAM. La juridiction se fixe à la création de la base et ne peut plus être modifiée ensuite — une porte refermée le jour même de sa création, et qui est écrite au lieu d’être découverte plus tard.- Deux espaces KV : les jetons de session de courte durée, et les indicateurs de fonctionnalité avec le cache du catalogue de contenu.
- Deux compartiments R2 : les médias, et l’archive froide où seront écrites les exportations de données personnelles.
- Analytics Engine, déclaré et réservé à la télémétrie des tentatives et aux performances de terrain. Personne n’y écrit encore : un jeu de données Analytics Engine ne naît qu’à la première écriture d’un Worker.
Tout le reste de l’inventaire est conçu et non créé. La liste figure plus bas, nom par nom, parce qu’une page qui ne montre que les pièces existantes est une brochure.
HTTP/3, QUIC et 0-RTT
HTTP/3 sur QUIC est actif sur la zone, ainsi que la reprise 0-RTT, qui permet à un visiteur qui revient d’envoyer sa première requête sans payer une nouvelle poignée de main. Aucune des deux n’est affirmée à partir d’une page de documentation : les deux lignes ci-dessous ont été lues directement en production, par la vérification qui tourne après chaque déploiement. Un interrupteur que quelqu’un éteint dans un tableau de bord ne laisse aucune trace dans le dépôt : le code ne s’en apercevrait pas.
alt-svc: h3=":443"; ma=86400
TLS 1.3 · Max Early Data: 14336 Ce que cela ne règle pas : HTTP/3 accélère le transport, pas le travail. Un paquet JavaScript lourd bloque le fil principal sur QUIC exactement comme sur TCP. C’est pourquoi le budget de poids plus bas compte davantage que le protocole.
RPC natif entre Workers
Les deux Workers se parlent par un service binding, avec des appels de méthodes typées plutôt que des requêtes HTTP. Cloudflare décrit ce que cela coûte :
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 qui ne traverse pas le réseau ne peut pas être battu par un RPC qui le traverse, si efficace que soit sa sérialisation. Le chemin complet — premier Worker, second Worker, D1 — répond sur un point de contrôle public, donc il se vérifie au lieu de se croire.
Pourquoi pas gRPC
gRPC a été demandé nommément. La recherche l’a écarté sur trois faits indépendants, chacun suffisant à lui seul :
- Workers ne peut pas émettre d’appels gRPC sortants. Le runtime n’a pas de streaming bidirectionnel HTTP/2, et un ticket ouvert dans le dépôt du runtime de Cloudflare en assure le suivi.
- Le navigateur ne parle pas gRPC non plus. Le client web implémente un autre protocole, retombe sur HTTP/1.1 — ce qui annule une partie des avantages — et ne prend en charge ni le streaming client ni les appels bidirectionnels.
- Les trailers HTTP, dont gRPC a besoin pour transporter le statut, ne sont que partiellement pris en charge par le proxy de bordure de Cloudflare. Cloudflare l’a documenté elle-même.
Ce qui reste n’est pas un lot de consolation. Adopter gRPC ici aurait été adopter la génération précédente avec plus de travail pour moins de résultat. La demande et les preuves qui l’ont renversée figurent toutes deux dans le journal public des décisions : c’est à cela qu’il sert.
Zéro requête vers un tiers
Pas de CDN de polices, pas de script d’analytique, pas de cadre intégré, pas de feuille de style externe. Pas une seule requête ne part vers un serveur qui ne soit pas le nôtre. Cela se vérifie contre le HTML que la production sert réellement, pas contre nos intentions.
Raleway est auto-hébergée comme police variable, en deux fichiers : latin et latin-ext. Le second n’est pas facultatif : sans lui, le ñ, le ç, le ã, le õ et le ü de cinq de nos locales retombent sur une autre police, précisément sur les caractères qui en font ces locales. Et la raison qui pèse plus lourd que la performance : charger une police chez un tiers, c’est lui remettre l’adresse et la signature de navigateur de chaque visiteur, sur les pages publiques d’un produit destiné à des enfants.
L’inférence tourne à l’intérieur du réseau
Les 23 auditeurs adverses tournent déjà sur Workers AI, et le tuteur ira au même endroit : l’inférence reste chez Cloudflare au lieu de partir vers une API externe. L’appel a lieu à l’intérieur du Worker, sans aller-retour externe, et l’appareil de référence est un téléphone Android d’entrée de gamme sur données mobiles lentes, où la latence coûte autant que le prix.
Ce que cela concède est écrit plutôt que maquillé. Le mode JSON de Workers AI est au mieux-effort, la validation de schéma nous revient donc. Un verdict qui ne valide pas compte comme un auditeur en échec, jamais comme un auditeur propre : le mode de défaillance bon marché est de relancer, le mode coûteux est une violation que personne n’a vue.
Les tentatives ne vont jamais dans D1
D1 plafonne à 10 Go par base de données. Une ligne par tentative atteindrait ce mur par erreur de conception et non par croissance : la première limite dure de cette architecture s’atteint en se trompant, pas en réussissant. Les tentatives brutes vont dans Analytics Engine ; D1 garde les comptes, les métadonnées de contenu et les agrégats. Un auditeur déterministe bloque le commit qui l’oublierait.
Nous mesurons nous-mêmes, et jamais un enfant
Les performances de terrain, c’est nous qui les mesurons et jamais un tiers : un script en ligne d’environ 600 octets qui rend compte à un point d’entrée qui nous appartient, lequel écrit dans Analytics Engine — jamais dans D1 et jamais chez un service extérieur. Rien de ce qu’il envoie n’identifie une personne, et il n’est jamais chargé sur l’écran d’un enfant : le point d’entrée refuse aussi ces tranches, si bien que les deux extrémités échouent en position fermée. Un auditeur garde le code ; la vérification en production garde l’autre moitié, car la plateforme peut injecter une balise à elle au niveau de la zone et cela ne laisse aucun fichier à inspecter.
Ce que pèsent les pages
Page la plus lourde : 9,5 Ko compressés en gzip, ce qui correspond à la façon dont elles voyagent. Tout le JavaScript client du site : 2,6 Ko. Tout le CSS : 1,5 Ko. Le budget qui bloque un commit est de 12 Ko par page et de 60 Ko pour l’ensemble du JavaScript client.
Le budget de performance, et ce que nous n’affirmons pas encore
Le budget est INP sous 150 ms, LCP sous 2,5 s, CLS sous 0,1. 150 et non les 200 habituels, parce que c’est un jeu à forte fréquence d’interaction : un enfant touche l’écran de nombreuses fois par session et chaque contact est mesuré. C’est exactement le profil où INP casse.
Dit sans détour : ce sont des budgets, pas des résultats. Aucune mesure de terrain n’a duré assez longtemps pour que l’auditeur qui fait respecter les seuils ait quoi que ce soit à faire respecter. D’ici là, cette page n’affirme aucun résultat de performance.
La flotte d’auditeurs
8 auditeurs déterministes tournent à chaque commit, en quelques millisecondes, et le bloquent. 23 auditeurs adverses appuyés sur des modèles de langage tournent avant l’ouverture d’une pull request, avec pour consigne de trouver la violation et non d’approuver. 39 sont prévus au total ; 8 attendent encore la phase qui les active, et sont listés comme en attente plutôt que discrètement abandonnés.
Deux règles les rendent utiles au lieu d’encombrants. Chaque auditeur doit citer la décision ou le document de recherche qu’il fait respecter : celui qui ne peut pas désigner un identifiant réel donne un avis, et son verdict ne bloque pas. Et annuler un auditeur exige d’écrire pourquoi, dans un fichier commité avec le changement qui a nécessité l’annulation.
Ce qui n’existe pas encore
L’inventaire s’écrit avant que les objets existent, pour que celui qui en crée un inscrive sa ligne au journal dans la même pull request. Une ressource créée et non documentée est une ressource que personne ne saura supprimer dans un an. Voici ce qui est conçu et non créé :
- Des Durable Objects pour les ligues, les classes et le modèle adaptatif par enfant — un par ligue et un par classe, jamais un objet global.
- Queues et Workflows pour la correction asynchrone et la génération d’explications.
- Un index Vectorize pour la recherche sur la banque d’indices vérifiés, limité au contenu vérifié et jamais à des vecteurs par enfant.
- La banque d’items elle-même, qui est le produit et non un jeu de données.
Qui exploite tout cela, et qui le fournit
Ignia exploite l’infrastructure. Chaque objet nommé sur cette page vit dans un compte Cloudflare qu’Ignia administre — exploiter des comptes Cloudflare, pour ses propres produits comme pour ses clients, fait partie de ce que fait Ignia. Math Challenge est un projet d’Ignia, financé par Ignia.
Cloudflare fournit la plateforme. Workers, D1, KV, R2, Analytics Engine et le réseau sur lequel ils tournent sont des produits de Cloudflare, exploités par Cloudflare. Ignia exploite ; Cloudflare fournit. Deux verbes, deux sociétés, tenus séparés à dessein — et la raison de les tenir séparés est celle sur laquelle repose toute cette page : le lecteur d’une page d’architecture est celui qui vérifie.
dig +short NS math.kilowatto.com
alberto.ns.cloudflare.com.
colette.ns.cloudflare.com. L’autre moitié ne se consulte nulle part, et nous le disons. Que ce domaine soit servi par Cloudflare, n’importe qui peut le confirmer. Qu’Ignia exploite le compte est une affirmation qui vient de nous : aucun registre public ne permet de la recouper. Le dire ainsi vaut mieux que l’habiller, car il suffit d’une seule affirmation invérifiable pour que le lecteur cesse de vérifier les autres.
Ce que cette page n’affirme pas, volontairement : qu’Ignia soit partenaire de Cloudflare. Nous ne citons aucun niveau de partenariat et n’affichons aucun badge, parce qu’il n’existe aucun annuaire public où vous pourriez le vérifier. Sur une page dont toute la promesse est « et voici comment le vérifier », une affirmation invérifiable coûte plus qu’elle ne rapporte.
Ignia, dans ses propres mots : ignia.cloud
Comment vérifier tout cela
- l’annonce de HTTP/3 dans les en-têtes de réponse
curl -sI https://math.kilowatto.com/en/ | grep alt-svc- 0-RTT : le ticket de session TLS 1.3 et son autorisation de données anticipées
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"- le chemin RPC : premier Worker, second Worker, D1
curl -s https://math.kilowatto.com/api/health- les poids cités plus haut, depuis le dépôt
node audits/bundle-budget.mjs
Sources
- 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 est un projet d’Ignia, et il tourne sur Cloudflare. Ce sont deux affirmations, tenues séparées à dessein : dire qu’Ignia fournit l’infrastructure serait démenti par une seule requête DNS, et le lecteur d’une page d’architecture est exactement la personne qui la lancerait. ignia.cloud