Arc OS — Architektur

Überblick

Arc OS ersetzt 11.321 Zeilen individuelle Python/JS-Infrastruktur durch native Claude Code-Werkzeuge. Der KI-Agent IST das Backend.

Systemarchitektur (Phase 52.1)

graph TB
    User[👤 User Browser :18888]
    TG[📱 Telegram]
    Local[💻 Local IDE]

    subgraph "Edge"
      Nginx[Nginx :18888<br/>basic-auth + deny-list]
      FE[React CRM + Phaser<br/>frontend container]
    end

    subgraph "Master Bot Bun :19210 — 127.0.0.1 only"
      ApiSrv[api-server.ts<br/>196 LOC]
      Routes[master-bot/routes/<br/>auth · internal · cli · websocket]
      Router[shared/routes/router.ts<br/>373 LOC dispatcher]

      subgraph "19 Domain Modules — Phase 48"
        D1[auth · projects · workers]
        D2[skills · sage · chat]
        D3[wiki · files · onboarding]
        D4[billing · invites · analytics]
        D5[+ 7 more]
      end
    end

    subgraph "Federated Bots"
      Master[Master Bot<br/>orchestrator]
      Child[Child Bots<br/>per-project]
      Claude[claude -p CLI]
    end

    subgraph "Intelligence Layer"
      Eval[Binary Evals]
      Ctx[Context Router top-5]
      Learn[Learnings inject]
      Karp[Karpathy nightly loop]
    end

    NB[NotebookLM Bridge<br/>FastAPI :19213 localhost]
    Google[(Google NotebookLM)]

    DB[(SQLite WAL<br/>30+ tables<br/>migrations 001-035)]
    Vault[(AES-256-GCM Vault<br/>config/vault.json)]
    Bridge[Local Bridge CLI<br/>WebSocket relay]

    User -->|HTTPS| Nginx
    Nginx --> FE
    Nginx -->|/api/crm/*| ApiSrv
    Nginx -->|/api/sse/*| ApiSrv
    Nginx -->|/ws/terminal| ApiSrv
    ApiSrv --> Routes
    Routes --> Router
    Router --> D1 & D2 & D3 & D4 & D5

    TG --> Master
    TG --> Child
    Master -.spawns via tmux.-> Child
    Child --> Claude
    Claude --> Eval & Ctx & Learn

    Router -.queries.-> NB
    Child -.auto-sync.-> NB
    NB --> Google

    Router --> DB
    Router --> Vault
    Child --> Vault

    Local --> Bridge
    Bridge -.ws relay.-> ApiSrv
    Karp -.nightly.-> DB

Wichtige Fakten:

LEGACY (v1):
User → Telegram Bot (Python 111KB) → HybridEngine → command_queue.json
       → bridge_processor.sh → Claude → command_responses.json
       → FastAPI EventBus → WebSocket → Phaser UI

V2 (NATIVE-FIRST):
User → Official Telegram Channel → Claude Code Session
       ↓                                ↓
       reply/edit_message          Agent Teams (TaskCreate/SendMessage)
                                        ↓
                                   Write state → JSON files
                                        ↓
                                   Arc OS Bridge MCP → HTTP/SSE → Phaser UI

Reduzierung: 11.321 Zeilen → ~3.700 Zeilen (67 % weniger Code)

Komponenten

1. Claude Code Session (Das Gehirn)

Die Claude Code Session ersetzt:

Die gesamte Orchestrierung läuft nativ über Agent Teams.

2. Arc OS Bridge MCP Server (State Adapter)

TypeScript MCP-Server (Bun), der den Claude Code-Zustand mit dem Frontend verbindet.

MCP-Tools (Claude Code-Seite):

Tool Richtung Beschreibung
get_office_state read Liest state/office-state.json
update_agent_state write Aktualisiert Agent-Position, Status, Bubble
get_tasks read Liest Agent Teams Task-Dateien
get_knowledge read Liest Knowledge-Index

HTTP-API (Frontend-Seite):

Endpunkt Methode Beschreibung
/api/state GET Vollständiger Office-State
/api/tasks GET Task-Liste
/api/knowledge GET Knowledge-Index
/api/events GET SSE-Stream

3. Phaser Frontend (Visuelle Ebene)

Phaser 3.80 + Vite. Verbindet sich mit dem MCP-Server via HTTP/SSE.

Wesentlicher Unterschied zu v1: kein WebSocket — ersetzt durch SSE vom MCP-Server. Keine Befehlseingabe-UI — Telegram ist die Befehlsschnittstelle.

4. Telegram Command Interface (Taktische UI-Ebene — Phase 21.0)

Telegram ist das primäre Steuerungspanel für das gesamte Arc OS. Zwei Bot-Ebenen:

Master Bot (@citadel_ceo_bot):

Child Bots (@cv2_pt_bot etc.):

CRM-Integration (Entwicklung):

CRM_BASE_URL = http://62.171.128.248:18888  (dev)
               https://crm.citadel.v2       (future prod)

Alle Tastatur-Layouts sind in shared/ui_templates.ts definiert — Single Source für UI-Änderungen.

Callback-Daten-Protokoll: action:target (max. 64 Bytes)

Datenfluss

1. User sendet "/status" via Telegram
2. Claude Code Session empfängt über Telegram Channel
3. Rick liest state/office-state.json + TaskList()
4. Rick antwortet via Telegram (edit_message für Live-Updates)
5. Rick ruft update_agent_state() via MCP auf
6. MCP-Server schreibt in state/office-state.json
7. File Watcher erkennt Änderung → sendet SSE-Event
8. Frontend empfängt SSE → aktualisiert Agent-Sprites

State-Verwaltung

Aller State lebt in JSON-Dateien unter state/:

Datei Schema Aktualisiert von Gelesen von
office-state.json Agent-Positionen, Status, Bubbles Claude (via MCP) Frontend (via HTTP)
knowledge.json Indexierte Datei-Metadaten Squanchy-Agent Frontend, Beth
reports/*.json Analyseausgabe Beth, Summer Frontend

Agent Teams Tasks werden von Claude Code nativ unter ~/.claude/tasks/citadel-v2/ verwaltet.

Agents (6)

Agent Rolle Modell Aktivierung
Rick CEO/Orchestrator opus-4.6 Immer aktiv (Teamleiter)
Morty SRE/Monitoring haiku-4.5 Health Checks, Cron
Summer Memory/Review sonnet-4.5 Code-Review, Security
Jerry Maintenance haiku-4.5 Aufräumen, Docs
Squanchy Archivist haiku-4.5 Datei-Indexierung
Beth Analyst sonnet-4.5 Recherche, Analyse

Supply Chain

CEO → Rick (aufteilen) → Agent Team (ausführen)
                        → Cross-Review (Summer validiert)
                        → Rick (zusammenführen)
                        → CEO (Endergebnis)

Durchgesetzt durch:

Technologie-Stack

Ebene v1 v2
Backend FastAPI + Python 3.12 Claude Code Session
Datenbank SQLite (8 Tabellen) JSON-Dateien + Agent Teams
Messaging WebSocket + EventBus Agent Teams SendMessage
State SQLite + JSON Bridge JSON-Dateien (state/)
Frontend Phaser 3.86 + WebSocket Phaser 3.80 + SSE
Telegram Custom Bot (111 KB) Official Channel Plugin
Bridge bash + jq (19,6 KB) Entfernt
MCP Keiner Arc OS Bridge (TypeScript)
Deploy Docker Compose Docker Compose

Lifecycle-Hooks

Claude Code-Hooks (~/.claude/settings.json) synchronisieren Agent-State automatisch:

SubagentStop event → subagent-stop.sh → office-state.json (agent=idle)
                                              ↓
                                   StateManager (fs.watch) → SSE → Phaser UI

Stop event → session-end.sh → latest_wrapup.txt + all agents=idle

Skripte: scripts/citadel-hooks/subagent-stop.sh, scripts/citadel-hooks/session-end.sh

Wichtige Felder: subagentName (SubagentStop), stopReason (Stop), cwd (beide).

NotebookLM Bridge (Phase 36.3)

Echte semantische Suche via Google NotebookLM — ersetzt manuelle Datei-Uploads:

Cloud PM Chat → Bun Master Bot (:19210)
                    │
         executeAskNotebooklm()
                    │
         HTTP → FastAPI Bridge (:19213, localhost only)
                    │
              notebooklm-py async client
                    │
              Google NotebookLM (kostenlose semantische Suche)

         Fallback: lokale Keyword-Suche (bestehender Code)

Dateibasierter Export als Fallback (/citadel-wrapup, /citadel-recall) bleibt weiterhin verfügbar.

Daten- und Task-Routing-Richtlinie

Drei-Ebenen-System. Jede Ebene hat einen strikten Zweck. Ebenen zu mischen = Chaos.

┌─────────────────────────────────────────────────────────────────┐
│  TIER 1: Working Memory (Hot)                                   │
│  office-state.json + state/tasks/                               │
│  CLI: /citadel-task, /citadel-status                            │
│                                                                 │
│  Tägliche Agent-Tasks. Lebt in der Sitzung. Stirbt danach.     │
│  Abgeschlossener Task → /citadel-wrapup → Tier 3.              │
├─────────────────────────────────────────────────────────────────┤
│  TIER 2: Strategic Backlog (Warm)                               │
│  GitHub Issues (Claude-CEO + citadel-v2 repos)                  │
│                                                                 │
│  Nur Epics: Phase 20, Phase 21, globale Bugs.                   │
│  Keine Tages-Tasks. Kein "Tippfehler korrigieren".              │
│  Ein Issue = eine mehrwöchige Initiative oder ein kritischer Defekt. │
├─────────────────────────────────────────────────────────────────┤
│  TIER 3: Long-term Memory (Cold)                                │
│  docs/library-export/ → Google Drive → NotebookLM              │
│  CLI: /citadel-wrapup, /citadel-recall                          │
│                                                                 │
│  Enzyklopädie. Sitzungsergebnisse, Architekturentscheidungen, RAG. │
│  Nur-lese-Archiv. Keine aktiven Tasks. Kein Status-Tracking.   │
└─────────────────────────────────────────────────────────────────┘

Routing-Regeln

Datentyp Tier Beispiel
"Deploy-Skript reparieren" 1 — Working Memory /citadel-task "Fix deploy script"
"Phase 20: Multi-Tenant SaaS" 2 — GitHub Issues gh issue create --title "Phase 20: ..."
"Sitzungszusammenfassung: Hooks implementiert" 3 — Library /citadel-wrapup → NotebookLM
"Architekturentscheidung: SSE statt WS gewählt" 3 — Library Nach der Sitzung im Wrapup archiviert
"Bug: SSE bricht auf VPS ab" 2 — GitHub Issues Nur wenn sitzungsübergreifend / blockierend
"Rick arbeitet an Hooks" 1 — Working Memory office-state.json Agent-Status

Lebenszyklus

CEO gibt Task → Tier 1 (Agent arbeitet daran)
                     ↓ abgeschlossen
                 /citadel-wrapup → Tier 3 (archiviert)
                     ↓ wenn strategisch
                 gh issue create → Tier 2 (langfristig verfolgt)

Anti-Patterns (NICHT TUN)

Infrastruktur (Phase 20.5)

Strukturiertes Logging (shared/logger.ts)

JSONL-Format, doppelte Ausgabe (Datei + Konsole), tägliche Dateiaufteilung nach Kategorie.

/var/log/citadel/
├── master/
│   ├── system-2026-04-02.log   ← Lifecycle, Config, Health
│   ├── dialog-2026-04-02.log   ← (von Master nicht genutzt)
│   └── error-2026-04-02.log    ← Fehler (auch in system)
├── citadel-v2/
│   ├── system-2026-04-02.log
│   ├── dialog-2026-04-02.log   ← User ↔ Claude Nachrichten
│   └── error-2026-04-02.log
└── <project-name>/             ← pro ongeboardetem Projekt

Log-Rotation: config/logrotate-citadel.conf/etc/logrotate.d/citadel (täglich, 7-Tage-Aufbewahrung).

Secrets Vault (shared/vault.ts)

AES-256-GCM-verschlüsselter Speicher für Bot-Token.

Key-Quelle: SECRET_ENCRYPTION_KEY env → config/vault-key Datei → auto-generiert
Speicher:   config/vault.json (atomare Schreibvorgänge: tmp + mv)
Cache:      Im Arbeitsspeicher nach initVault() — kein Disk-I/O pro getSecret()
Fallback:   getSecret("FOO") → vault cache → process.env.FOO
Namensgebung: child:<project-name>:token

Der eigene Token des Masters bleibt in .env (Vault lädt innerhalb des Master-Prozesses).

Self-Healing Watchdog (master-bot/watchdog.ts)

Hintergrundmonitor für Child Bots. Läuft im Master-Bot-Prozess.

Alle 30s: HTTP-Health-Check → /api/child/health (5s Timeout)
  Gesund → Fehler zurücksetzen
  Ungesund → Fehler hochzählen
  3+ Fehler + Backoff abgelaufen → Auto-Restart (tmux beenden → neu starten mit Vault-Token)
  Backoff: 30s → 1 min → 5 min → 15 min → 60 min (Cap)
  10 aufeinanderfolgende Fehler → dauerhaft deaktivieren + CEO benachrichtigen

State wird in config/watchdog-state.json persistiert (überlebt Master-Restart). CEO-Benachrichtigungen via Telegram: erster Restart, Restart-Fehler, dauerhafte Deaktivierung. /watchdog-Befehl: Echtzeit-Status aller Children.

Projekt-Entfernungs-Protokoll (Phase 20.4 + 21.0)

/remove_project <name> → dreifache Bestätigung → vollständige Bereinigung:

1. tmux-Sitzung beenden: child-<name>
2. Ghost-Prozesse beenden: ps aux | grep child-<name> → kill -9 alle PIDs
3. Port-Prüfung: ss -tlnp | grep :<port> → erzwingen freigeben, falls belegt
4. Aus bot_registry.json entfernen + In-Memory-Reload
5. /opt/repos/<name>/ löschen (Sicherheit: Pfad muss unter /opt/repos/, min. 3 Segmente)
6. /var/log/citadel/<name>/ löschen

Geschützte Namen: citadel-v2, citadel, claude-ceo, claude-CEO — Entfernung blockiert.

Ghost-Prozess-Problem (gelernte Lektion): Nach tmux kill können verwaiste bun-Prozesse Ports belegen. Massen-Kill + Port-Prüfung verhindert "Phantom-Bots", die auf Health Checks reagieren, aber Telegram ignorieren.

Skill-Verwaltung (Phase 21.0)

Child Bots laden Skills beim Start aus zwei Quellen, dedupliziert via Set:

Quelle 1: MANIFEST.md (JSON)
  → manifest.skills[]          (beim Onboarding abgeglichen)
  → manifest.library_skills[]  (Library .md-Dateien abgeglichen)

Quelle 2: skills/-Verzeichnis
  → *.md Dateien → Dateiname ohne Erweiterung

Zusammenführung: Set<string>(manifest.skills + manifest.library_skills + skills/*.md)

Beispiel (PT-Projekt): 5 aus Manifest + 7 aus skills/ = 12 eindeutige Skills.

Skills erscheinen als:

Sicherheit (Phase 42 — Sentinel-Audit 2026-04-23, 4 Durchläufe, 13 Patches)

Vollständiger Überblick: SECURITY.md. Audit-Bericht: security/audit-2026-04-23.md.

Secrets & Speicherung:

Auth:

Multi-Tenancy (Phase 42):

Netzwerkperimeter:

Eingabe-/Pfadsicherheit:

Regressions-Coverage: scripts/vps-sync.sh führt nach jedem Deploy 7+ Post-Deploy-Smoke-Tests durch (Loopback-Bind, Path-Traversal, chat/save-Validierung, Proxy-Canary, SSE ?token=, Fail-Closed-Validierung, SSRF-Block wenn CEO_TOKEN gesetzt).

Master-Bot-Module (Phase 25 — DEBT-1-Auflösung)

Der Master Bot ist in fokussierte Module aufgeteilt:

master-bot/
├── bot.ts               ← Bootstrap: Vault, Registry, Kontext, alles verdrahten (106 Zeilen)
├── context.ts           ← MasterContext-Typ + Domain-Interfaces
├── api-server.ts        ← Bun.serve() — HTTP, WebSocket, SSE, CRM-Routen
├── tg-api.ts            ← Telegram Bot API-Wrapper (sendMessage, getUpdates usw.)
├── child-state.ts       ← Child-Bot-State, Health, Heartbeat, tmux, Bridge lesen
├── telegram-commands.ts ← Alle /command-Handler, Callback-Query-Handler, Nachrichten-Router
├── telegram.ts          ← Schlanke Polling-Schleife + Re-Exporte für Rückwärtskompatibilität
├── watchdog.ts          ← Self-Healing Child-Bot-Monitor
└── onboarding.ts        ← Interaktiver Projekt-Erstellungs-Wizard

bot.ts importiert nur aus telegram.ts und api-server.ts. Alle anderen Module sind interne Implementierungsdetails.

Bridge CLI (Phase 25 — Local Gateway)

bridge/
├── bin/citadel-bridge.ts    ← CLI-Einstieg (commander.js): connect, pull, push, status, disconnect
├── src/
│   ├── auth.ts              ← JWT-Validierung gegen CRM
│   ├── config.ts            ← ~/.citadel/bridge.json Persistenz
│   ├── inject.ts            ← CLAUDE.md <!-- CITADEL:START/END --> Marker
│   ├── sync.ts              ← Skills-Bundle + Learnings pullen, lokale Learnings pushen
│   └── heartbeat.ts         ← Sitzungs-Aktivitäts-Reporter
├── package.json
└── tsconfig.json

CRM API-Endpunkte (50+ gesamt)

Core CRM (Phase 22)

Endpunkt Methode Zweck
/api/master/health GET Master-Bot-Health (öffentlich)
/api/crm/projects GET Alle Projekte + Live-Health auflisten
/api/crm/projects/:name GET Projektdetails
/api/crm/projects/:name/logs GET JSONL-Logs tailing
/api/crm/projects/:name/files GET Sicheres Verzeichnis-Listing
/api/crm/projects/:name/files/upload POST Dateien hochladen
/api/crm/projects/:name/files/mkdir POST Ordner erstellen
/api/crm/projects/:name/files/create POST Datei erstellen
/api/crm/projects/:name/files/delete DELETE Datei/Ordner löschen
/api/crm/projects/:name/files/clone POST Git-Repo klonen
/api/crm/projects/:name/files/read GET Dateiinhalt lesen
/api/crm/projects/:name/wiki/tree GET Wiki .md-Dateien auflisten
/api/crm/projects/:name/wiki/file GET Wiki-Dateiinhalt lesen
/api/crm/projects/:name/skills GET Installierte Skills
/api/crm/projects/:name/metrics GET Qualitäts-Zeitreihen
/api/crm/projects/:name/restart POST Child Bot neustarten
/api/crm/projects/:name/specs GET Specs auflisten
/api/crm/projects/:name/specs/:id/approve POST Spec genehmigen
/api/crm/projects/:name/specs/:id/reject POST Spec ablehnen
/api/crm/projects/:name/active-role GET/POST Agent-Rolle
/api/crm/projects/:name/message POST Nachricht an Worker in Warteschlange stellen
/api/crm/projects/:name/workers GET/POST Worker-Registry CRUD
/api/crm/projects/:name/workers/:id PUT/DELETE Worker aktualisieren/löschen
/api/crm/projects/:name/workers/generate-prompt POST System-Prompt per KI generieren
/api/crm/projects/:name/skills-bundle GET Skills + Evals für Bridge
/api/crm/projects/:name/learnings GET/POST Learnings-Sync
/api/sse/logs/:name GET SSE-Log-Stream (?category=)

Dashboard & Wiki (Phase 32)

Endpunkt Methode Zweck
/api/crm/projects/:name/wiki/save PUT Wiki-Seite speichern/erstellen
/api/crm/projects/:name/skills/save PUT Skill-Datei speichern
/api/crm/projects/:name/skills/delete DELETE Skill-Datei löschen

Multi-Tenant (Phase 33)

Endpunkt Methode Zweck
/api/crm/account/settings GET/PUT Account-weite API-Keys
/api/crm/projects/create POST Schlanke Projekterstellung
/api/crm/onboarding/setup POST Vollständiger Onboarding-Wizard

MCP & CLI (Phase 34)

Endpunkt Methode Zweck
/api/mcp/issues/:project POST/GET Issue-CRUD
/api/mcp/issues/:project/:id PUT Issue aktualisieren
/api/mcp/wiki/:project PUT Wiki-Sync via MCP
/api/mcp/roadmap/:project GET/PUT Roadmap lesen/synchronisieren
/api/cli/init/:project/:mode GET Cloud-Kontext für CLI
/api/mcp/skills/:project/:skill GET Skill für MCP abrufen
/api/mcp/report/:project POST Missions-Report
/api/mcp/learnings/:project GET Learnings für MCP abrufen

Live Terminal (Phase 35)

Endpunkt Methode Zweck
/api/crm/projects/:name/terminal/log POST Terminal-JSONL von ARC CLI empfangen

Cloud PM & Knowledge (Phase 36)

Endpunkt Methode Zweck
/api/crm/projects/:name/chat POST SSE-Chat-Proxy zur Anthropic API
/api/crm/projects/:name/skills/generate POST Neural Skill Generator (NotebookLM)
/api/crm/projects/:name/notebooks GET Verknüpfte NotebookLM-Notebooks

Auth & OAuth (Phase 37)

Endpunkt Methode Zweck
/api/auth/register POST E-Mail/Passwort-Registrierung
/api/auth/login POST E-Mail/Passwort-Login
/api/auth/google GET OAuth Google-Redirect
/api/auth/callback/google GET OAuth Google-Callback
/api/auth/github GET OAuth GitHub-Redirect
/api/auth/callback/github GET OAuth GitHub-Callback
/api/auth/providers GET Verfügbare OAuth-Provider

Angeheftete Notizen (Phase 41.8)

Endpunkt Methode Zweck
/api/crm/projects/:name/pins GET Angeheftete Notizen auflisten (neueste zuerst)
/api/crm/projects/:name/pins POST Worker-Nachricht an Context Rail anheften (Body: worker_id, body, optional title, author)
/api/crm/projects/:name/pins/:id DELETE Pin entfernen

Backend: Migration 009 (pinned_notes-Tabelle), pinnedNoteQueries in shared/db.ts. Frontend: ContextRail.jsx lädt beim Mount, hört auf crm-pin-created CustomEvent, optimistisches DELETE.

Datenschutz-Ebene (Phase 45)

Hybride At-Rest-Verschlüsselung — clientseitige Krypto-Grundlage + serverseitige Vault-Verschlüsselung.

Clientseitig (Browser)

Serverseitig (Bun + SQLite)

Sicherheits-Header

PII-Bereinigung

Recovery-Key-Endpunkte

Endpunkt Methode Zweck
/api/crm/account/recovery POST Recovery-Key erstellen (verschlüsselten Master-Key speichern)
/api/crm/account/recovery GET Aktive Recovery-Keys auflisten
/api/crm/account/recovery DELETE Recovery-Key(s) widerrufen
/api/crm/account/recovery/restore GET Verschlüsselten Master-Key für Recovery abrufen

Vollständige Sicherheitsdetails: docs/SECURITY.md, docs/architecture/PHASE_45_E2EE.md.