Wie ich OMLA gebaut habe, indem ich KI-Agenten dirigiert habe, statt selbst zu coden
- Kategorie
- KI & lokale LLMs
- Veröffentlicht
- 11 Juli 2026
- Von
- Jacob Lloyd — mit KI-Unterstützung geschrieben, im Nachhinein
- Lesezeit
- 9 Min. Lesezeit
Kurz gesagt: Die wahre Geschichte, wie eine einzelne Person ohne Programmierteam KI-Assistenten dirigiert hat, um eine echte Online-Lizenzierungsplattform für eine gemeinnützige Organisation zu bauen. Erklärt wird die Arbeitsmethode: klare schriftliche Pläne, mehrere Schichten gegenseitiger Kontrolle, und die Regel, dass nichts live geht ohne menschliche Freigabe. Ein praktischer Bauplan, um mit KI-Hilfe ernsthafte Software zu bauen.
Hier ist die genaue Schleife, mit der ich KI-Coding-Agenten dazu gebracht habe, OMLA zu bauen — das Lizenzierungs-Backend einer gemeinnützigen Organisation für Tantiemen, fast im Alleingang. Klau den Prozess. Das fertige Backend ist nur der Beweis, dass er funktioniert.
tl;dr
- Was es ist: der Workflow, mit dem Claude Code und ein lokaler KI-Agenten-Schwarm ein echtes Backend für eine gemeinnützige Organisation gebaut haben — ich als Architekt und Torwächter, nicht als Tippkraft.
- Was es kostet: im Grunde nichts Neues. Ein Coding-Agent-Abo, vielleicht eine übrige GPU für Zweitmeinungen. Die eigentlichen Kosten ist deine eigene Review-Zeit.
- Was du brauchst: einen Coding-Agenten, echte schriftliche Spezifikationen statt Bauchgefühl, und die harte Regel, dass nichts deployt wird, bevor ein Mensch das Wort tippt.
- Was am Ende dabei rauskommt: ein funktionierendes Backend mit Papierspur. Geschichtete Audits, Tests gegen einen echten lokalen Stack, und ein Deploy-Skript, das sich ohne menschliches "GO" weigert, live zu gehen.
Was am Ende dabei rauskommt
Vor jeder Anleitung erst mal, was am anderen Ende herauskam: OMLA, die Open Model Licensing Association. Ein Lizenzierungs-Backend für Tantiemen samt öffentlicher Website für offene KI-Modelle.
| Was | Das Ergebnis |
|---|---|
| Status | Im Aufbau als gemeinnützige Organisation im Bundesstaat Washington, 501(c)(3) beantragt. Die Website trägt absichtlich das Badge "Beta 0.9.0". |
| Das Modell | Kostenlos für persönliche, wissenschaftliche und Bildungsnutzung. Kommerzielle Nutzung schuldet 30 % des jeweils höheren Werts: zurechenbarer Umsatz oder die Kosten für den Modellbetrieb. |
| Datenbank | 12 Kerntabellen, Postgres 17 auf Supabase, überall Row-Level-Security, Geldrechnung in Integer-Cent (nie Fließkommazahlen) |
| Identität | Hybride ed25519- + ML-DSA-65-Signaturen, klassisch plus postquanten, damit ein künftiger Quantenbruch das System nicht aushebelt. Keine verifizierte Signatur, keine Tantiemenabrechnung. |
| Audit-Trail | Nur-Anhängen, SHA-256-hash-verkettet, stündlich geprüft, mit einem täglichen "Anker" der Kettenspitze, der komplett außerhalb der Datenbank gespeichert wird |
| Frontend | Etwa 30 einfache statische HTML-Seiten, kein Framework, in 13 Sprachen, auf gewöhnlichem Shared Hosting |
| Wer es gebaut hat | Ein Mensch, der den Kurs vorgibt, Claude Code, das den Code schreibt, ein lokaler Schwarm offener Modelle, der Review-Durchgänge und Fleißarbeit erledigt |
| Wo es steht | Vollständig staged und getestet. Deploy ist reines Dry-Run, bis ich persönlich "GO" tippe. |
Grobe Zeitleiste, falls du denkst, das sei an einem Wochenende passiert:
- Ende Mai 2026 — Kurswechsel von einem privaten Chatbot-Nebenprojekt zu einer Lizenzierungs-Non-Profit, plus das anfängliche 12-Tabellen-Schema
- Innerhalb weniger Tage — Postquanten-Signaturhärtung, die Edge-Functions für den Zahlungspfad und die "No-Custody"-Umschreibung (mehr dazu weiter unten)
- Anfang Juni — Backend in Produktion gepusht (die Datenbank, nicht das öffentliche Frontend)
- 16. Juni — juristische Überarbeitung abgeschlossen, Lizenz v1.0 tritt in Kraft
- 2. Juli — vollständige Auffrischung von Website und Backend plus Neuübersetzung in 13 Sprachen, staged und getestet, wartet zum Zeitpunkt dieses Artikels noch auf mein "GO"
Lustige Randnotiz zur Herkunft: OMLA stand ursprünglich für "Open Machine Learning Assistant", ein privates Chatbot-Projekt. Die Infrastrukturentscheidungen wurden beim Kurswechsel zur Lizenzierung eins zu eins übernommen; der Chatbot wurde zu einem umfangsbeschränkten Doku-Helfer degradiert, der in Produktion absichtlich abgeschaltet ist. Aus einem Hobby-Chatbot Lizenzierungsinfrastruktur zu machen, ist doch ein ganz normales Maß an Scope Creep, oder?
Die Regel, die den Rest erst möglich gemacht hat
Eine Designentscheidung hat den Großteil der Schwerarbeit übernommen, noch bevor irgendein Code geschrieben wurde: OMLA bewegt, hält oder überträgt niemals Geld. Es berechnet, was geschuldet wird, und veröffentlicht die vom Empfänger selbst hinterlegte Wallet oder Zahlungsdaten. Der kommerzielle Nutzer zahlt den Urheber direkt, Peer-to-Peer. Keine Zahlungsabwicklung, kein Treuhandkonto, kein "OMLA-Guthaben". Wallet-Kennungen sind Routing-Informationen, keine Konten.
Der Lizenztext sagt es unverblümt: "OMLA veröffentlicht; OMLA zahlt nicht."
Warum der Aufwand? Ein Geldtransmitter zu sein, ist sein eigener regulatorischer Albtraum, obendrauf auf den Lizenzierungs-Albtraum. Verzichtet man auf die Verwahrung, verlagert sich die KYC-/Sanktions-/Steuerlast auf die beiden Parteien, die tatsächlich die Transaktion durchführen — wo sie rechtlich ohnehin hingehört. Diese eine Entscheidung ist der Grund, warum ein Solo-Hobbyist dieses Projekt überhaupt versuchen konnte.
Es war nicht einmal das ursprüngliche Design. Mitten im Bau implizierte das Schema noch Verwahrung, also wurde eine Tabelle, die buchstäblich payments hieß, zu royalty_statements umbenannt — eine ganze Migration aus Spalten, Indizes, Triggern, RLS-Policies und Enums, nur damit die Datenbank aufhört, über ihre eigene Funktion zu lügen. Der alte Wortlaut ist mir später trotzdem noch auf die Füße gefallen (siehe die Stolperfallen).
Hier ist der Weg, den ein Dollar Tantieme nimmt, ohne dass OMLA ihn je in der Hand hält.
Schritt 4 ist das Nadelöhr: keine verifizierte Signatur zurück in Schritt 1, keine Tantiemenabrechnung, niemals. OMLA veröffentlicht die Zahl und die Wallet, und dann ist es Sache zweier anderer Parteien — genau wie wenn ich dir erzähle, dass ein Freund dir zwanzig Dollar schuldet, und euch beiden überlasse, das untereinander zu klären.
Die eigentliche Schleife: Mensch, Agenten, verifizieren, deployen
Der Workflow ist nicht kompliziert. Er ist nur strikt darin, wer was macht, und er wiederholt sich jedes Mal, wenn sich etwas ändert.
Es geht nicht um die Kästchen, sondern darum, dass keins übersprungen wird. Jede Änderung beginnt damit, dass ich aufschreibe, was existieren soll und warum — nicht "mach es besser". Agenten entwerfen gegen diese Spezifikation, andere Agenten prüfen den Entwurf, bevor ich ihn überhaupt zu Gesicht bekomme, und Tests laufen gegen Infrastruktur, die sich wie Produktion verhält. Erst dann lese ich den Diff.
Das gesamte Backend läuft zuerst lokal: ein echter Supabase-Stack, Postgres 17 plus Edge-Functions, auf meiner eigenen Maschine in rootless Containern. Produktion ist ein Deploy-Ziel, nie die Quelle der Wahrheit. Wenn lokal und live sich widersprechen, ist live im Unrecht, bis ich deploye.
Die Qualitätsmesslatte steckt in der Projektkonfiguration, nicht nur in meinem Kopf: "Audit-ready by default: ship work that can withstand external audit." Alles geht davon aus, dass irgendwann ein unabhängiger Prüfer, KI oder Mensch, versuchen wird, Löcher hineinzustoßen.
Gegen einen echten Stack testen, nicht gegen eine Attrappe
Nichts von alldem zählt, wenn die Tests lügen, also läuft hier nichts gegen eine Mock-Datenbank. Alles läuft gegen einen echten, wegwerfbaren lokalen Stack. Drei Schichten, alle grün, bevor irgendetwas Richtung Deploy geht:
deno test # 13 unit tests for the edge functions
./test/run.sh # isolated scratch DB per run, migrations w/ ON_ERROR_STOP,
# smoke tests (PQ signatures, wrong-key rejection, audit
# hash-chain, payout gate), RLS cross-tenant isolation,
# an adversarial suite, then rollback + reapply
./test/integration.sh # the full money path against the live local stack:
# register → tampered payload rejected → wallet verify →
# splits → usage report → statement published → replay
# (asserts zero duplicate writes) → admin gate → compliance
Diese Zeile "Replay prüft auf null doppelte Schreibvorgänge" ist wichtiger, als sie klingt. Nutzungsberichte kommen aus den Systemen anderer Leute, also wird irgendwann einer versehentlich erneut eingereicht; der Test prüft, dass das erneute Einspielen desselben Berichts null neue Abrechnungen erzeugt, nicht nur, dass nichts abgestürzt ist. Die Suite wurde später darauf gehärtet, Ergebnisse zu behaupten statt sie nur zu protokollieren — pedantisch klingend, bis man einen Test debuggt, der "bestanden" hat, während er nichts getan hat.
Audits, die andere Audits benoten
Eine KI, die ihre eigene Arbeit prüft, ist ein Abnickstempel. Deshalb sind Audits absichtlich geschichtet: lokale Schwarm-Agenten machen einen ersten Korrekturdurchgang, dann läuft ein stärkeres Modell als letztes, unabhängiges Audit-Gate.
Dieses letzte Gate ist keine reine Formalität. In einem Durchgang verifizierte es die Korrekturen der lokalen Agenten und fand trotzdem noch einen Search-Path-Bypass sowie Lücken bei Spalten, Signatur und Herkunft, die der erste Durchgang übersehen hatte. Daraus wurde eine eigene Härtungsmigration: zwölf nummerierte Befunde, jeder mit einem Regressionstest ausgeliefert, damit er nicht unbemerkt zurückkehren kann.
Der Umfang richtet sich nach der Änderung. Routinesachen bekommen ein 3-Agenten-Review-Panel. Die vollständige Frontend- und i18n-Überarbeitung bekam einen 24-Agenten-Schwarm-Durchgang, der auffing, dass die Website abgedriftet war (beschrieb immer noch das alte Chatbot-Produkt, nicht das Lizenzierungs-Backend, zu dem sie geworden war) und einen Neubau erzwang, der der Realität entspricht.
Das Deploy-Gate: Dry-Run, bis ich es tippe
Der Deploy-Runner hat einen einzigen Job: sicherstellen, dass nichts oberhalb dieser Linie versehentlich in Produktion landet. Standardmäßig immer Dry-Run; Produktion wird nur mit einem expliziten Flag berührt:
./deploy.sh # dry run (default) — shows exactly what WOULD happen
./deploy.sh --go # only a human runs this, only when ready
Ein geführter Wrapper geht noch weiter: Du tippst buchstäblich "GO", bevor irgendein produktionsverändernder Schritt läuft. Und die stehende Agenten-Regel, aufgeschrieben, damit sie keine bloße Empfehlung bleibt: nie --go ohne eine explizite menschliche Anweisung hinzufügen.
Weitere Garantien, die im Runner selbst stecken, nicht nur auf Papier als Richtlinie:
- Der Frontend-Sync nutzt niemals
--delete, kann also keine Dateien auf dem Host wegfegen, die er nicht verwaltet - Vor jedem Frontend-Überschreiben wird serverseitig ein Backup angelegt
- Das Backend weigert sich zu handeln, wenn das verknüpfte Projekt nicht mit dem erwarteten Produktionsprojekt übereinstimmt — eine harte Schutzmaßnahme gegen das Pushen in die falsche Datenbank
- Gegen remote laufen ausschließlich additive Migrationen, niemals ein Reset
- Die Dokumentations-Chatbot-Funktion ist bei jedem Deploy grundsätzlich ausgeschlossen
- Secrets leben nur in einer Datei mit Modus 600 außerhalb des Repos; eine Klartext-Prüfung bestätigt, dass der Service-Role-Key nie in dem landet, was ausgeliefert wird
Ehrlich gesagt ist das Deploy-Log dieses Projekts größtenteils ein Tagebuch meines eigenen Kalte-Füße-Bekommens: Dry-Run nach Dry-Run, während ein vollständig staged fertiges Redesign auf das Wort wartet. Das ist kein Bug. Genau so soll sich "gated" anfühlen.
Stolperfallen
Die Teile, die wehgetan haben:
- Das Zero-Width-Byte. Migrations-SQL muss reines ASCII sein. Ein einziges unsichtbares Non-ASCII-Byte kann ein
$$-Dollar-Quote-Token spalten und zwei SQL-Statements stillschweigend verschmelzen. Die Lösung wurde zum Ritual: nach Non-ASCII-Bytes grep-en (null erwünscht) und$$-Tokens zählen (gerade Anzahl erwünscht), jedes Mal. - Prozentzeichen beißen. In einem
RAISE EXCEPTION-Format-String ist%%ein wörtliches Prozentzeichen, und jeder Wert braucht genau ein%. Das hat mich einmal erwischt, was der einzige Grund ist, warum es jetzt aufgeschrieben ist. - SECURITY DEFINER braucht einen fixierten search_path. Jede Definer- und Trigger-Funktion pinnt ihren Suchpfad, damit niemand eine Tabelle in
pg_tempunterschieben und mit falschen Daten füttern kann. Der adversarielle Test zählt die gepinnten Funktionen und schlägt fehl, wenn die Zahl sinkt. - Der wacklige Superuser. Nach einem unsauberen Neustart kommt die lokale Postgres-Rolle manchmal ohne Superuser-Rechte zurück. Die Lösung ist ein sauberes Stoppen und Starten des Stacks, kein Fehlersuchen. Aufgeschrieben, damit die Agenten aufhören, das immer wieder neu zu entdecken.
- i18n überschreibt HTML still und leise. Die Übersetzungsschicht überschreibt den Seiteninhalt komplett, sodass "reparierte" HTML trotzdem noch veralteten Wortlaut ausliefern kann, wenn die Locale-Datei nicht mitrepariert wurde. Genau so ist alter Custody-Ära-Text immer wieder aufgetaucht, obwohl er angeblich entfernt war.
- Die Audit-Kette lässt sich absichtlich täuschen, damit wir es wissen. Ein Angreifer mit voller Datenbankkontrolle könnte den Hash-Ketten-Trigger löschen und eine in sich konsistente gefälschte Historie neu aufbauen; die interne Prüfung allein würde das nicht bemerken. Deshalb verankert ein täglicher Job den Spitzen-Hash der Kette komplett außerhalb der Datenbank, und die Testsuite beweist beide Hälften in einem Lauf: interne Prüfung getäuscht, externer Anker erwischt die Umschreibung trotzdem.
- Die Live-Seite ist nicht die staged Seite. Zum Zeitpunkt dieses Artikels zeigt die öffentliche Website noch teilweise Text von vor der Neuausrichtung (einen alten Lizenzentwurf, eine veraltete Zahlungswege-Karte), weil das Redesign staged, aber nicht deployed ist. Außerdem ist die öffentliche
/about-URL ein nackter Verzeichnisindex; die KI-Offenlegungsseite liegt eine Ebene tiefer.