Zurück zum Blog
Architektur
12 Min. Lesezeit

Eine Codebase. 13 Plattformen. Ohne Kompromisse.

Wie drei Dateien gleichzeitig zum Webformular, CLI-Befehl, MCP-Tool, nativen Screen und automatisierten Job werden.

definition.ts → web · cli · mcp · native · cron · 10 weitere

5.802 TypeScript-Dateien. ~2,1 Millionen Zeilen. Null `any`. Null Runtime-Typfehler. Ein Pattern. 374-mal wiederholt.

Das ist die Codebase hinter unbottled.ai - und das Framework dahinter, next-vibe. Dieselbe Architektur betreibt eine Web-App, eine Mobile-App, eine CLI, ein KI-Agent-Interface, einen MCP-Server, ein Cron-System, einen WebSocket-Event-Bus und eine Live-Datenfluss-Engine.

Das Pattern heißt Unified Surface. Hier erfährst du, was es ist, wie es funktioniert und warum du danach schwer davon loskommst.

Ein Feature ist ein Ordner

Jedes Feature in next-vibe lebt in einem Ordner. Drei Dateien sind Pflicht. Alles andere ist optional.

bash
1explain-to-my-boss/
2  definition.ts    ← was es tut
3  repository.ts    ← wie es das tut
4  route.ts         ← lässt es überall existieren
5  widget.tsx       ← eigene React-UI (optional)
6  widget.cli.tsx   ← eigene Terminal-UI (optional)

Das ist alles. Ein Ordner. Drei Pflichtdateien. Und aus diesen drei Dateien existiert das Feature auf bis zu 13 Plattformen gleichzeitig.

13 Plattformen aus 3 Dateien

Wenn du ein Feature zu next-vibe hinzufügst, wird es nicht nur ein API-Endpoint. Es wird gleichzeitig alles.

Web API

REST-Endpoint, automatisch validiert, vollständig typisiert

React UI

Automatisch aus der Definition generiert - kein JSX geschrieben

CLI

Jeder Endpoint ist ein Befehl mit automatisch generierten Flags

AI Tool Schema

Function-Calling-Schema wird automatisch generiert

MCP Server

Verbinde dich mit Claude Desktop, Cursor oder einem anderen MCP-Client

React Native

iOS- und Android-Screens aus derselben Definition

Cron Job

Jeden Endpoint nach Zeitplan ausführen

WebSocket Events

Updates an verbundene Clients pushen, sobald etwas fertig ist

Electron Desktop

Native Desktop-App über dieselben Endpoint-Verträge

Admin-Panel

Automatisch generiertes Admin-UI, kein zusätzlicher Code nötig

VibeFrame Widget

Einbettbares iframe-Widget für jede Website

Agent Skill

Von KI-Agenten als strukturierter Skill aufrufbar

Vibe Sense Node

Node in einem Live-Datenflussgraphen - selber Endpoint

Lösch den Ordner. Das Feature hört überall gleichzeitig auf zu existieren.

Plattformzugriff ist ein Enum-Array

Du schreibst keine separaten Berechtigungsschichten für jede Plattform. Der Plattformzugriff wird direkt in der Definition deklariert - ein Enum-Array, das jede Plattform zur Laufzeit nativ ausliest.

typescript
1// Dieses eine Array steuert, wo das Feature auftaucht
2allowedRoles: [
3  CLI_OFF,         // blockt die CLI
4  MCP_VISIBLE,     // Opt-in für die MCP-Tool-Liste
5  REMOTE_SKILL,    // trägt es in die Agent-Skill-Datei ein
6  PRODUCTION_OFF,  // deaktiviert es in Produktion
7]

Selbe Definition. Selber Ort. Keine Konfig-Dateien zum Synchronisieren. Keine separaten Berechtigungssysteme pro Plattform.

Es gibt keine API für Menschen und API für KI. Es gibt nur das Tool.

Die Live-Demo: Thea baut einen Endpoint

Statt das Pattern abstrakt zu erklären, zeig ich dir, wie es in der Praxis aussieht.

Wer ist Thea?

Thea ist die KI-Administratorin dieser Plattform. Sie läuft 24/7 auf Produktion und arbeitet mit denselben Endpoint-Verträgen wie jeder Benutzer - selbe Validierung, selbe Berechtigungen, keine Hintertür. Und sie kann Arbeit an eine lokale Maschine delegieren.

Ich hab Thea gebeten, einen neuen Endpoint zu bauen - explain-to-my-boss - mit Claude Code auf meinem PC. Du gibst eine technische Entscheidung ein, und raus kommt eine nicht-technische Begründung, die dein Chef tatsächlich glaubt. Das hat jeder Entwickler schon mal gebraucht.

Die Live-Demo: Thea baut einen Endpoint

1
Du

Frag Thea

Tippe die Aufgabe in den Chat - zwei Eingabefelder, eine KI-generierte Antwort, alle Plattformen, MCP_VISIBLE, eigene React- und CLI-Widgets.

2
Thea

Erstellt die Aufgabe

Thea denkt laut nach, erstellt eine Aufgabe mit targetInstance="hermes" (deine lokale Maschine) und geht in den Ruhezustand.

3
Lokaler Hermes

Holt die Aufgabe ab

Die lokale Instanz synchronisiert alle 60 Sekunden. Keine offenen Ports. Deine Maschine initiiert die Verbindung.

4
Claude Code

Baut den Endpoint

Interaktive Session. Liest zuerst bestehende Patterns, erstellt fünf Dateien, führt vibe check aus. Null Fehler.

5
Claude Code

Meldet Fertigstellung

Ruft complete-task mit der Aufgaben-ID auf. Status: abgeschlossen. Zusammenfassung anbei.

6
Thea

Wacht auf

wakeUp feuert. Thea setzt das Gespräch per WebSocket fort, streamt ihre Antwort, TTS spricht.

7
Ergebnis

Existiert überall

Webformular. CLI-Befehl. MCP-Tool. React-Native-Screen. Alles live. Aus fünf Dateien.

Der Beweis

Sobald Claude Code complete-task aufgerufen hat, gab es drei Dinge, die fünf Minuten vorher noch nicht existierten:

Ein eigenes React-Widget - dramatische Überschrift, animierter Gradient auf dem KI-Output, ein gefälschter Corporate-Alignment-Score.

Ein CLI-Widget - ASCII-Banner, Spinner während die KI nachdenkt, die Begründung Zeile für Zeile in Grün.

Ein MCP-Tool - explain-to-my-boss_POST - weil MCP_VISIBLE in der Definition stand. Claude Desktop kann jetzt deine Entscheidungen deinem Chef erklären.

Eine Definition. Fünf Dateien insgesamt. Drei komplett verschiedene UIs. Der Endpoint-Vertrag hat sich nicht geändert. Nur die Präsentationsschicht.

Unter der Haube

definition.ts - der lebende Vertrag

Die Definition ist kein Code-Generator. Sie ist ein lebender Vertrag, den jede Plattform zur Laufzeit nativ ausliest. Ändere sie - alles aktualisiert sich. Lösch den Ordner - nichts bricht downstream. Es gibt keinen generierten Code zum Aufräumen.

typescript
1// definition.ts
2const { POST } = createEndpoint({
3  scopedTranslation,
4  aliases: [EXPLAIN_TO_MY_BOSS_ALIAS],
5  method: Methods.POST,
6  path: ["explain", "to-my-boss"],
7  title: "post.title",
8  description: "post.description",
9  icon: "sparkles",
10  category: "endpointCategories.ai",
11  allowedRoles: [UserRole.CUSTOMER, UserRole.ADMIN] as const,
12  fields: objectField(scopedTranslation, {
13    type: WidgetType.CONTAINER,
14    usage: { request: "data", response: true },
15    children: {
16      decision: requestField(scopedTranslation, {
17        type: WidgetType.FORM_FIELD,
18        fieldType: FieldDataType.TEXTAREA,
19        label: "post.fields.decision.label",
20        description: "post.fields.decision.description",
21        schema: z.string().min(1).max(2000),
22        columns: 12,
23      }),
24      justification: responseField(scopedTranslation, {
25        type: WidgetType.TEXT,
26        content: "post.fields.justification.content",
27        schema: z.string(),
28      }),
29    },
30  }),
31  errorTypes: { /* ... all 9 required error types ... */ },
32  successTypes: { title: "post.success.title", description: "post.success.description" },
33  examples: { requests: { default: { decision: "Migrate to Bun" } }, responses: { default: { justification: "..." } } },
34});

repository.ts - niemals throw

Repository-Funktionen werfen nie. Fehler propagieren als Daten - typisiert, explizit und vom Aufrufer abfangbar. Die KI kann über Fehlerpfade nachdenken. Keine überraschenden Exceptions.

typescript
1// repository.ts
2// Gibt ResponseType<T> zurück - wirft niemals
3export async function explainToMyBoss(
4  data: { decision: string },
5  logger: Logger,
6): Promise<ResponseType<{ justification: string }>> {
7  const result = await ai.generateText({
8    prompt: buildPrompt(data.decision),
9  });
10  if (!result.text) {
11    return fail({ message: "AI returned empty response", errorType: EndpointErrorTypes.SERVER_ERROR });
12  }
13  return success({ justification: result.text });
14}

route.ts - die komplette Brücke

route.ts verbindet Definition mit Handler. endpointsHandler kümmert sich um Validierung, Authentifizierung, Logging und Exposition für alle 13 Plattformen. Die eigentliche Geschäftslogik ist eine Zeile.

typescript
1// route.ts
2import { endpointsHandler } from "@/app/api/[locale]/system/unified-interface/shared/endpoints/route/multi";
3import { Methods } from "@/app/api/[locale]/system/unified-interface/shared/types/enums";
4import definitions from "./definition";
5import { explainToMyBoss } from "./repository";
6
7export const { POST, tools } = endpointsHandler({
8  endpoint: definitions,
9  [Methods.POST]: { handler: ({ data, logger }) => explainToMyBoss(data, logger) },
10});

Die Zahlen

374 Endpoints

Ein Pattern, 374-mal angewandt

Null `any`

Zur Build-Zeit erzwungen, keine Konvention

Drei Sprachen

Zur Compile-Zeit geprüft - t("typo.here") ist ein Compiler-Fehler

Das ist keine Konvention. Das wird zur Build-Zeit erzwungen.

Ein Pattern. 374-mal wiederholt.
Als Nächstes
Vibe Sense: Die Pipeline ist die Plattform

Jede Node in einem Vibe-Sense-Graph ist ein regulärer next-vibe-Endpoint. Dasselbe createEndpoint(). Dieselbe 3-Dateien-Struktur. Ein EMA-Indikator ist ein Endpoint. Ein Schwellenwert-Evaluator ist ein Endpoint. Und weil es ein Endpoint ist, kannst du ihn über CLI, KI oder von überall aufrufen.

vibe analytics/indicators/ema --source=leads_created --period=7

Die Pipeline ist die Plattform.

Vibe Sense sind einfach… mehr Endpoints. Dasselbe Prinzip, angewandt auf Zeitreihendaten. Lead-Funnels. Credit-Ökonomie. Nutzerwachstum. Deine Plattform überwacht sich selbst.

Zurück zum Blog

Einmal definieren. Überall vorhanden.

WordPress gab jedem die Macht zu publizieren. next-vibe gibt dir die Macht, Plattformen zu bauen, die auf Web, Mobile, CLI, KI-Agenten und Automatisierung funktionieren - die sich selbst überwachen, über ihre eigenen Daten nachdenken und danach handeln.

next-vibe auf GitHub mit Stern versehen