Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/nearai/ironclaw/llms.txt

Use this file to discover all available pages before exploring further.

The Signal channel enables IronClaw to send and receive messages via Signal Private Messenger using the signal-cli daemon.

Features

  • Direct messages - Private 1:1 conversations
  • Group chats - Multi-party conversations (with allowlist)
  • DM pairing - Approve unknown users before they can message
  • Typing indicators - Shows when agent is thinking
  • Attachment support - Send files within ~/.ironclaw/ sandbox
  • Thread continuity - Conversation history persists across restarts
  • Privacy mode - Works with users who hide their phone numbers (UUID-based)

Prerequisites

  • signal-cli daemon with HTTP/JSON-RPC API mode
  • A registered Signal account for the bot
  • IronClaw installed and configured

Setup

1. Install signal-cli

macOS (Homebrew):
brew install signal-cli
Linux (Manual):
wget https://github.com/AsamK/signal-cli/releases/download/v0.12.8/signal-cli-0.12.8-Linux.tar.gz
tar xf signal-cli-0.12.8-Linux.tar.gz -C /opt
ln -sf /opt/signal-cli-0.12.8/bin/signal-cli /usr/local/bin/

2. Register Signal Account

Link to existing account (recommended):
# Generate QR code to link
signal-cli link -n "IronClaw Bot"

# Scan QR code with Signal app:
# Settings → Linked Devices → Link New Device
Or register new number:
# Request verification code
signal-cli -a +1234567890 register

# Verify with code from SMS
signal-cli -a +1234567890 verify CODE

3. Start signal-cli Daemon

# Start HTTP/JSON-RPC server
signal-cli --account +1234567890 daemon --http 127.0.0.1:8080
Or use systemd:
# /etc/systemd/system/signal-cli.service
[Unit]
Description=signal-cli daemon
After=network.target

[Service]
Type=simple
ExecStart=/usr/local/bin/signal-cli --account +1234567890 daemon --http 127.0.0.1:8080
Restart=always
User=signal

[Install]
WantedBy=multi-user.target
sudo systemctl enable signal-cli
sudo systemctl start signal-cli

4. Configure IronClaw

Environment variables:
export SIGNAL_HTTP_URL=http://127.0.0.1:8080
export SIGNAL_ACCOUNT=+1234567890
export SIGNAL_ALLOW_FROM=+1234567890  # Your personal number
Or via config file:
ironclaw onboard

Configuration

Set via environment variables or .env file:
# signal-cli daemon URL
SIGNAL_HTTP_URL=http://127.0.0.1:8080

# Bot's Signal account (phone number or UUID)
SIGNAL_ACCOUNT=+1234567890

# Allowed senders for DMs (comma-separated)
SIGNAL_ALLOW_FROM=+1234567890,uuid:abc-123,*

# Allowed groups (comma-separated group IDs)
SIGNAL_ALLOW_FROM_GROUPS=

# DM policy: open | allowlist | pairing
SIGNAL_DM_POLICY=pairing

# Group policy: open | allowlist | disabled
SIGNAL_GROUP_POLICY=allowlist

# Inherit DM allow_from for groups (if group_allow_from is empty)
SIGNAL_GROUP_ALLOW_FROM=

# Ignore attachment-only messages
SIGNAL_IGNORE_ATTACHMENTS=false

# Ignore story messages
SIGNAL_IGNORE_STORIES=true

Configuration Options

VariableTypeDefaultDescription
SIGNAL_HTTP_URLstringhttp://127.0.0.1:8080signal-cli daemon HTTP endpoint
SIGNAL_ACCOUNTstringrequiredBot’s phone number or UUID
SIGNAL_ALLOW_FROMstring""Comma-separated phone numbers/UUIDs. * allows all. Empty = deny/require pairing
SIGNAL_ALLOW_FROM_GROUPSstring""Comma-separated group IDs. * allows all groups. Empty = deny all groups
SIGNAL_DM_POLICYstring"pairing"open, allowlist, or pairing
SIGNAL_GROUP_POLICYstring"allowlist"open, allowlist, or disabled
SIGNAL_GROUP_ALLOW_FROMstring""Sender allowlist for group messages. Empty = inherit from ALLOW_FROM
SIGNAL_IGNORE_ATTACHMENTSbooleanfalseDrop attachment-only messages
SIGNAL_IGNORE_STORIESbooleantrueDrop story messages

DM Policies

open

Allow all DMs from anyone.
SIGNAL_DM_POLICY=open

allowlist

Only accept DMs from pre-approved senders. Silent drop for others.
SIGNAL_DM_POLICY=allowlist
SIGNAL_ALLOW_FROM=+1234567890,uuid:abc-123

pairing (default)

Combines allowlist with interactive pairing. Unknown users receive a pairing code:
To pair with this bot, run: `ironclaw pairing approve signal ABC12345`
SIGNAL_DM_POLICY=pairing
SIGNAL_ALLOW_FROM=+1234567890

Group Policies

disabled

Ignore all group messages.
SIGNAL_GROUP_POLICY=disabled

allowlist (default)

Only process messages from allowed groups AND allowed senders.
SIGNAL_GROUP_POLICY=allowlist
SIGNAL_ALLOW_FROM_GROUPS=group:abc123,group:def456
SIGNAL_GROUP_ALLOW_FROM=+1234567890  # Only this sender in allowed groups

open

Process messages from allowed groups (any sender).
SIGNAL_GROUP_POLICY=open
SIGNAL_ALLOW_FROM_GROUPS=group:abc123

Pairing

DM Pairing

When an unknown user sends a DM with dm_policy: "pairing":
  1. User sends a message
  2. Bot replies: To pair with this bot, run: \ironclaw pairing approve signal ABC12345“
  3. You run: ironclaw pairing approve signal ABC12345
  4. User is added to the persistent allow list

Commands

# List pending pairing requests
ironclaw pairing list signal

# Approve a user by code
ironclaw pairing approve signal ABC12345

# List approved users
ironclaw pairing list signal --approved

Privacy Mode (UUID-based)

Signal users can hide their phone numbers. For these users:
  • sourceNumber is empty
  • source contains a UUID (e.g., abc-123-def-456)
  • Use uuid:abc-123-def-456 in allowlists
Example:
SIGNAL_ALLOW_FROM=+1234567890,uuid:abc-123-def-456
The channel automatically normalizes uuid: prefixes when matching.

Attachments

The channel supports sending attachments from the ~/.ironclaw/ sandbox:
OutgoingResponse {
    content: "Here's the report".to_string(),
    attachments: vec!["/home/user/.ironclaw/files/report.pdf".to_string()],
    ..Default::default()
}
Paths outside ~/.ironclaw/ are rejected to prevent path traversal attacks.

Thread Continuity

The channel generates deterministic UUIDs for thread IDs:
  • DMs with privacy users - Use Signal’s source_uuid
  • DMs with regular users - Generate UUID from phone number
  • Groups - Generate UUID from group ID
This ensures conversation history persists across restarts and works with the agent’s maybe_hydrate_thread feature.

Status Updates

The channel sends status updates to Signal:

Typing Indicators

When the agent is thinking, a typing indicator is sent via sendTyping RPC.

Status Messages

  • ApprovalNeeded - Sends approval prompt with request ID
  • JobStarted - Notifies about sandbox jobs
  • AuthRequired - Extension authentication prompts
  • Tool execution - Shown in debug mode (toggle with /debug)

Debug Mode

Toggle verbose tool output with the /debug command:
You: /debug
Bot: Debug mode enabled. Tool execution will be shown in chat.

[Agent executes a tool]
Bot: ◯ Running tool: FileRead
     ● Tool 'FileRead' completed (success)
     Tool 'FileRead' result:
     File contents: ...

signal-cli JSON-RPC API

The channel uses signal-cli’s HTTP/JSON-RPC API:

Receive Messages (SSE)

GET /api/v1/events?account=+1234567890
Returns Server-Sent Events stream:
data: {"envelope":{"source":"+1234567890","dataMessage":{"message":"hello"}}}

Send Message

POST /api/v1/rpc
Content-Type: application/json

{
  "jsonrpc": "2.0",
  "method": "send",
  "params": {
    "recipient": ["+1234567890"],
    "message": "Hello!",
    "account": "+1234567890"
  },
  "id": "uuid"
}

Send to Group

{
  "jsonrpc": "2.0",
  "method": "send",
  "params": {
    "groupId": "abc123",
    "message": "Hello group!",
    "account": "+1234567890"
  },
  "id": "uuid"
}

Troubleshooting

signal-cli daemon not running

# Check if daemon is running
curl http://127.0.0.1:8080/api/v1/check

# Should return 200 OK

Messages not received

  1. Check SSE connection - Logs should show “Signal SSE connected”
  2. Verify account - Ensure SIGNAL_ACCOUNT matches the daemon’s account
  3. Check allowlist - Verify sender is in SIGNAL_ALLOW_FROM
  4. Check policy - Ensure SIGNAL_DM_POLICY isn’t blocking messages

Pairing not working

  1. Check policy - Ensure SIGNAL_DM_POLICY=pairing
  2. HTTP allowlist - Verify the channel can send messages
  3. Logs - Look for “Pairing request upserted” or “Failed to send pairing reply”

Group messages ignored

  1. Check group policy - Ensure SIGNAL_GROUP_POLICY isn’t disabled
  2. Group ID - Verify group is in SIGNAL_ALLOW_FROM_GROUPS
  3. Sender allowlist - Check sender is in SIGNAL_GROUP_ALLOW_FROM (or SIGNAL_ALLOW_FROM if empty)

Attachments rejected

  • Ensure attachment paths are absolute
  • Verify paths are within ~/.ironclaw/ sandbox
  • Check file exists and is readable

High memory usage

The channel uses an LRU cache (10,000 entries) for reply targets. If you have millions of messages, consider restarting periodically.

Source Code

  • Implementation: ~/workspace/source/src/channels/signal.rs
  • HTTP helpers: ~/workspace/source/src/channels/http.rs

Example Usage

Simple DM

You: Hello
Bot: Hi! I'm your IronClaw agent. How can I help?

You: What's in my workspace?
Bot: [reads workspace]
     You have 3 files:
     - identity.md
     - daily/2026-03-03.md
     - memory/context.md

Group Chat

Alice: Hey @IronClaw, summarize this conversation
Bot: [reads group history]
     Here's a summary of the last 20 messages...

Bob: What were the action items?
Bot: Main action items:
     1. Alice: Update docs
     2. Bob: Fix CI pipeline
     3. Carol: Review PR #123

Debug Mode

You: /debug
Bot: Debug mode enabled. Tool execution will be shown in chat.

You: Read the README
Bot: ◯ Running tool: FileRead
     [reading /home/user/project/README.md]
     ● Tool 'FileRead' completed (success)
     Tool 'FileRead' result:
     # Project Title
     ...

Build docs developers (and LLMs) love