Control UI (Browser)

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

  • Standard: http://<host>:18789/
  • Optionaler 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:

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

Authentifizierung erfolgt beim WebSocket-Handshake über:

  • connect.params.auth.token
  • connect.params.auth.password Das Dashboard-Einstellungspanel speichert ein Token für die aktuelle Browser-Tab-Sitzung und die gewählte Gateway-URL; Passwörter werden nicht gespeichert. Der Einrichtungsassistent generiert standardmäßig ein Gateway-Token — füge es hier beim ersten Verbinden ein.

Gerätekopplung (erste Verbindung)

Wenn du dich von einem neuen Browser oder Gerät mit der Control UI verbindest, verlangt das Gateway eine einmalige Kopplungsgenehmigung — 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”

Um das Gerät zu genehmigen:

# Ausstehende Anfragen auflisten
openclaw devices list

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

Einmal genehmigt, wird das Gerät gespeichert und benötigt keine erneute Genehmigung, es sei denn du widerrufst es mit openclaw devices revoke --device <id> --role <role>. Siehe Geräte-CLI für Token-Rotation und Widerruf.

Hinweise:

  • Lokale Verbindungen (127.0.0.1) werden automatisch genehmigt.
  • Remote-Verbindungen (LAN, Tailnet usw.) erfordern explizite Genehmigung.
  • Jedes Browser-Profil generiert eine eindeutige Geräte-ID, daher erfordert ein Browserwechsel oder das Löschen der Browserdaten eine erneute Kopplung.

Sprachunterstützung

Die Control UI kann sich beim ersten Laden basierend auf deiner Browser-Sprache lokalisieren, und du kannst sie später über die Sprachauswahl in der Access-Karte ändern.

  • Unterstützte Locales: en, zh-CN, zh-TW, pt-BR, de, es
  • Nicht-englische Übersetzungen werden im Browser lazy geladen.
  • Das gewählte Locale wird im Browser-Speicher gespeichert und bei zukünftigen Besuchen wiederverwendet.
  • Fehlende Übersetzungsschlüssel fallen auf Englisch zurück.

Was sie kann (aktuell)

  • Mit dem Modell via Gateway-WS chatten (chat.history, chat.send, chat.abort, chat.inject)
  • Streaming-Tool-Calls + Live-Tool-Ausgabekarten im Chat (Agent-Events)
  • Kanäle: WhatsApp/Telegram/Discord/Slack + Plugin-Kanäle (Mattermost usw.) Status + QR-Login + kanalspezifische Konfiguration (channels.status, web.login.*, config.patch)
  • Instanzen: Präsenzliste + Aktualisierung (system-presence)
  • Sitzungen: Liste + sitzungsspezifische Thinking/Fast/Verbose/Reasoning-Overrides (sessions.list, sessions.patch)
  • Cron-Jobs: Liste/Hinzufügen/Bearbeiten/Ausführen/Aktivieren/Deaktivieren + Ausführungsverlauf (cron.*)
  • Skills: Status, Aktivieren/Deaktivieren, Installieren, API-Key-Updates (skills.*)
  • Nodes: Liste + Fähigkeiten (node.list)
  • Exec-Genehmigungen: Gateway- oder Node-Allowlists bearbeiten + Ask-Richtlinie für exec host=gateway/node (exec.approvals.*)
  • Konfiguration: ~/.openclaw/openclaw.json anzeigen/bearbeiten (config.get, config.set)
  • Konfiguration: Anwenden + Neustart mit Validierung (config.apply) und letzte aktive Sitzung wecken
  • Konfigurationsschreibvorgänge enthalten einen Base-Hash-Schutz gegen das Überschreiben gleichzeitiger Änderungen
  • Konfigurations-Schema + Formulardarstellung (config.schema, einschließlich Plugin- + Kanal-Schemas); Raw-JSON-Editor bleibt verfügbar
  • Debug: Status-/Health-/Modell-Snapshots + Event-Log + manuelle RPC-Aufrufe (status, health, models.list)
  • Logs: Live-Tail der Gateway-Dateilogs mit Filter/Export (logs.tail)
  • Update: Paket-/Git-Update + Neustart ausführen (update.run) mit Neustartbericht

Hinweise zum Cron-Jobs-Panel:

  • Für isolierte Jobs wird die Zustellung standardmäßig als Zusammenfassungsankündigung gesendet. Du kannst auf „keine” umschalten, wenn du rein interne Runs möchtest.
  • Kanal-/Zielfelder erscheinen, wenn Ankündigung ausgewählt ist.
  • Webhook-Modus nutzt delivery.mode = "webhook" mit delivery.to als gültige HTTP(S)-Webhook-URL.
  • Für Hauptsitzungs-Jobs sind Webhook- und Keine-Zustellmodi verfügbar.
  • Erweiterte Bearbeitungssteuerungen umfassen Löschen-nach-Ausführung, Agent-Override löschen, Cron-Exakt/Stagger-Optionen, Agent-Modell/Thinking-Overrides und Best-Effort-Zustellungsschalter.
  • Formularvalidierung ist inline mit Feldebenen-Fehlern; ungültige Werte deaktivieren den Speichern-Button bis zur Korrektur.
  • Setze cron.webhookToken, um ein dediziertes Bearer-Token zu senden; wenn weggelassen, wird der Webhook ohne Auth-Header gesendet.
  • Veralteter Fallback: gespeicherte Legacy-Jobs mit notify: true können weiterhin cron.webhook nutzen, bis sie migriert werden.

Chat-Verhalten

  • chat.send ist nicht-blockierend: Es bestätigt sofort mit { runId, status: "started" } und die Antwort streamt über chat-Events.
  • Erneutes Senden mit demselben idempotencyKey gibt { status: "in_flight" } während der Ausführung und { status: "ok" } nach Abschluss zurück.
  • chat.history-Antworten sind größenbeschränkt für UI-Sicherheit. Wenn Transkripteinträge zu groß sind, kann das Gateway lange Textfelder kürzen, schwere Metadatenblöcke weglassen und übergroße Nachrichten durch einen Platzhalter ersetzen ([chat.history omitted: message too large]).
  • chat.inject hängt eine Assistenten-Notiz an das Sitzungstranskript an und sendet ein chat-Event für reine UI-Updates (kein Agent-Run, keine Kanalzustellung).
  • Stoppen:
    • Klicke Stopp (ruft chat.abort auf)
    • Tippe /stop (oder eigenständige Abbruch-Phrasen wie stop, stop action, stop run, stop openclaw, please stop), um out-of-band abzubrechen
    • chat.abort unterstützt { sessionKey } (ohne runId), um alle aktiven Runs für diese Sitzung abzubrechen
  • Partielle Beibehaltung bei Abbruch:
    • Wenn ein Run abgebrochen wird, kann partieller Assistententext weiterhin in der UI angezeigt werden
    • Das Gateway speichert abgebrochenen partiellen Assistententext in der Transkript-History, wenn gepufferter Output existiert
    • Gespeicherte Einträge enthalten Abbruch-Metadaten, damit Transkript-Konsumenten Abbruch-Partials von normaler Ausgabe unterscheiden können

Tailnet-Zugang (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 Control-UI-/WebSocket-Serve-Anfragen sich über Tailscale-Identity-Header (tailscale-user-login) authentifizieren, wenn gateway.auth.allowTailscale true ist. OpenClaw verifiziert die Identität, indem es die x-forwarded-for-Adresse per tailscale whois auflöst und mit dem Header abgleicht, und akzeptiert diese nur, wenn die Anfrage auf Loopback mit Tailscales x-forwarded-*-Headern ankommt. Setze gateway.auth.allowTailscale: false (oder erzwinge gateway.auth.mode: "password"), wenn du auch für Serve-Traffic ein Token/Passwort verlangen möchtest. Tokenlose Serve-Auth setzt voraus, dass der Gateway-Host vertrauenswürdig ist. Wenn nicht vertrauenswürdiger lokaler Code auf diesem Host laufen könnte, verlange Token/Passwort-Auth.

Tailnet-Bind + 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 den UI-Einstellungen ein (wird als connect.params.auth.token gesendet).

Unsicheres HTTP

Wenn du das Dashboard über unverschlüsseltes HTTP öffnest (http://<lan-ip> oder http://<tailscale-ip>), läuft der Browser in einem nicht-sicheren Kontext und blockiert WebCrypto. Standardmäßig blockiert OpenClaw Control-UI-Verbindungen ohne Geräteidentität.

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

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

Verhalten des Unsicher-Auth-Schalters:

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

allowInsecureAuth ist ein reiner lokaler Kompatibilitätsschalter:

  • Er erlaubt localhost-Control-UI-Sitzungen ohne Geräteidentität in nicht-sicheren HTTP-Kontexten fortzufahren.
  • Er umgeht keine Kopplungsprüfungen.
  • Er lockert keine Remote-(nicht-localhost-)Geräteidentitätsanforderungen.

Nur für Notfälle:

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

dangerouslyDisableDeviceAuth deaktiviert Control-UI-Geräteidentitätsprüfungen und ist eine schwerwiegende Sicherheitsabsenkung. Nach Notfallnutzung schnell zurücksetzen.

Siehe Tailscale für HTTPS-Setup-Anleitung.

UI bauen

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

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

Optionaler absoluter Basispfad (wenn du feste Asset-URLs möchtest):

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 Lauf automatisch

Richte dann die UI auf deine Gateway-WS-URL (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-Ursprung unterscheiden. Das ist praktisch, wenn du den Vite-Dev-Server lokal nutzt, aber das Gateway woanders läuft.

  1. UI-Dev-Server starten: pnpm ui:dev
  2. Eine URL wie diese öffnen:
http://localhost:5173/?gatewayUrl=ws://<gateway-host>:18789

Optionale einmalige Auth (wenn 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 aus dem URL-Fragment importiert, in sessionStorage für die aktuelle Browser-Tab-Sitzung und gewählte Gateway-URL gespeichert und aus der URL entfernt; es wird nicht in localStorage gespeichert.
  • password wird nur im Arbeitsspeicher gehalten.
  • Wenn gatewayUrl gesetzt ist, fällt die UI nicht auf Konfigurations- oder Umgebungszugangsdaten zurück. Gib token (oder password) explizit an. Fehlende explizite Zugangsdaten sind ein Fehler.
  • Verwende wss://, wenn das Gateway hinter TLS liegt (Tailscale Serve, HTTPS-Proxy usw.).
  • gatewayUrl wird nur in einem Top-Level-Fenster akzeptiert (nicht eingebettet), um Clickjacking zu verhindern.
  • Nicht-Loopback-Control-UI-Deployments müssen gateway.controlUi.allowedOrigins explizit setzen (vollständige Origins). Das gilt auch für Remote-Dev-Setups.
  • gateway.controlUi.dangerouslyAllowHostHeaderOriginFallback=true aktiviert den Host-Header-Origin-Fallback-Modus, ist aber eine gefährliche Sicherheitsabsenkung.

Beispiel:

{
  gateway: {
    controlUi: {
      allowedOrigins: ["http://localhost:5173"],
    },
  },
}

Remote-Zugang-Setup-Details: Remote-Zugang.