21 KiB
Nexus Board-First Orchestration & Sichere OpenClaw-Integration
Gegenprüfung: Architekt, 2026-06-22 Gegenstand: Sicherster Pfad für Board-first-Agent-Orchestrierung und MCP-artige/strukturierte OpenClaw-Integration im Nexus-Backend Kein Frontend-direkter MCP-Pfad. Kein Deploy.
1. Executive Summary
1.1 Prüfergebnis
Der eingeschlagene Pfad ist architektonisch korrekt und sicher. Das Board-first-Modell mit Nexus-Backend als zentraler Brücke zwischen Benutzer, Board und OpenClaw-Gateway ist der richtige Ansatz. Das Backend fungiert bereits als sichere Schicht zwischen allen Akteuren.
1.2 Kernbewertung
| Aspekt | Status | Bewertung |
|---|---|---|
| Board-first Architektur | ✅ Umsetzung läuft | Parent/Child-Modell korrekt implementiert |
| Kein Frontend-direkter Gateway-Zugriff | ✅ Eingehalten | Frontend spricht NUR mit Nexus-Backend |
| Backend als sichere Brücke | ✅ Eingehalten | API-Container proxyt alle Gateway-Calls |
| Auth-/Rechte-Modell | ✅ Solide | JWT + ApiKey + X-Agent-Id Enforcement |
| Gateway-Security | ⚠️ Verbesserbar | loopback-Bind muss auf lan für Docker |
| Migration-Reihenfolge | 📋 Vorgeschlagen | (siehe Abschnitt 8) |
2. Ist-Architektur: Wer spricht mit wem?
┌──────────────────────────────────────────────────────────────────┐
│ Browser (Bao/Iris) │
│ auth.ts → JWT Access Token (15m) + HttpOnly Refresh Cookie │
│ api.ts → fetch /api/v1/* → Authorization: Bearer <JWT> │
└───────────────────────────┬──────────────────────────────────────┘
│ HTTPS :443 (Traefik/npm)
▼
┌──────────────────────────────────────────────────────────────────┐
│ Nexus web (nginx container) │
│ location /api/ → proxy_pass http://api:8080 │
│ location / → SPA (index.html) │
│ CSP: connect-src 'self' — kein externer Gateway-Call möglich │
└───────────────────────────┬──────────────────────────────────────┘
│ HTTP :8080 (internal network)
▼
┌──────────────────────────────────────────────────────────────────┐
│ Nexus API (.NET 10 Container) ← SICHERE BRÜCKE │
│ │
│ Auth-Middleware (JWT → Controller) │
│ ApiKey-Middleware (X-Nexus-Api-Key → Role=Service) │
│ SecurityHeaders-Middleware (HSTS, CSP, XFO) │
│ Rate Limiter (auth: 5/min/caller, agents: 30/min/caller) │
│ │
│ IOpenClawGatewayClient │
│ ├─ InvokeToolAsync("session_status", ...) │
│ ├─ InvokeToolAsync("sessions_list", ...) │
│ ├─ InvokeToolAsync("sessions_history", ...) │
│ ├─ InvokeToolAsync("memory_search", ...) │
│ ├─ InvokeToolAsync("sessions_send", ...) │
│ └─ InvokeToolAsync("cron", ...) │
│ │
│ GatewayBridgeController (/api/bridge/*) <- NEU (2026-06-22) │
│ └─ ITaskBridgeService │
│ create_task / create_child_task / update_status / │
│ append_activity / handoff / get_board / get_agent_overview │
│ │
│ IAgentRuntime (OpenClawRuntime) │
│ └─ POST /v1/chat/completions (OpenAI-compat) │
│ │
│ ALLE Gateway-Calls → Authorization: Bearer <Gateway-Password> │
└──────────────┬──────────────────────────────┬────────────────────┘
│ host.docker.internal:18789 │
│ (Gateway loopback/lan) │
▼ │
┌──────────────────────────────┐ │
│ OpenClaw Gateway Container │ │
│ Port 18789 │ │
│ Auth: password │ │
│ Bind: loopback (127.0.0.1) │ │
│ │ │
│ HTTP Deny-List (default): │ │
│ exec, spawn, shell, │ │
│ fs_write, fs_delete, │ │
│ fs_move, apply_patch, │ │
│ sessions_spawn, sessions_send│ │
│ cron, gateway, nodes, │ │
│ whatsapp_login │ │
└───────────────────────────────┴──────────────┘
2.1 Wichtig: Das Frontend sieht den Gateway NICHT
Browser ─── [Nexus API] ─── Gateway
│
+─ Gateway-Passwort lebt NUR im Backend
+─ CSP: connect-src 'self' blockiert jeden Direktzugriff
+─ Gateway HTTP Deny-List blockiert alle RCE-Tools
Das ist die korrekte Architektur. Kein Frontend-komponenten-direkter MCP-Pfad existiert und keiner sollte eingeführt werden.
3. Board-First Orchestrierung: Ist-Stand & Bewertung
3.1 Das Parent/Child-Task-Modell (Phase 3)
Parent-Task (Owner: Iris)
├── Child-Task A (AssignedTo: programmer)
├── Child-Task B (AssignedTo: reviewer)
└── Child-Task C (AssignedTo: architekt)
Status: ✅ Implementiert (2026-06-21)
Datenmodell (WorkTask):
ParentTaskId(Guid?) — verknüpft Child mit ParentIsAgentTask(bool) — markiert programmatisch erstellte Agent-TasksExpectedFrom(string?) — wer als nächstes antworten sollAssignedTo(string?) — operativer Owner der Task
State Machine:
Parent: Backlog → InProgress → Review → Done
↘ Blocked → Backlog
Child: Backlog → InProgress → Done
↘ Blocked → Backlog
Rules (aus TaskStateHelper.CanChangeState):
- Nur Iris & Bao dürfen State-Änderungen vornehmen
- Sub-Agenten (programmer, reviewer, architekt, researcher, executor) niemals
nexus-systemals technischer Fallback für Cron/Reset-Stale
3.2 Bewertung: Architektonisch korrekt
| Kriterium | Bewertung | Begründung |
|---|---|---|
| Board als Single Source of Truth | ✅ | Task Board = sichtbare Aufgabenwahrheit |
| Delegation sichtbar | ✅ | Child-Tasks statt unsichtbarem Delegations-Status |
| Feingliedrige Berechtigung | ✅ | State-Change nur durch Iris/Bao |
| Agenten arbeiten gegen Child-Tasks | ✅ | Klare Ownership durch AssignedTo |
| Parent bleibt bei Iris | ✅ | Parent in InProgress während Koordination |
| Review-Gate für Bao | ✅ | Parent erst in Review, dann Bao-Entscheidung |
3.3 Offene Punkte im Board-Modell
-
Keine automatische Child-Task-Erstellung — Das Board-Modell setzt voraus, dass Iris manuell Child-Tasks anlegt. Ein strukturierter Workflow für automatische Child-Task-Erstellung bei
spawn/Subagent-Aufrufen fehlt. -
Keine Task→Session-Verknüpfung — Es gibt keine direkte Verknüpfung zwischen einer Child-Task und der OpenClaw-Subagent-Session, die sie bearbeitet. Der
AgentServicekennt Sessions,TaskServicekennt Tasks — aber sie sind nicht verknüpft. -
Reset-Stale ist ungeschützt —
POST /api/v1/tasks/reset-stalehat[AllowAnonymous]und kann von jedem aufgerufen werden (siehe Risiko #1).
4. Auth- und Rechte-Modell
4.1 Authentifizierungsebenen
Ebene 1: Browser-JWT (Access Token 15m, Refresh Token HttpOnly Cookie)
Ebene 2: X-Nexus-Api-Key (Service-zu-Service, Role=Service)
Ebene 3: Gateway-Password (Backend → Gateway, Bearer Authorization)
Ebene 4: X-Agent-Id Header (Agent-Identität für Task-State-Enforcement)
4.2 Berechtigungsmatrix
| Aktion | Bao | Iris | Sub-Agent | nexus-system | Service (ApiKey) |
|---|---|---|---|---|---|
| Task State ändern | ✅ | ✅ | ❌ | ✅ (intern) | ❌ |
| Task Inhalt editieren | ✅ | ✅ | ✅ | ✅ | ✅ |
| Agent-Config lesen | ✅ | ✅ | ❌ | ❌ | ✅ |
| Gateway-Tool aufrufen | ❌ | ❌ | ❌ | ❌ | ✅ (intern) |
| Dashboard-Metriken sehen | ✅ | ✅ | ❌ | ❌ | ✅ |
4.3 Bewertung
Positiv:
- JWT-Sicherheit entspricht Best Practices (PBKDF2-SHA256, 210k Iterationen, Rotating Refresh Tokens)
- Refresh-Token-Reuse-Detection verhindert Token-Theft
- Rate-Limiting auf Login und Refresh
- Cookie-backed Refresh/Logout über
Secure,HttpOnly,SameSite=StrictplusOrigin-/Sec-Fetch-Site-Prüfung; die unvalidierte Legacy-CSRF-Route wurde in v0.2.60 entfernt - Security Headers (HSTS, CSP, XFO, Referrer-Policy)
Kritisch:
[AllowAnonymous]auf/tasks/boardund/tasks/reset-stale— siehe Risiko-Analyse- Kein Scoped-ApiKey — der
X-Nexus-Api-Keygibt volle Service-Rechte - Keine Audit-Protokollierung für ApiKey-Nutzung
5. Gateway-Integration: Sicherheitsanalyse
5.1 Tool-Invoke-Pfad (Ist-Stand)
POST /api/v1/operations/snapshot
→ DashboardService → OpenClawGatewayClient.InvokeToolAsync()
→ POST http://host.docker.internal:18789/tools/invoke
Authorization: Bearer <Gateway-Password>
Aufgerufene Tools (durch Nexus-Backend):
session_status— Agent-Status abfragen (read-only)sessions_list— Session-Liste (read-only)sessions_history— Chat-Verlauf (read-only)memory_search— Memory-Suche (read-only)sessions_send— Chat-Nachricht senden (write, aber kontrolliert)cron— Cron-Jobs verwalten (write)
Gateway HTTP Deny-List blockiert:
- Alle Exec-Tools (exec, spawn, shell)
- Alle Filesystem-Tools (fs_write, fs_delete, fs_move, apply_patch)
- Gateway-Control-Plane (gateway)
- Node-Relay (nodes)
- Session-Orchestrierung (sessions_spawn, sessions_send)
5.2 Docker-Netzwerk & Gateway-Bind
Aktuelles Problem:
compose.yaml:
api:
extra_hosts:
- host.docker.internal:host-gateway
networks:
- nexus
- openclaw_default ← API-Container ist im Gateway-Netzwerk
Gateway-Konfiguration:
gateway.bind: "loopback" ← Bindet nur 127.0.0.1 IM GATEWAY-CONTAINER
Ergebnis:
host.docker.internal:18789funktioniert, weilextra_hostsauf den Docker-Host zeigt- ABER: Docker-Port-Forward (wenn vorhanden) sendet an Container-IP, nicht loopback
- Die
openclaw_defaultNetzwerk-Mitgliedschaft des API-Containers wird NICHT genutzt
Empfehlung (siehe gateway-api-research.md, Abschnitt 6):
// openclaw.json
{
gateway: {
bind: "lan" // war "loopback"
}
}
Alternativ: API-Container über Gateway-Container-Namen ansprechen:
Integrations__OpenClaw__BaseUrl: http://openclaw_gateway:18789
(Vorausgesetzt der Gateway-Container heißt openclaw_gateway und ist im openclaw_default Netzwerk)
5.3 MCP-artige Integration: Bewertung
Das Gateway /tools/invoke ist bereits MCP-artig:
- JSON-RPC-ähnliche Aufrufe mit
tool+args+sessionKey - Strukturierte Responses mit
{ ok, result, error } - Tool-Discovery via Policy (Deny/Allow-List)
- Request/Response mit eindeutiger Fehlersemantik
Was fehlt für ein vollständiges MCP-Interface:
- Keine Tool-Listing/Discovery über API (kein
tools/list) - Keine Schema-Validierung für Tool-Arguments
- Keine Structured Outputs (function-calling-ähnliches Format)
Empfehlung: NICHT ein MCP-Protokoll zwischen Nexus und Gateway einführen.
Stattdessen den bestehenden /tools/invoke-Pfad weiter nutzen und strukturieren.
5.4 Neue Backend-Bridge (Implementiert 2026-06-22)
Der Nexus-eigene strukturierte Kommando-Adapter wurde eingeführt:
Nexus Backend
├─ GatewayToolClient (bestehender OpenClawGatewayClient)
│ └─ POST /tools/invoke (Gateway)
│
├─ GatewayBridgeController (NEU — /api/bridge/)
│ ├─ POST /api/bridge/tasks (create_task)
│ ├─ POST /api/bridge/tasks/{id}/children (create_child_task)
│ ├─ PATCH /api/bridge/tasks/{id}/status (update_status)
│ ├─ POST /api/bridge/tasks/{id}/activity (append_activity)
│ ├─ POST /api/bridge/tasks/{id}/handoff (handoff)
│ ├─ GET /api/bridge/board (get_board)
│ ├─ GET /api/bridge/tasks/{id} (get_task)
│ ├─ GET /api/bridge/tasks/{id}/children (get_children)
│ ├─ GET /api/bridge/tasks/{id}/activity (get_activity)
│ └─ GET /api/bridge/agent-overview (get_agent_overview)
│
├─ ITaskBridgeService / TaskBridgeService (NEU)
│ └─ Typisierte TaskBridgeResult<T> mit Outcome: Success/NotFound/InvalidState/Unauthorized/ValidationError
│
├─ BoardOrchestrationService (offen)
│ ├─ Tasks erstellen/aktualisieren
│ ├─ Session-Status abfragen
│ └─ Agent-Progress berechnen
│
└─ AgentDelegationService (offen)
├─ Subagent-Task anlegen
├─ Session verfolgen
└─ Ergebnis integrieren
Auth-Modell für /api/bridge:
- Primär:
X-Agent-IdHeader (Agent-Identität vom Gateway) - Fallback: JWT (Browser-authenticated user → bao)
- Fallback:
X-Nexus-Api-Key(Backend-zu-Backend Service-Identität) - Rate-Limiting: 30 Requests/Minute (agents-Policy)
Das Frontend sieht /api/bridge NICHT. Der Pfad ist ausschließlich für Agent-zu-Backend-Kommunikation.
6. Risikoanalyse
6.1 KRITISCH — [AllowAnonymous] auf Board-Endpunkten ✅ BEHOBEN (2026-06-22)
Betroffen (vorher):
// TasksController.cs
[AllowAnonymous]
[HttpGet("board")] // Gab ALLE Tasks zurück — inkl. Detail-Texte
[AllowAnonymous]
[HttpPost("reset-stale")] // Konnte Tasks zurücksetzen — datenändernd
Fix angewandt:
[AllowAnonymous]entfernt- Inline-Auth-Check:
X-Agent-IdHeader, ApiKey-Rolle, oder JWT erforderlich reset-staleerfordert zusätzlichX-Agent-Id: irisIdentität (nur Iris darf)- Neuer
/api/bridge/Pfad als sauberer Agent-zu-Backend-Adapter - Rate-Limiting (agents-Policy: 30/min) auf Bridge-Endpunkte
6.2 MITTEL — Keine Task→Session-Verknüpfung
Wenn ein Subagent eine Child-Task bearbeitet, gibt es keine technische Verknüpfung zwischen der Child-Task und der OpenClaw-Session. Das bedeutet:
- Keine automatische Status-Aktualisierung bei Session-Abschluss
- Keine Sitzungs-Historie direkt von der Task aus erreichbar
- Iris muss manuell prüfen, ob ein Agent fertig ist
Empfehlung:
WorkTaskumSessionKey(string?) erweitern- Bei Child-Task-Erstellung Session-Key speichern
- Status-Polling: Wenn Session inaktiv, Child-Task auf Done/Blocked prüfen
6.3 MITTEL — Gateway-Passwort in Config-Dateien
Das Gateway-Passwort ieDmOjBiVfbbDM0ibrEebPAg ist:
- In
.envauf dem Host (OK) - In
gateway-api-research.md(maskiert:ieDm...PAg) - Im
openclaw.jsonauf dem Host (OK) - In der API-Container-Umgebungsvariable (notwendig)
Empfehlung:
- Gateway-Rate-Limiting aktiv halten (10 attempts/60s → 5min lockout)
- Gateway-Bind auf
lanändern (nicht öffentlich exponiert) gateway-api-research.mdaus dem öffentlichen Repo entfernen oder Passwort entfernen
6.4 NIEDRIG — Keine Scoped API-Keys
Der X-Nexus-Api-Key gibt volle Service-Rechte. Es gibt keine Möglichkeit, verschiedene
API-Keys mit unterschiedlichen Rechten zu vergeben.
Empfehlung (spätere Phase):
- API-Key-Scopes einführen (read, write, admin)
- Rate-Limiting pro API-Key
- Audit-Log für ApiKey-Nutzung
6.5 NIEDRIG — Kein Structured Output vom Gateway
Die /tools/invoke-Responses sind JSON, aber ohne Schema-Garantie. Die Backend-Logik
extrahiert Felder mit vielen Fallbacks (??-Kettenantworten).
Empfehlung:
- Response-Typen für jedes Tool definieren (DTOs)
- Deserialisierung mit Schema-Validierung
- Fallback-Logik zentralisieren
7. Migrationsreihenfolge (Vorschlag)
Phase 3b — Sichere Board-Grundlage (JETZT)
1. [AllowAnonymous] auf /tasks/board und /tasks/reset-stale fixen
→ ApiKey-Auth plus X-Agent-Id-Validierung
→ README/Iris-Doku aktualisieren
2. Gateway-Bind von loopback auf lan ändern
→ API-Container über openclaw_default Netzwerk ansprechen
→ host.docker.internal-Fallback entfernen
Phase 4 — Strukturierte Orchestrierung
3. Task→Session-Verknüpfung einführen
→ WorkTask.SessionKey (string?)
→ Bei Child-Task-Erstellung Session speichern
4. AgentDelegationService
→ Zentralisierte Subagent-Task-Erstellung
→ Session-Status-Monitoring
→ Automatische Status-Propagation (Session done → Task done)
5. BoardOrchestrationService
→ Refresh-Intervall für Agent-Progress
→ Stale-Erkennung mit Session-Status
→ Priorisierung nach Workload
Phase 5 — Erweiterte Integration
6. Structured Tool Responses
→ DTOs für jedes Gateway-Tool
→ Schema-Validierung
→ Caching für häufige Abfragen
7. API-Key-Scopes
→ read/write/admin Scopes
→ Audit-Log für ApiKey-Nutzung
8. Automatische Child-Task-Erstellung
→ Bei spawn/subagent-Aufrufen automatisch Child-Task anlegen
→ Session-Key verknüpfen
→ Activity-Feed erweitern
8. Sichere Brücke: Architekturprinzipien
8.1 Das Backend ist die einzige Brücke
Browser ←→ Nexus API ←→ OpenClaw Gateway
↑ ↑
JWT Auth Gateway Password
(pro User) (nur im Backend)
Niemals:
- Gateway-Passwort im Frontend
- Direkter Browser→Gateway API-Call
- MCP-Protokoll zwischen Frontend und Gateway
- Agent-Sessions direkt aus dem Frontend steuern
8.2 Prinzipien für jede neue Integration
- Neue Endpunkte immer im Nexus-Backend
- Auth über bestehendes JWT/ApiKey-System
- Gateway-Calls immer serverseitig mit Gateway-Passwort
- Kein Gateway-Tool direkt aus dem Frontend aufrufen
- State-Änderungen nur durch Iris/Bao (via Backend-Enforcement)
- Activity/Audit für jede State-Änderung
8.3 Strukturierte OpenClaw-Integration (MCP-artig)
Der Gateway /tools/invoke-Endpunkt ist bereits strukturell MCP-artig. Eine formale
MCP-Implementierung zwischen Nexus und Gateway ist nicht notwendig. Stattdessen:
Nexus.Backend.Services
├── IOpenClawGatewayClient (Gateway-Tool-Abstraktion)
│ └── InvokeToolAsync(tool, args) → JsonNode?
│
├── IBoardOrchestrationService (NEU)
│ ├── CreateDelegateTask(agentId, title, detail) → WorkTask
│ ├── WatchAgentSession(agentId) → SessionWatcher
│ └── SyncAgentProgress() → Progress[]
│
└── IAgentDelegationService (NEU)
├── DelegateToAgent(parentTaskId, agentId, instruction)
├── CollectResult(subTaskId) → AgentResult
└── HandleBlocker(subTaskId, reason) → void
9. Zusammenfassung
Was gut ist (nicht ändern):
- Board-first-Ansatz mit Parent/Child-Tasks
- Backend als einzige Gateway-Brücke
- JWT + Gateway-Password-Trennung
- State-Change-Restriktion auf Iris/Bao
- HTTP Deny-List des Gateways
- CSP im Frontend (kein externer Connect)
Was verbessert werden muss:
[AllowAnonymous]auf Board-Endpunkten → ApiKey-Auth- Gateway-Bind loopback → lan (oder Netzwerk-Routing korrigieren)
- Task→Session-Verknüpfung fehlt
Was später kommen kann:
- Automatische Child-Task-Erstellung bei Subagent-Aufrufen
- Structured Gateway Responses mit Schema-Validierung
- API-Key-Scopes und Audit
- Session-gesteuertes Progress-Tracking
Was niemals kommen darf:
- Gateway-Passwort im Frontend
- Direkter MCP-Pfad Browser→Gateway
- Agent-Session-Steuerung aus dem Frontend
- Task-State-Änderung durch Sub-Agenten