Control UI (Browser)

Die Control UI ist eine kleine Vite + Lit Single-Page-App, die vom Gateway bereitgestellt wird:

  • Standard: http://<host>:18789/
  • Optionales Präfix: setze gateway.controlUi.basePath (z.B. /openclaw)

Sie kommuniziert direkt mit dem Gateway WebSocket auf demselben Port.

Schnell öffnen (lokal)

Wenn das Gateway auf demselben Computer läuft, öffne:

Falls die Seite nicht lädt, starte zuerst das Gateway: openclaw gateway.

Die Authentifizierung erfolgt während des WebSocket-Handshakes über:

  • connect.params.auth.token
  • connect.params.auth.password

Das Dashboard-Einstellungspanel ermöglicht das Speichern eines Tokens; Passwörter werden nicht gespeichert. Der Onboarding-Wizard generiert standardmäßig ein Gateway-Token – füge es beim ersten Verbinden hier ein.

Device Pairing (erste Verbindung)

Wenn du dich von einem neuen Browser oder Gerät mit der Control UI verbindest, verlangt das Gateway eine einmalige Pairing-Freigabe — selbst wenn du im selben Tailnet bist mit gateway.auth.allowTailscale: true. Das ist eine Sicherheitsmaßnahme gegen unbefugten Zugriff.

Was du siehst: “disconnected (1008): pairing required”

So gibst du das Gerät frei:

# Ausstehende Anfragen auflisten
openclaw devices list

# Nach Request-ID freigeben
openclaw devices approve <requestId>

Nach der Freigabe wird das Gerät gespeichert und benötigt keine erneute Freigabe, außer du widerrufst sie mit openclaw devices revoke --device <id> --role <role>. Siehe Devices CLI für Token-Rotation und Widerruf.

Hinweise:

  • Lokale Verbindungen (127.0.0.1) werden automatisch freigegeben.
  • Remote-Verbindungen (LAN, Tailnet, etc.) benötigen explizite Freigabe.
  • Jedes Browser-Profil generiert eine eindeutige Device-ID, daher erfordert ein Browserwechsel oder das Löschen von Browser-Daten ein erneutes Pairing.

Was sie kann (aktuell)

  • Chat mit dem Modell über Gateway WS (chat.history, chat.send, chat.abort, chat.inject)
  • Streaming von Tool-Aufrufen + Live-Tool-Output-Karten im Chat (Agent-Events)
  • Channels: WhatsApp/Telegram/Discord/Slack + Plugin-Channels (Mattermost, etc.) Status + QR-Login + Channel-spezifische Config (channels.status, web.login.*, config.patch)
  • Instances: Presence-Liste + Refresh (system-presence)
  • Sessions: Liste + Session-spezifische Thinking/Verbose-Overrides (sessions.list, sessions.patch)
  • Cron-Jobs: Liste/Hinzufügen/Ausführen/Aktivieren/Deaktivieren + Ausführungsverlauf (cron.*)
  • Skills: Status, Aktivieren/Deaktivieren, Installation, API-Key-Updates (skills.*)
  • Nodes: Liste + Capabilities (node.list)
  • Exec-Freigaben: Gateway- oder Node-Allowlists bearbeiten + Ask-Policy für exec host=gateway/node (exec.approvals.*)
  • Config: Anzeigen/Bearbeiten von ~/.openclaw/openclaw.json (config.get, config.set)
  • Config: Anwenden + Neustart mit Validierung (config.apply) und Aufwecken der zuletzt aktiven Session
  • Config-Schreibvorgänge enthalten einen Base-Hash-Guard, um gleichzeitige Bearbeitungen zu verhindern
  • Config-Schema + Formular-Rendering (config.schema, inkl. Plugin- + Channel-Schemas); Raw-JSON-Editor bleibt verfügbar
  • Debug: Status/Health/Models-Snapshots + Event-Log + manuelle RPC-Aufrufe (status, health, models.list)
  • Logs: Live-Tail der Gateway-Datei-Logs mit Filter/Export (logs.tail)
  • Update: Package/Git-Update ausführen + Neustart (update.run) mit Neustart-Report

Chat-Verhalten

  • chat.send ist non-blocking: Es bestätigt sofort mit { runId, status: "started" } und die Antwort wird über chat-Events gestreamt.
  • Erneutes Senden mit demselben idempotencyKey gibt { status: "in_flight" } während der Ausführung zurück und { status: "ok" } nach Abschluss.
  • chat.inject fügt eine Assistenten-Notiz zum Session-Transcript hinzu und sendet ein chat-Event nur für UI-Updates (kein Agent-Run, keine Channel-Zustellung).
  • Stoppen:
    • Klicke Stop (ruft chat.abort auf)
    • Tippe /stop (oder stop|esc|abort|wait|exit|interrupt), um out-of-band abzubrechen
    • chat.abort unterstützt { sessionKey } (ohne runId), um alle aktiven Runs für diese Session abzubrechen

Tailnet-Zugriff (empfohlen)

Integriertes Tailscale Serve (bevorzugt)

Halte das Gateway auf Loopback und lass Tailscale Serve es mit HTTPS proxyen:

openclaw gateway --tailscale serve

Öffne:

  • https://<magicdns>/ (oder dein konfigurierter gateway.controlUi.basePath)

Standardmäßig können Serve-Anfragen sich über Tailscale-Identity-Header (tailscale-user-login) authentifizieren, wenn gateway.auth.allowTailscale auf true steht. OpenClaw verifiziert die Identität, indem es die x-forwarded-for-Adresse mit tailscale whois auflöst und mit dem Header abgleicht, und akzeptiert diese nur, wenn die Anfrage Loopback mit Tailscales x-forwarded-*-Headern erreicht. Setze gateway.auth.allowTailscale: false (oder erzwinge gateway.auth.mode: "password"), wenn du auch für Serve-Traffic ein Token/Passwort verlangen willst.

An Tailnet binden + Token

openclaw gateway --bind tailnet --token "$(openssl rand -hex 32)"

Dann öffne:

  • http://<tailscale-ip>:18789/ (oder dein konfigurierter gateway.controlUi.basePath)

Füge das Token in die UI-Einstellungen ein (wird als connect.params.auth.token gesendet).

Unsicheres HTTP

Wenn du das Dashboard über einfaches HTTP öffnest (http://<lan-ip> oder http://<tailscale-ip>), läuft der Browser in einem non-secure context und blockiert WebCrypto. Standardmäßig blockiert OpenClaw Control-UI-Verbindungen ohne Device-Identity.

Empfohlene Lösung: Nutze HTTPS (Tailscale Serve) oder öffne die UI lokal:

  • https://<magicdns>/ (Serve)
  • http://127.0.0.1:18789/ (auf dem Gateway-Host)

Downgrade-Beispiel (nur Token über HTTP):

{
  gateway: {
    controlUi: { allowInsecureAuth: true },
    bind: "tailnet",
    auth: { mode: "token", token: "replace-me" },
  },
}

Das deaktiviert Device-Identity + Pairing für die Control UI (auch bei HTTPS). Nutze das nur, wenn du dem Netzwerk vertraust.

Siehe Tailscale für HTTPS-Setup-Anleitung.

UI bauen

Das Gateway stellt statische Dateien aus dist/control-ui bereit. Baue sie mit:

pnpm ui:build # installiert UI-Abhängigkeiten beim ersten Durchlauf automatisch

Optionale absolute Base (wenn du fixe Asset-URLs willst):

OPENCLAW_CONTROL_UI_BASE_PATH=/openclaw/ pnpm ui:build

Für lokale Entwicklung (separater Dev-Server):

pnpm ui:dev # installiert UI-Abhängigkeiten beim ersten Durchlauf automatisch

Dann richte die UI auf deine Gateway-WS-URL aus (z.B. ws://127.0.0.1:18789).

Debugging/Testing: Dev-Server + Remote-Gateway

Die Control UI besteht aus statischen Dateien; das WebSocket-Ziel ist konfigurierbar und kann sich vom HTTP-Origin unterscheiden. Das ist praktisch, wenn du den Vite-Dev-Server lokal haben willst, aber das Gateway woanders läuft.

  1. Starte den UI-Dev-Server: pnpm ui:dev
  2. Öffne eine URL wie:
http://localhost:5173/?gatewayUrl=ws://<gateway-host>:18789

Optionale einmalige Authentifizierung (falls nötig):

http://localhost:5173/?gatewayUrl=wss://<gateway-host>:18789&token=<gateway-token>

Hinweise:

  • gatewayUrl wird nach dem Laden in localStorage gespeichert und aus der URL entfernt.
  • token wird in localStorage gespeichert; password wird nur im Speicher gehalten.
  • Nutze wss://, wenn das Gateway hinter TLS läuft (Tailscale Serve, HTTPS-Proxy, etc.).

Details zum Remote-Zugriff: Remote access.