Zurück zum Blog
·von Patrick Hofmann

Den zweiten Service-Provider habe ich nicht entworfen

Ich wollte einen Timetracker für meine eigenen Stundenlisten. Herausgekommen ist ein Beweis, dass ein neuer protokoll-konformer Service-Provider keine Erfindung mehr ist, sondern eine Kopie — mit allem, was Kopieren mit sich bringt.

OpenApeInfrastructureArchitectureBuilding in Public

Meine Activity-Logs füttern die Timesheets pro Projekt und Firma. Bisher war das eine jq-Pipeline über JSONL — funktioniert, aber ich kann es nicht am Handy aufmachen und fragen, wie viele Stunden im Mai auf ein bestimmtes Projekt liefen. Also timetrack.openape.ai.

Der ehrliche Teil: Ich habe mich nicht hingesetzt und einen Service entworfen. Ich habe openape-tasks kopiert und umbenannt. Der erste Commit im neuen Repo heißt wörtlich "mirror openape-tasks, rename to timetrack". Das ist kein Tippfehler in der Commit-Message, das ist die Methode.

Wie der erste Service-Provider war

Erfunden. Jeder Baustein war eine Entscheidung, die einmal getroffen wurde, schmerzhaft, mit Smoke-Tests, die beim ersten echten Dogfooding fünf verkettete Bugs auf einmal fanden.

DDISA-Discovery: ein SP registriert sich nicht im IdP, er publiziert seine Metadata über DNS. Token-Exchange: RFC 8693, mit dem delegation-grant-only Pfad für den Fall, dass der Caller nicht beide Tokens hat. Die zweistufige RBAC. Das ape-*-CLI-Muster mit --json für agent-scriptbaren Output. Jedes davon war Arbeit, bei der ich nicht wusste, wie es ausgeht, bis es ausging.

Das ist der teure Teil. Den macht man einmal.

Wie timetrack war

Die eigentliche Arbeit war die Domäne: Firmen und Projekte, wer sieht welche Einträge, wie ein Report nach Projekt gruppiert. Die zweistufige RBAC musste auf das neue Datenmodell gelegt werden — eine Sichtbarkeitsfunktion plus die Tests, die die Matrix aus der Spec abklopfen.

Was kein Schritt war: die Identity-Schicht. Es gibt keinen Milestone "Auth designen". Nicht weil ich es vergessen hätte, sondern weil diese Schicht in jedem SP dieselbe ist. DDISA-Discovery, Token-Exchange, das Auth-Plus-Exchange-Route-Paar — kopiert, aud/iss auf timetrack.openape.ai gezogen, fertig. Der erste echte Agent-E2E gegen Prod war grün.

Die Reibung, die tatsächlich Zeit gekostet hat, lag nicht im SP. ape-timetrack companies use gegen localhost hat den activeEndpoint persistiert, ein lokaler State, der einen späteren Lauf gegen Prod als "HTTP 0" sterben ließ. Zwei Fehldiagnosen — erst cli-auth, dann Node 25 — bevor der echte Grund auf dem Tisch lag: Test-Pollution des CLI-State, nichts am Protokoll. Das ist die Pointe an der Reibung: sie war im lokalen State, nicht in der Architektur. Die Architektur hat sich mechanisch verhalten.

Was weggefallen ist

Das Erfinden. Es gibt keinen Punkt mehr, an dem ich überlege, wie ein SP sich gegenüber dem IdP authentifiziert. Diese Frage ist beantwortet, und die Antwort ist überall dieselbe.

Genau das macht einen neuen SP kopierbar statt erfindbar. Und genau da kippt es.

Kopierbar heißt auch: Annahmen werden mitkopiert

In derselben Woche landeten drei Commits mit identischen Messages in fünf Repos:

fix: resolve IdP issuer from subject DDISA, drop hardcoded issuer
feat: declare scope catalog in /.well-known/openape.json
feat: enforce delegated scopes (chokepoint + subset@exchange + short TTL)

openape-tasks, openape-plans, openape-preview, openape-timetrack, der sp-starter — fünf Mal dasselbe, weil der Fehler fünf Mal derselbe war. Der Issuer war hardcoded auf https://id.openape.ai. Konzeptuell:

// vorher: in jedem SP gleich, weil aus dem ersten SP kopiert
const ISSUER = "https://id.openape.ai"

// nachher: Issuer aus dem _ddisa-DNS-Record der Subject-Domain,
// nicht mehr fest verdrahtet

Der erste SP hatte den hardcoded Issuer. Jede Kopie hat ihn mitgenommen. Niemand ist gestolpert, weil hofmann.eco per _ddisa-DNS ohnehin auf id.openape.ai zeigt — die Verletzung war behavior-preserving, also unsichtbar. Aufgefallen ist sie erst, als ich das SP-Data-Access-Profil geschrieben und über alle fünf SPs hinweg auditiert habe.

Ein Audit der Doku hätte das nicht gefunden — der Code war ja überall "korrekt", weil überall gleich falsch. Gefunden hat es erst echtes Cross-SP-E2E.

Der Schnitt

Das Erfinden ist einmalig und teuer. Das Kopieren ist billig und uniform — im Guten wie im Schlechten. Die uniforme richtige Entscheidung trägt jeden neuen SP fast mechanisch. Die uniforme falsche Annahme reproduziert sich genauso mechanisch, fünf Mal, und wird mit fünf Mal demselben Diff gefixt.

Dass die Architektur kopierbar ist, ist der Gewinn. Der Beweis, dass sie wirklich kopierbar ist, ist, dass der Bug es auch war.

Den sp-starter habe ich danach öffentlich gemacht. Nicht weil ich geplant hatte, ein Template zu bauen. Ich wollte einen Timetracker für meine Stundenlisten — und habe dabei gemerkt, dass das Ding, von dem ich kopiere, jetzt ein Ding ist statt meiner Erinnerung an den letzten SP. Ein Template ist nur ehrlicher darüber, was sowieso passiert: der nächste SP wird nicht erfunden.


Code: github.com/openape-ai/openape, MIT-lizenziert. Das SP-Data-Access-Profil liegt in protocol, der Kopier-Ausgangspunkt in github.com/openape-ai/sp-starter.