Math Challenge
Plus

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.

Dernière vérification en production:

Ce qui tourne aujourd’hui

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.

Lu en production à la date ci-dessus :

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.

Cloudflare, documentation des Service Bindings et annonce du RPC natif JavaScript

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 :

  1. 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.
  2. 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.
  3. 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.

Mesuré à la date ci-dessus. Lancez vous-même l’auditeur et vous obtenez le chiffre au lieu de notre parole.

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éé :

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.

L’une de ces deux moitiés tient dans une requête DNS publique. Lue à la date ci-dessus :

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

  1. About Service bindings — Cloudflare Workers docs
  2. We've added JavaScript-native RPC to Cloudflare Workers
  3. Support HTTP/2 bidirectional streaming (gRPC) in Workers/Durable Objects — cloudflare/workerd issue #6455
  4. gRPC Web (PROTOCOL-WEB) — gRPC core documentation
  5. Road to gRPC — Cloudflare blog
  6. HTTP/3 (with QUIC) — Cloudflare Speed docs
  7. D1 Platform limits — Cloudflare docs
  8. 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