Architektur: was läuft, und wie man es nachprüft
Math Challenge läuft vollständig auf Cloudflare. Diese Seite trennt, was heute läuft, von dem, was nur entworfen ist, und nennt den Befehl, der jede Aussage belegt.
Was heute läuft
math-challenge-web— Ein Worker mit Astro und Static Assets, der die sieben Locales und die dahinterliegenden Routen ausliefert.math-challenge-ingest— Ein zweiter Worker ganz ohne öffentliche Route. Er ist nur über ein Service Binding vom ersten aus erreichbar.math-challenge-db— Eine D1-Datenbank in der Region WNAM. Die Rechtsordnung wird beim Anlegen festgelegt und lässt sich danach nicht mehr ändern — eine Tür, die am Tag der Erstellung zufiel, und die aufgeschrieben ist, statt später entdeckt zu werden.- Zwei KV-Namensräume: kurzlebige Sitzungstoken, und Funktionsschalter samt Zwischenspeicher des Inhaltskatalogs.
- Zwei R2-Buckets: die Medien, und das Kaltarchiv, in das die Auskunfts- und Exportanfragen geschrieben werden.
- Analytics Engine, gebunden und reserviert für die Versuchstelemetrie und die Feldmessung der Leistung. Noch schreibt nichts hinein: Ein Analytics-Engine-Datensatz entsteht erst, wenn ein Worker zum ersten Mal hineinschreibt.
Alles Übrige aus dem Bestandsverzeichnis ist entworfen und nicht angelegt. Es steht weiter unten namentlich da, denn eine Seite, die nur die vorhandenen Teile zeigt, ist ein Prospekt.
HTTP/3, QUIC und 0-RTT
HTTP/3 über QUIC ist für die Zone eingeschaltet, und die 0-RTT-Wiederaufnahme ebenfalls: Wer wiederkommt, schickt die erste Anfrage, ohne einen neuen Handschlag zu bezahlen. Keines von beidem wird aus einer Dokumentationsseite behauptet: Die beiden Zeilen unten wurden direkt aus der Produktion gelesen, von derselben Prüfung, die nach jeder Auslieferung läuft. Ein Schalter, den jemand in einer Oberfläche ausschaltet, hinterlässt keine Spur im Repository — der Code würde es nicht merken.
alt-svc: h3=":443"; ma=86400
TLS 1.3 · Max Early Data: 14336 Was das nicht behebt: HTTP/3 beschleunigt den Transport, nicht die Arbeit. Ein schweres JavaScript-Bündel blockiert den Hauptthread über QUIC genauso wie über TCP. Deshalb zählt das Gewichtsbudget weiter unten mehr als das Protokoll.
Natives RPC zwischen Workers
Die beiden Workers sprechen über ein Service Binding miteinander, als typisierte Methodenaufrufe statt als HTTP-Anfragen. Cloudflare beschreibt, was das kostet:
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.
Ein RPC, das kein Netz überquert, kann von einem, das es überquert, nicht geschlagen werden — so effizient dessen Serialisierung auch sein mag. Der ganze Weg — erster Worker, zweiter Worker, D1 — antwortet auf einem öffentlichen Statusendpunkt, also lässt er sich nachprüfen statt glauben.
Warum kein gRPC
gRPC wurde ausdrücklich verlangt. Die Recherche hat es an drei voneinander unabhängigen Tatsachen scheitern lassen, von denen jede einzelne genügt:
- Workers kann keine ausgehenden gRPC-Aufrufe machen. Der Laufzeitumgebung fehlt bidirektionales HTTP/2-Streaming, und ein offenes Ticket im Repository von Cloudflares eigener Laufzeitumgebung verfolgt das.
- Der Browser spricht ebenfalls kein gRPC. Der Web-Client setzt ein anderes Protokoll um, fällt auf HTTP/1.1 zurück — was einen Teil der Vorteile aufhebt — und unterstützt weder Client-Streaming noch bidirektionale Aufrufe.
- HTTP-Trailer, die gRPC für den Status braucht, werden vom Edge-Proxy von Cloudflare nur eingeschränkt unterstützt. Dokumentiert hat das Cloudflare selbst.
Was übrig blieb, ist kein Trostpreis. gRPC hier einzuführen hieße, die vorige Generation mit mehr Aufwand und weniger Ertrag einzuführen. Der Wunsch und die Belege, die ihn gekippt haben, stehen beide im öffentlichen Entscheidungsprotokoll — dafür führt man eines.
Null Anfragen an Dritte
Kein Schriften-CDN, kein Analyseskript, kein eingebetteter Rahmen, kein externes Stylesheet. Nicht eine Anfrage geht an einen Server, der nicht unserer ist. Geprüft wird das gegen das HTML, das die Produktion tatsächlich ausliefert, nicht gegen unsere Absichten.
Raleway hosten wir selbst, als variable Schrift in zwei Dateien: latin und latin-ext. Die zweite ist nicht optional: ohne sie fallen das ñ, das ç, das ã, das õ und das ü aus fünf unserer Locales auf eine andere Schrift zurück — genau bei den Zeichen, die diese Locales ausmachen. Und der Grund, der schwerer wiegt als die Geschwindigkeit: Wer eine Schrift von einem Dritten lädt, übergibt diesem Dritten Adresse und Browserkennung jedes Besuchers, auf den öffentlichen Seiten eines Kinderprodukts.
Die Inferenz läuft innerhalb des Netzes
Die 23 gegnerischen Prüfer laufen bereits auf Workers AI, und der Tutor kommt dorthin ebenfalls: Die Inferenz bleibt bei Cloudflare, statt zu einer externen Schnittstelle zu gehen. Der Aufruf geschieht innerhalb des Workers, ohne Weg nach draußen, und das Referenzgerät ist ein günstiges Android-Telefon in langsamem Mobilfunk, wo Latenz so viel kostet wie der Preis.
Was das aufgibt, steht geschrieben statt beschönigt. Der JSON-Modus von Workers AI ist ein Bemühen ohne Garantie, die Schemaprüfung liegt also bei uns. Ein Urteil, das die Prüfung nicht besteht, zählt als gescheiterter Prüfer, nie als sauberer: Der billige Fehlerfall ist ein erneuter Lauf, der teure ist ein Verstoß, den niemand gesehen hat.
Versuche kommen nie nach D1
D1 endet bei 10 GB je Datenbank. Eine Zeile pro Versuch liefe durch einen Entwurfsfehler gegen diese Wand, nicht durch Wachstum: Die erste harte Grenze dieser Architektur erreicht man, indem man sich irrt, nicht indem man Erfolg hat. Rohe Versuche gehen an Analytics Engine; D1 behält Konten, Inhaltsmetadaten und Aggregate. Ein deterministischer Prüfer blockiert den Commit, der das vergisst.
Wir messen selbst, und niemals ein Kind
Die Feldmessung der Leistung machen wir selbst und niemals ein Dritter: ein eingebettetes Skript von rund 600 Byte, das an einen Endpunkt von uns meldet, der in Analytics Engine schreibt — nie in D1 und nie zu einem Dienst von außen. Nichts davon identifiziert eine Person, und geladen wird es nie auf dem Bildschirm eines Kindes: Der Endpunkt weist diese Bänder ebenfalls ab, damit beide Enden geschlossen scheitern. Ein Prüfer bewacht den Code; die Prüfung in der Produktion bewacht die andere Hälfte, denn die Plattform kann ein eigenes Zählpixel auf Zonenebene einspielen, und das hinterlässt keine Datei zum Nachsehen.
Was die Seiten wiegen
Schwerste Seite: 9,5 KB mit gzip komprimiert, so nämlich reisen sie. Das gesamte Client-JavaScript der Website: 2,6 KB. Das gesamte CSS: 1,5 KB. Das Budget, das einen Commit blockiert, liegt bei 12 KB pro Seite und 60 KB für das gesamte Client-JavaScript zusammen.
Das Leistungsbudget, und was wir noch nicht behaupten
Das Budget lautet INP unter 150 ms, LCP unter 2,5 s, CLS unter 0,1. 150 und nicht die üblichen 200, denn dies ist ein Spiel mit hoher Interaktionsfrequenz: Ein Kind tippt viele Male pro Sitzung, und jedes Tippen wird gemessen. Genau in diesem Profil bricht INP ein.
Klar gesagt: Das sind Budgets, keine Ergebnisse. Keine Feldmessung läuft lange genug, als dass der Prüfer, der die Schwellen durchsetzt, etwas durchzusetzen hätte. Bis dahin behauptet diese Seite kein einziges Leistungsergebnis.
Die Prüferflotte
8 deterministische Prüfer laufen bei jedem Commit, in Millisekunden, und blockieren ihn. 23 gegnerische Prüfer auf Basis von Sprachmodellen laufen, bevor ein Pull Request geöffnet wird, mit dem Auftrag, den Verstoß zu finden statt zuzustimmen. Insgesamt sind 39 geplant; 8 warten noch auf die Phase, die sie freischaltet, und stehen als wartend da, statt still zu verschwinden.
Zwei Regeln machen sie nützlich statt lästig. Jeder Prüfer muss die Entscheidung oder das Forschungsdokument nennen, das er durchsetzt: Wer auf keine echte Kennung zeigen kann, äußert eine Meinung, und sein Urteil blockiert nicht. Und einen Prüfer zu übergehen verlangt, das Warum aufzuschreiben — in einer Datei, die zusammen mit der Änderung eingecheckt wird, die das Übergehen nötig machte.
Was es noch nicht gibt
Das Bestandsverzeichnis wird geschrieben, bevor die Objekte existieren, damit wer eines anlegt, seine Zeile im selben Pull Request einträgt. Eine angelegte und nicht dokumentierte Ressource ist eine Ressource, die in einem Jahr niemand mehr löschen kann. Dies ist entworfen und nicht angelegt:
- Durable Objects für Ligen, Klassen und das anpassende Modell je Kind — eines je Liga und eines je Klasse, niemals ein globales.
- Queues und Workflows für die asynchrone Bewertung und das Erzeugen von Erklärungen.
- Ein Vectorize-Index für die Suche über die kuratierte Hinweissammlung, beschränkt auf kuratierte Inhalte und niemals auf Vektoren je Kind.
- Die Aufgabensammlung selbst, die das Produkt ist und keine Datenmenge.
Wer das betreibt, und wer es bereitstellt
Ignia betreibt die Infrastruktur. Jedes Objekt, das diese Seite nennt, liegt in einem Cloudflare-Konto, das Ignia führt — Cloudflare-Konten zu betreiben, für eigene Produkte wie für Kunden, gehört zu dem, was Ignia tut. Math Challenge ist ein Projekt von Ignia, finanziert von Ignia.
Cloudflare stellt die Plattform bereit. Workers, D1, KV, R2, Analytics Engine und das Netz, auf dem sie laufen, sind Produkte von Cloudflare und werden von Cloudflare betrieben. Ignia betreibt; Cloudflare stellt bereit. Zwei Verben, zwei Firmen, mit Absicht getrennt gehalten — und der Grund dafür ist derselbe, auf dem diese ganze Seite steht: Wer eine Architekturseite liest, ist derjenige, der nachprüft.
dig +short NS math.kilowatto.com
alberto.ns.cloudflare.com.
colette.ns.cloudflare.com. Die andere Hälfte lässt sich nirgends nachschlagen, und das sagen wir. Dass diese Domain von Cloudflare ausgeliefert wird, kann jeder bestätigen. Dass Ignia das Konto betreibt, ist unsere eigene Aussage: Es gibt kein öffentliches Register, in dem Sie sie gegenprüfen könnten. Das so zu sagen ist mehr wert, als es auszuschmücken — eine einzige nicht nachprüfbare Aussage genügt, damit ein Leser aufhört, die übrigen nachzuprüfen.
Was diese Seite mit Absicht nicht behauptet: dass Ignia ein Cloudflare-Partner sei. Wir nennen keine Partnerstufe und zeigen kein Abzeichen, denn es gibt kein öffentliches Verzeichnis, in dem Sie das prüfen könnten. Auf einer Seite, deren ganzes Versprechen „und so prüft man es nach“ lautet, kostet eine nicht prüfbare Aussage mehr, als sie einbringt.
Ignia, in eigenen Worten: ignia.cloud
Wie man all das nachprüft
- die HTTP/3-Ankündigung in den Antwort-Headern
curl -sI https://math.kilowatto.com/en/ | grep alt-svc- 0-RTT: das TLS-1.3-Sitzungsticket und sein Kontingent für frühe Daten
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"- der RPC-Weg: erster Worker, zweiter Worker, D1
curl -s https://math.kilowatto.com/api/health- die oben genannten Gewichte, aus dem Repository
node audits/bundle-budget.mjs
Quellen
- 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 ist ein Projekt von Ignia, und es läuft auf Cloudflare. Das sind zwei Aussagen, und sie bleiben mit Absicht getrennt: zu sagen, Ignia stelle die Infrastruktur, wäre mit einer einzigen DNS-Abfrage widerlegt — und wer eine Architekturseite liest, ist genau die Person, die sie stellen würde. ignia.cloud