Signal (signal-cli)

Status: Externe CLI-Integration. Der Gateway kommuniziert mit signal-cli über HTTP JSON-RPC + SSE.

Schnellstart (für Einsteiger)

  1. Nutze eine separate Signal-Nummer für den Bot (empfohlen).
  2. Installiere signal-cli (Java erforderlich).
  3. Verknüpfe das Bot-Gerät und starte den Daemon:
    • signal-cli link -n "OpenClaw"
  4. Konfiguriere OpenClaw und starte den Gateway.

Minimale Konfiguration:

{
  channels: {
    signal: {
      enabled: true,
      account: "+15551234567",
      cliPath: "signal-cli",
      dmPolicy: "pairing",
      allowFrom: ["+15557654321"],
    },
  },
}

Was ist das?

  • Signal Channel über signal-cli (nicht eingebettetes libsignal).
  • Deterministisches Routing: Antworten gehen immer zurück zu Signal.
  • DMs teilen sich die Haupt-Session des Agent; Gruppen sind isoliert (agent:<agentId>:signal:group:<groupId>).

Config-Schreibzugriff

Standardmäßig darf Signal Config-Updates durchführen, die durch /config set|unset ausgelöst werden (erfordert commands.config: true).

Deaktivieren mit:

{
  channels: { signal: { configWrites: false } },
}

Das Nummernmodell (wichtig)

  • Der Gateway verbindet sich mit einem Signal-Gerät (dem signal-cli-Account).
  • Wenn du den Bot auf deinem persönlichen Signal-Account laufen lässt, ignoriert er deine eigenen Nachrichten (Loop-Schutz).
  • Für “Ich schreibe dem Bot und er antwortet” brauchst du eine separate Bot-Nummer.

Setup (schneller Weg)

  1. Installiere signal-cli (Java erforderlich).
  2. Verknüpfe einen Bot-Account:
    • signal-cli link -n "OpenClaw" und scanne dann den QR-Code in Signal.
  3. Konfiguriere Signal und starte den Gateway.

Beispiel:

{
  channels: {
    signal: {
      enabled: true,
      account: "+15551234567",
      cliPath: "signal-cli",
      dmPolicy: "pairing",
      allowFrom: ["+15557654321"],
    },
  },
}

Multi-Account-Unterstützung: Nutze channels.signal.accounts mit individueller Konfiguration pro Account und optionalem name. Das gemeinsame Muster findest du unter gateway/configuration.

Externer Daemon-Modus (httpUrl)

Wenn du signal-cli selbst verwalten möchtest (langsame JVM-Kaltstarts, Container-Init oder geteilte CPUs), führe den Daemon separat aus und weise OpenClaw darauf hin:

{
  channels: {
    signal: {
      httpUrl: "http://127.0.0.1:8080",
      autoStart: false,
    },
  },
}

Damit wird das automatische Spawnen und die Startup-Wartezeit in OpenClaw übersprungen. Bei langsamen Starts beim Auto-Spawning setze channels.signal.startupTimeoutMs.

Zugriffskontrolle (DMs + Gruppen)

DMs:

  • Standard: channels.signal.dmPolicy = "pairing".
  • Unbekannte Absender erhalten einen Pairing-Code; Nachrichten werden ignoriert, bis sie freigegeben sind (Codes laufen nach 1 Stunde ab).
  • Freigabe über:
    • openclaw pairing list signal
    • openclaw pairing approve signal <CODE>
  • Pairing ist der Standard-Token-Austausch für Signal-DMs. Details: Pairing
  • UUID-only-Absender (von sourceUuid) werden als uuid:<id> in channels.signal.allowFrom gespeichert.

Gruppen:

  • channels.signal.groupPolicy = open | allowlist | disabled.
  • channels.signal.groupAllowFrom steuert, wer in Gruppen triggern kann, wenn allowlist gesetzt ist.

Wie es funktioniert (Verhalten)

  • signal-cli läuft als Daemon; der Gateway liest Events via SSE.
  • Eingehende Nachrichten werden in das gemeinsame Channel-Envelope normalisiert.
  • Antworten werden immer zurück zur gleichen Nummer oder Gruppe geroutet.

Medien + Limits

  • Ausgehender Text wird in Chunks von channels.signal.textChunkLimit aufgeteilt (Standard 4000).
  • Optionales Newline-Chunking: Setze channels.signal.chunkMode="newline", um bei Leerzeilen (Absatzgrenzen) zu splitten, bevor nach Länge gechunkt wird.
  • Attachments werden unterstützt (base64 von signal-cli abgerufen).
  • Standard-Medien-Cap: channels.signal.mediaMaxMb (Standard 8).
  • Nutze channels.signal.ignoreAttachments, um Medien-Downloads zu überspringen.
  • Gruppen-Verlaufskontext nutzt channels.signal.historyLimit (oder channels.signal.accounts.*.historyLimit), mit Fallback auf messages.groupChat.historyLimit. Setze 0 zum Deaktivieren (Standard 50).

Tippindikatoren + Lesebestätigungen

  • Tippindikatoren: OpenClaw sendet Tippsignale via signal-cli sendTyping und aktualisiert sie, während eine Antwort läuft.
  • Lesebestätigungen: Wenn channels.signal.sendReadReceipts auf true steht, leitet OpenClaw Lesebestätigungen für erlaubte DMs weiter.
  • Signal-cli stellt keine Lesebestätigungen für Gruppen bereit.

Reaktionen (message tool)

  • Nutze message action=react mit channel=signal.
  • Ziele: Absender-E.164 oder UUID (nutze uuid:<id> aus der Pairing-Ausgabe; blanke UUID funktioniert auch).
  • messageId ist der Signal-Timestamp der Nachricht, auf die du reagierst.
  • Gruppenreaktionen erfordern targetAuthor oder targetAuthorUuid.

Beispiele:

message action=react channel=signal target=uuid:123e4567-e89b-12d3-a456-426614174000 messageId=1737630212345 emoji=🔥
message action=react channel=signal target=+15551234567 messageId=1737630212345 emoji=🔥 remove=true
message action=react channel=signal target=signal:group:<groupId> targetAuthor=uuid:<sender-uuid> messageId=1737630212345 emoji=✅

Konfiguration:

  • channels.signal.actions.reactions: Reaktionsaktionen aktivieren/deaktivieren (Standard true).
  • channels.signal.reactionLevel: off | ack | minimal | extensive.
    • off/ack deaktiviert Agent-Reaktionen (message tool react gibt einen Fehler).
    • minimal/extensive aktiviert Agent-Reaktionen und setzt das Guidance-Level.
  • Pro-Account-Overrides: channels.signal.accounts.<id>.actions.reactions, channels.signal.accounts.<id>.reactionLevel.

Zustellziele (CLI/cron)

  • DMs: signal:+15551234567 (oder blanke E.164).
  • UUID-DMs: uuid:<id> (oder blanke UUID).
  • Gruppen: signal:group:<groupId>.
  • Usernames: username:<name> (falls von deinem Signal-Account unterstützt).

Konfigurationsreferenz (Signal)

Vollständige Konfiguration: Configuration

Provider-Optionen:

  • channels.signal.enabled: Channel-Startup aktivieren/deaktivieren.
  • channels.signal.account: E.164 für den Bot-Account.
  • channels.signal.cliPath: Pfad zu signal-cli.
  • channels.signal.httpUrl: Vollständige Daemon-URL (überschreibt host/port).
  • channels.signal.httpHost, channels.signal.httpPort: Daemon-Bind (Standard 127.0.0.1:8080).
  • channels.signal.autoStart: Daemon automatisch spawnen (Standard true, wenn httpUrl nicht gesetzt).
  • channels.signal.startupTimeoutMs: Startup-Wartezeit-Timeout in ms (max 120000).
  • channels.signal.receiveMode: on-start | manual.
  • channels.signal.ignoreAttachments: Attachment-Downloads überspringen.
  • channels.signal.ignoreStories: Stories vom Daemon ignorieren.
  • channels.signal.sendReadReceipts: Lesebestätigungen weiterleiten.
  • channels.signal.dmPolicy: pairing | allowlist | open | disabled (Standard: pairing).
  • channels.signal.allowFrom: DM-Allowlist (E.164 oder uuid:<id>). open erfordert "*". Signal hat keine Usernames; nutze Telefon-/UUID-IDs.
  • channels.signal.groupPolicy: open | allowlist | disabled (Standard: allowlist).
  • channels.signal.groupAllowFrom: Gruppen-Absender-Allowlist.
  • channels.signal.historyLimit: Max. Gruppennachrichten als Context (0 deaktiviert).
  • channels.signal.dmHistoryLimit: DM-Verlaufslimit in User-Turns. Pro-User-Overrides: channels.signal.dms["<phone_or_uuid>"].historyLimit.
  • channels.signal.textChunkLimit: Ausgehende Chunk-Größe (Zeichen).
  • channels.signal.chunkMode: length (Standard) oder newline, um bei Leerzeilen (Absatzgrenzen) zu splitten, bevor nach Länge gechunkt wird.
  • channels.signal.mediaMaxMb: Eingehende/ausgehende Medien-Cap (MB).

Verwandte globale Optionen:

  • agents.list[].groupChat.mentionPatterns (Signal unterstützt keine nativen Mentions).
  • messages.groupChat.mentionPatterns (globaler Fallback).
  • messages.responsePrefix.