Skip to main content
The Signal channel connects NullClaw to Signal Messenger using the signal-cli daemon’s HTTP/JSON-RPC API for maximum privacy and security.

Features

  • End-to-end encrypted messaging
  • Group chat support
  • Allowlist-based access control
  • UUID and phone number support
  • Configurable group policies
  • Real-time message delivery via Server-Sent Events (SSE)
  • Typing indicators
  • Attachment handling

Prerequisites

You must have signal-cli installed and running in daemon mode:
See signal-cli documentation for detailed installation and setup instructions.

Configuration

Add Signal to your config.json:

Configuration Options

string
required
Base URL of the signal-cli daemon (e.g., http://127.0.0.1:8080). Trailing slashes are automatically stripped.
string
required
Your Signal account phone number in E.164 format (e.g., +1234567890)
string[]
default:"[]"
List of allowed senders (phone numbers or UUIDs). Empty list = deny all (secure by default).Formats:
  • Phone: "+1234567890"
  • UUID: "uuid:a1b2c3d4-5678-90ab-cdef-1234567890ab"
  • Wildcard: "*" (not recommended)
string[]
default:"[]"
Senders allowed in group chats when group_policy is "allowlist". If empty, falls back to allow_from.
enum
default:"allowlist"
Group message handling:
  • "open" — Respond to all group messages
  • "allowlist" — Only respond to senders in group_allow_from
  • "disabled" — Ignore all group messages
boolean
default:"true"
Skip messages that contain only attachments with no text content
boolean
default:"true"
Ignore Signal story posts
string
default:"default"
Account identifier for multi-account setups

Setup Guide

1

Install signal-cli

macOS (Homebrew):
Linux (Debian/Ubuntu):
From source:
2

Register Your Phone Number

Note: Use a dedicated phone number for your bot, not your personal Signal account.
3

Start signal-cli Daemon

Leave this running in the background or set up as a system service.
4

Get Sender UUIDs or Phone Numbers

From your Signal app, share your contact information with someone who will send messages to the bot. You can use:
  • Phone numbers (e.g., +1234567890)
  • UUIDs from signal-cli logs when messages arrive
5

Configure NullClaw

Add Signal to ~/.nullclaw/config.json:
6

Start NullClaw

Run nullclaw and send a Signal message to the bot’s phone number from an allowed contact. You should receive a response.

Group Chat Support

NullClaw supports Signal group chats with configurable policies:

Allowlist-Based Groups

Only specified users can interact in groups.

Open Groups

Respond to all messages in groups (not recommended for privacy).

Disabled Groups

Ignore all group messages, only respond to 1:1 DMs.

UUID vs Phone Number

Signal supports both phone numbers and UUIDs for identification: Phone numbers (E.164 format):
UUIDs (for privacy mode users):
Mixed:

Message Limits

Signal has no strict character limit, but NullClaw chunks messages at 4096 characters for optimal UX.

Attachment Handling

By default, NullClaw ignores attachment-only messages:
Set to false to process messages with attachments. Attachment content is not passed to the agent (only text).

Running as a System Service

systemd (Linux)

Create /etc/systemd/system/signal-cli.service:
Enable and start:

launchd (macOS)

Create ~/Library/LaunchAgents/com.signal-cli.daemon.plist:
Load:

Troubleshooting

Daemon Not Starting

  1. Verify signal-cli is installed: signal-cli --version
  2. Check that phone number is registered: signal-cli -u +1234567890 listIdentities
  3. Ensure port 8080 is not in use: lsof -i :8080
  4. Check daemon logs for errors

Messages Not Received

  1. Verify daemon is running: curl http://127.0.0.1:8080/api/v1/check
  2. Check sender is in allow_from list
  3. Verify phone number format is E.164 (+ prefix, country code)
  4. For groups, check group_policy and group_allow_from
  5. Review NullClaw logs: nullclaw --log-level debug

Connection Refused

  1. Verify http_url matches daemon address (default: http://127.0.0.1:8080)
  2. Check firewall rules if daemon is on remote host
  3. Ensure daemon is listening on correct interface (not just localhost if remote)

UUID Mismatches

  1. UUIDs change when users reset their Signal account
  2. Check signal-cli logs for current sender UUIDs
  3. Update allow_from with new UUIDs as needed

Source Code

Implementation: src/channels/signal.zig