Connections — API-Key-Lebenszyklus

Wie eine Personal-Brain-Instanz sich mit einer Business-Brain-Instanz verbindet und wieder trennt.

Für die architektonische Einordnung siehe ARCHITECTURE.md, für gelockte Entscheidungen ../DECISIONS.md (D24, D25).


1. Ablauf

Admin@Business                               User@Personal
(z.B. gustavo                                (z.B. gustavo
 im Business-Admin-UI)                        auf seinem Mac)
     │                                                │
     │ 1. Invite generieren                           │
     │    POST /admin/invites                         │
     │    → { code: "KBL-7A3X", api_key: "sk_…" }     │
     │    (Code ist 24h gültig, one-shot)             │
     │                                                │
     │ 2. Code an den Eingeladenen senden             │
     │    (E-Mail, Signal, o.Ä. — out of band)        │
     │ ────────────────────────────────────────────► │
     │                                                │
     │                                                │ 3. UI: "Business verbinden"
     │                                                │    → URL + Code eingeben
     │                                                │    POST /api/connect
     │                                                │      { business_url, code }
     │ ◄───────────────────────────────────────────── │
     │                                                │
     │ 4. Business validiert Code                     │
     │    - existiert?                                │
     │    - nicht abgelaufen?                         │
     │    - noch nicht eingelöst?                     │
     │    Bindet Code an callers Identity             │
     │    Deaktiviert Code (one-shot)                 │
     │                                                │
     │ 5. Response: {                                 │
     │     business_id: "kuble",                      │
     │     business_name: "Kuble GmbH",               │
     │     api_key: "sk_…",                           │
     │     scopes: [...]                              │
     │    }                                           │
     │ ────────────────────────────────────────────► │
     │                                                │
     │                                                │ 6. Personal persistiert
     │                                                │    in SQLite (api_keys Tabelle)
     │                                                │    UI: "Verbunden mit Kuble ✓"
     │                                                │
     │                                                │ 7. UI: Brain-Liste laden
     │                                                │    → GET /api/brain/list
     │                                                │       Header: X-GTS-Key
     │ ◄───────────────────────────────────────────── │
     │                                                │
     │ 8. UI: User haken ab, welche                   │
     │    Projekte/Kunden syncen sollen               │
     │                                                │
     │ ... ongoing sync (via /api/sync) ...           │
     │                                                │
     │ n. Admin: "Trennen"                            │
     │    DELETE /admin/connections/<id>              │
     │    → API-Key revoked                           │
     │                                                │
     │ n+1. Nächster Personal-Sync                    │
     │      Response: 401 Unauthorized                │
     │                                                │ Personal UI:
     │                                                │ "Verbindung getrennt durch Admin"
     │                                                │ zeigt Kontakt-Hinweis

2. Zustände einer Connection

Aus Business-Sicht (Spalte status in connections-Tabelle):

Status Bedeutung
invited Code ausgestellt, noch nicht eingelöst. Expires in 24h.
active Eingelöst, funktioniert.
revoked Admin hat widerrufen. Key funktioniert nicht mehr. Historisch sichtbar im Audit-Log.
expired Code lief ab, ohne eingelöst zu werden.

Aus Personal-Sicht analog: connecting, connected, disconnected, failed.


3. Scope (Phase 4+)

API-Keys haben einen Scope-Set. Im MVP der einzige Scope ist sync (Lesen + Schreiben von Brain-Sync-Daten). Erweiterbar:

  • sync.read — Nur-Lese-Personal
  • sync.write — Bidirektional (Default)
  • admin — Darf auf die Business-Seite zurückschreiben auch ohne Sync (z.B. ein Delegated-Admin-Setup)

Scopes werden in Phase 4 im Detail ausgearbeitet — MVP reicht sync.write.


4. Sicherheits-Constraints

  • Code einmalig einlösbar — nach erstem Redeem deaktiviert, unabhängig vom Ergebnis.
  • API-Key nie im Klartext loggen — nur das letzte Präfix (sk_abc…).
  • Rate-Limit auf /api/connect — 10 Versuche/IP/10min (verhindert Brute-Force auf Codes).
  • Key-Rotation: Admin kann einen Key ohne Disconnect rotieren (neuer Key ersetzt alten). Personal bekommt Push-Notification oder schlägt beim nächsten Sync fehl und muss den neuen Key eintragen.
  • Offboarding: Bei "Mitarbeiter verlässt Firma" sollte der Admin die Connection revoken. Die historischen Brain-Daten auf der Personal-Instanz bleiben beim Ex-Mitarbeiter (kann nicht verhindert werden — git ist dezentral) — aber Business-Soul-Updates stoppen, und der Ex-Mitarbeiter kann nichts mehr ins Business schreiben.

5. CLI-Äquivalente

Was die Web-UI kann, kann auch die CLI:

# Business (Admin):
gts admin invite --email user@example.com    # → Code + URL
gts admin connections list
gts admin connections revoke <id>
gts admin connections rotate-key <id>        # neuen Key ausstellen, alten widerrufen

# Personal (User):
gts connect <business-url> <code>
gts connect list
gts connect revoke <business-slug>
gts sync [<business-slug>]                   # pull/push, logged in git + SQLite