Files
Selig 4c966a3ad2 Initial commit: OpenClaw Skill Collection
6 custom skills (assign-task, dispatch-webhook, daily-briefing,
task-capture, qmd-brain, tts-voice) with technical documentation.
Compatible with Claude Code, OpenClaw, Codex CLI, and OpenCode.
2026-03-13 10:58:30 +08:00

7.1 KiB

Signal

Signal (signal-cli)

Status: External CLI integration. Gateway talks to signal-cli over HTTP JSON-RPC + SSE.

Quick Setup (Beginner)

  1. Use a separate Signal number for the bot (recommended)
  2. Install signal-cli (Java required)
  3. Link the bot device and start the daemon:
    • signal-cli link -n "OpenClaw"
  4. Configure OpenClaw and start the gateway

Minimal config:

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

What It Is

  • Signal channel via signal-cli (not embedded libsignal)
  • Deterministic routing: replies always go back to Signal
  • DMs share the agent's main session; groups are isolated (agent:<agentId>:signal:group:<groupId>)

Config Writes

By default, Signal is allowed to write config updates triggered by /config set|unset (requires commands.config: true).

Disable with:

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

The Number Model (Important)

  • The gateway connects to a Signal device (the signal-cli account)
  • If you run the bot on your personal Signal account, it will ignore your own messages (loop protection)
  • For "I text the bot and it replies," use a separate bot number

Setup (Fast Path)

  1. Install signal-cli (Java required)
  2. Link a bot account:
    • signal-cli link -n "OpenClaw" then scan the QR in Signal
  3. Configure Signal and start the gateway

Example:

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

Multi-account support: use channels.signal.accounts with per-account config and optional name.

External Daemon Mode (httpUrl)

If you want to manage signal-cli yourself (slow JVM cold starts, container init, or shared CPUs), run the daemon separately and point OpenClaw at it:

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

This skips auto-spawn and the startup wait inside OpenClaw. For slow starts when auto-spawning, set channels.signal.startupTimeoutMs.

Access Control (DMs + Groups)

DMs

  • Default: channels.signal.dmPolicy = "pairing"
  • Unknown senders receive a pairing code; messages are ignored until approved (codes expire after 1 hour)
  • Approve via:
    • openclaw pairing list signal
    • openclaw pairing approve signal <CODE>
  • UUID-only senders (from sourceUuid) are stored as uuid:<id> in channels.signal.allowFrom

Groups

  • channels.signal.groupPolicy = open | allowlist | disabled
  • channels.signal.groupAllowFrom controls who can trigger in groups when allowlist is set

How It Works (Behavior)

  • signal-cli runs as a daemon; the gateway reads events via SSE
  • Inbound messages are normalized into the shared channel envelope
  • Replies always route back to the same number or group

Media + Limits

  • Outbound text is chunked to channels.signal.textChunkLimit (default 4000)
  • Optional newline chunking: set channels.signal.chunkMode="newline" to split on blank lines (paragraph boundaries) before length chunking
  • Attachments supported (base64 fetched from signal-cli)
  • Default media cap: channels.signal.mediaMaxMb (default 8)
  • Use channels.signal.ignoreAttachments to skip downloading media
  • Group history context uses channels.signal.historyLimit (or channels.signal.accounts.*.historyLimit), falling back to messages.groupChat.historyLimit. Set 0 to disable (default 50)

Typing + Read Receipts

  • Typing indicators: OpenClaw sends typing signals via signal-cli sendTyping and refreshes them while a reply is running
  • Read receipts: when channels.signal.sendReadReceipts is true, OpenClaw forwards read receipts for allowed DMs
  • Signal-cli does not expose read receipts for groups

Reactions (Message Tool)

Use message action=react with channel=signal.

  • Targets: sender E.164 or UUID (use uuid:<id> from pairing output; bare UUID works too)
  • messageId is the Signal timestamp for the message you're reacting to
  • Group reactions require targetAuthor or targetAuthorUuid

Examples:

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=✅

Config:

  • channels.signal.actions.reactions: enable/disable reaction actions (default true)
  • channels.signal.reactionLevel: off | ack | minimal | extensive
    • off/ack disables agent reactions (message tool react will error)
    • minimal/extensive enables agent reactions and sets the guidance level
  • Per-account overrides: channels.signal.accounts.<id>.actions.reactions, channels.signal.accounts.<id>.reactionLevel

Delivery Targets (CLI/cron)

  • DMs: signal:+15551234567 (or plain E.164)
  • UUID DMs: uuid:<id> (or bare UUID)
  • Groups: signal:group:<groupId>
  • Usernames: username:<name> (if supported by your Signal account)

Configuration Reference (Signal)

Provider Options

  • channels.signal.enabled: enable/disable channel startup
  • channels.signal.account: E.164 for the bot account
  • channels.signal.cliPath: path to signal-cli
  • channels.signal.httpUrl: full daemon URL (overrides host/port)
  • channels.signal.httpHost, channels.signal.httpPort: daemon bind (default 127.0.0.1:8080)
  • channels.signal.autoStart: auto-spawn daemon (default true if httpUrl unset)
  • channels.signal.startupTimeoutMs: startup wait timeout in ms (cap 120000)
  • channels.signal.receiveMode: on-start | manual
  • channels.signal.ignoreAttachments: skip attachment downloads
  • channels.signal.ignoreStories: ignore stories from the daemon
  • channels.signal.sendReadReceipts: forward read receipts
  • channels.signal.dmPolicy: pairing | allowlist | open | disabled (default: pairing)
  • channels.signal.allowFrom: DM allowlist (E.164 or uuid:<id>). open requires "*". Signal has no usernames; use phone/UUID ids
  • channels.signal.groupPolicy: open | allowlist | disabled (default: allowlist)
  • channels.signal.groupAllowFrom: group sender allowlist
  • channels.signal.historyLimit: max group messages to include as context (0 disables)
  • channels.signal.dmHistoryLimit: DM history limit in user turns. Per-user overrides: channels.signal.dms["<phone_or_uuid>"].historyLimit
  • channels.signal.textChunkLimit: outbound chunk size (chars)
  • channels.signal.chunkMode: length (default) or newline to split on blank lines (paragraph boundaries) before length chunking
  • channels.signal.mediaMaxMb: inbound/outbound media cap (MB)
  • agents.list[].groupChat.mentionPatterns (Signal does not support native mentions)
  • messages.groupChat.mentionPatterns (global fallback)
  • messages.responsePrefix