website/docs/user-guide/messaging/signal.md
Hermes connects to Signal through the signal-cli daemon running in HTTP mode. The adapter streams messages in real-time via SSE (Server-Sent Events) and sends responses via JSON-RPC.
Signal is the most privacy-focused mainstream messenger — end-to-end encrypted by default, open-source protocol, minimal metadata collection. This makes it ideal for security-sensitive agent workflows.
:::info No New Python Dependencies
The Signal adapter uses httpx (already a core Hermes dependency) for all communication. No additional Python packages are required. You just need signal-cli installed externally.
:::
# macOS
brew install signal-cli
# Linux (download latest release)
VERSION=$(curl -Ls -o /dev/null -w %{url_effective} \
https://github.com/AsamK/signal-cli/releases/latest | sed 's/^.*\/v//')
curl -L -O "https://github.com/AsamK/signal-cli/releases/download/v${VERSION}/signal-cli-${VERSION}.tar.gz"
sudo tar xf "signal-cli-${VERSION}.tar.gz" -C /opt
sudo ln -sf "/opt/signal-cli-${VERSION}/bin/signal-cli" /usr/local/bin/
:::caution signal-cli is not in apt or snap repositories. The Linux install above downloads directly from GitHub releases. :::
Signal-cli works as a linked device — like WhatsApp Web, but for Signal. Your phone stays the primary device.
# Generate a linking URI (displays a QR code or link)
signal-cli link -n "HermesAgent"
# Replace +1234567890 with your Signal phone number (E.164 format)
signal-cli --account +1234567890 daemon --http 127.0.0.1:8080
:::tip
Keep this running in the background. You can use systemd, tmux, screen, or run it as a service.
:::
Verify it's running:
curl http://127.0.0.1:8080/api/v1/check
# Should return: {"versions":{"signal-cli":...}}
The easiest way:
hermes gateway setup
Select Signal from the platform menu. The wizard will:
http://127.0.0.1:8080)Add to ~/.hermes/.env:
# Required
SIGNAL_HTTP_URL=http://127.0.0.1:8080
SIGNAL_ACCOUNT=+1234567890
# Security (recommended)
SIGNAL_ALLOWED_USERS=+1234567890,+0987654321 # Comma-separated E.164 numbers or UUIDs
# Optional
SIGNAL_GROUP_ALLOWED_USERS=groupId1,groupId2 # Enable groups (omit to disable, * for all)
SIGNAL_HOME_CHANNEL=+1234567890 # Default delivery target for cron jobs
Then start the gateway:
hermes gateway # Foreground
hermes gateway install # Install as a user service
sudo hermes gateway install --system # Linux only: boot-time system service
DM access follows the same pattern as all other Hermes platforms:
SIGNAL_ALLOWED_USERS set → only those users can messagehermes pairing approve signal CODE)SIGNAL_ALLOW_ALL_USERS=true → anyone can message (use with caution)Group access is controlled by the SIGNAL_GROUP_ALLOWED_USERS env var:
| Configuration | Behavior |
|---|---|
| Not set (default) | All group messages are ignored. The bot only responds to DMs. |
| Set with group IDs | Only listed groups are monitored (e.g., groupId1,groupId2). |
Set to * | The bot responds in any group it's a member of. |
The adapter supports sending and receiving media in both directions.
Incoming (user → agent):
Outgoing (agent → user):
The agent can send media files via MEDIA: tags in responses. The following delivery methods are supported:
send_multiple_images and send_image_file send PNG, JPEG, GIF, WebP as native Signal attachmentssend_voice sends audio files (OGG, MP3, WAV, M4A, AAC) as attachmentssend_video sends MP4 video filessend_document sends any file type (PDF, ZIP, etc.)All outgoing media goes through Signal's standard attachment API. Unlike some platforms, Signal does not distinguish between voice messages and file attachments at the protocol level.
Attachment size limit: 100 MB (both directions). :::warning Signal servers will rate-limit attachment uploads, the adapter uses a scheduler for multiple image sending that batches images in groups of 32 and throttles uploads to match the Signal server policy. :::
Signal messages render with native formatting instead of literal markdown characters. The adapter converts markdown (**bold**, *italic*, `code`, ~~strike~~, ||spoiler||, headings) into Signal bodyRanges so the text shows up with real styling on the recipient's client rather than as visible ** / ` characters.
Reply quotes. When Hermes replies to a specific message, it now posts a native reply that quotes the original — same UI affordance Signal users see when they use "Reply" themselves. This is automatic for replies generated in response to an inbound message.
Reactions. The agent can react to messages via the standard reaction API; reactions surface in Signal as emoji reactions on the referenced message rather than as extra text.
None of this requires additional config — it ships on by default in recent signal-cli builds. If your signal-cli version is too old, Hermes falls back to plaintext delivery and logs a one-time warning.
The bot sends typing indicators while processing messages, refreshing every 8 seconds.
Signal does not support editing already-sent messages. Hermes therefore suppresses gateway tool-progress bubbles on Signal, even when /verbose is enabled and saves a non-off mode for the platform.
You can still see tool activity in the CLI, and final Signal replies can include normal assistant output. If you need live per-tool progress in chat, use a messaging platform with message editing support.
All phone numbers are automatically redacted in logs:
+15551234567 → +155****4567If you run signal-cli as a linked secondary device on your own phone number (rather than a separate bot number), you can interact with Hermes through Signal's "Note to Self" feature.
Just send a message to yourself from your phone — signal-cli picks it up and Hermes responds in the same conversation.
How it works:
syncMessage.sentMessage envelopesNo extra configuration needed. This works automatically as long as SIGNAL_ACCOUNT matches your phone number.
The adapter monitors the SSE connection and automatically reconnects if:
| Problem | Solution |
|---|---|
| "Cannot reach signal-cli" during setup | Ensure signal-cli daemon is running: signal-cli --account +YOUR_NUMBER daemon --http 127.0.0.1:8080 |
| Messages not received | Check that SIGNAL_ALLOWED_USERS includes the sender's number in E.164 format (with + prefix) |
| "signal-cli not found on PATH" | Install signal-cli and ensure it's in your PATH, or use Docker |
| Connection keeps dropping | Check signal-cli logs for errors. Ensure Java 17+ is installed. |
| Group messages ignored | Configure SIGNAL_GROUP_ALLOWED_USERS with specific group IDs, or * to allow all groups. |
| Bot responds to no one | Configure SIGNAL_ALLOWED_USERS, use DM pairing, or explicitly allow all users through gateway policy if you want broader access. |
| Duplicate messages | Ensure only one signal-cli instance is listening on your phone number |
:::warning
Always configure access controls. The bot has terminal access by default. Without SIGNAL_ALLOWED_USERS or DM pairing, the gateway denies all incoming messages as a safety measure.
:::
~/.local/share/signal-cli/ contains account credentials — protect it like a password| Variable | Required | Default | Description |
|---|---|---|---|
SIGNAL_HTTP_URL | Yes | — | signal-cli HTTP endpoint |
SIGNAL_ACCOUNT | Yes | — | Bot phone number (E.164) |
SIGNAL_ALLOWED_USERS | No | — | Comma-separated phone numbers/UUIDs |
SIGNAL_GROUP_ALLOWED_USERS | No | — | Group IDs to monitor, or * for all (omit to disable groups) |
SIGNAL_ALLOW_ALL_USERS | No | false | Allow any user to interact (skip allowlist) |
SIGNAL_HOME_CHANNEL | No | — | Default delivery target for cron jobs |