> ## 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.

# Signal Channel

> Setup and usage guide for the Signal messenger channel

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):**

```bash theme={null}
brew install signal-cli
```

**Linux (Manual):**

```bash theme={null}
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):**

```bash theme={null}
# 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:**

```bash theme={null}
# Request verification code
signal-cli -a +1234567890 register

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

### 3. Start signal-cli Daemon

```bash theme={null}
# Start HTTP/JSON-RPC server
signal-cli --account +1234567890 daemon --http 127.0.0.1:8080
```

Or use systemd:

```ini theme={null}
# /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
```

```bash theme={null}
sudo systemctl enable signal-cli
sudo systemctl start signal-cli
```

### 4. Configure IronClaw

**Environment variables:**

```bash theme={null}
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:**

```bash theme={null}
ironclaw onboard
```

## Configuration

Set via environment variables or `.env` file:

```bash theme={null}
# 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

| Variable | Type | Default | Description |
| - | - | - | - |
| `SIGNAL_HTTP_URL` | `string` | `http://127.0.0.1:8080` | signal-cli daemon HTTP endpoint |
| `SIGNAL_ACCOUNT` | `string` | *required* | Bot's phone number or UUID |
| `SIGNAL_ALLOW_FROM` | `string` | `""` | Comma-separated phone numbers/UUIDs. `*` allows all. Empty = deny/require pairing |
| `SIGNAL_ALLOW_FROM_GROUPS` | `string` | `""` | Comma-separated group IDs. `*` allows all groups. Empty = deny all groups |
| `SIGNAL_DM_POLICY` | `string` | `"pairing"` | `open`, `allowlist`, or `pairing` |
| `SIGNAL_GROUP_POLICY` | `string` | `"allowlist"` | `open`, `allowlist`, or `disabled` |
| `SIGNAL_GROUP_ALLOW_FROM` | `string` | `""` | Sender allowlist for group messages. Empty = inherit from `ALLOW_FROM` |
| `SIGNAL_IGNORE_ATTACHMENTS` | `boolean` | `false` | Drop attachment-only messages |
| `SIGNAL_IGNORE_STORIES` | `boolean` | `true` | Drop story messages |

## DM Policies

### `open`

Allow all DMs from anyone.

```bash theme={null}
SIGNAL_DM_POLICY=open
```

### `allowlist`

Only accept DMs from pre-approved senders. Silent drop for others.

```bash theme={null}
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`
```

```bash theme={null}
SIGNAL_DM_POLICY=pairing
SIGNAL_ALLOW_FROM=+1234567890
```

## Group Policies

### `disabled`

Ignore all group messages.

```bash theme={null}
SIGNAL_GROUP_POLICY=disabled
```

### `allowlist` (default)

Only process messages from allowed groups AND allowed senders.

```bash theme={null}
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).

```bash theme={null}
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

```bash theme={null}
# 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:**

```bash theme={null}
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:

```rust theme={null}
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)

```bash theme={null}
GET /api/v1/events?account=+1234567890
```

Returns Server-Sent Events stream:

```
data: {"envelope":{"source":"+1234567890","dataMessage":{"message":"hello"}}}
```

### Send Message

```bash theme={null}
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

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

## Troubleshooting

### signal-cli daemon not running

```bash theme={null}
# 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
     ...
```
