-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Feature: Signal Messenger Gateway Platform via signal-cli-rest-api (inspired by Nightwire) #405
Description
Overview
Nightwire (MIT license) is a Signal-based AI coding bot that demonstrates a clean pattern for bridging Signal Messenger to an AI agent. It uses bbernhard/signal-cli-rest-api — a Docker container that exposes Signal's protocol via REST API and WebSocket.
This issue proposes adding Signal Messenger as a new gateway platform for Hermes Agent, joining the existing Telegram, Discord, WhatsApp, and Slack adapters. Signal is the most secure mainstream messaging platform (end-to-end encrypted by default, open-source protocol, no metadata collection), making it an ideal channel for agent communication — especially for security-sensitive workflows where users don't want their code/conversations flowing through third-party cloud APIs.
Hermes's gateway architecture (gateway/platforms/base.py) already defines a clean abstract base class that Telegram, Discord, WhatsApp, Slack, and Home Assistant implement. Adding Signal follows the same pattern with a new gateway/platforms/signal.py adapter.
Research Findings
How Nightwire Bridges Signal
Nightwire uses the signal-cli-rest-api Docker container as a bridge. This is the established open-source approach to programmatic Signal access (22k+ GitHub stars on the underlying signal-cli).
Architecture:
User's Signal App ←→ Signal Servers ←→ signal-cli-rest-api (Docker) ←→ Hermes Agent
WebSocket (receive)
REST API (send)
Receiving Messages (WebSocket):
- Connect to
ws://<signal_api_url>/v1/receive/<phone_number> - Uses aiohttp
ws_connect()with 30s heartbeat - Reconnects with exponential backoff (5s → 300s max)
- Each WS TEXT frame is a JSON envelope containing:
envelope.source/sourceNumber— sender phone numberenvelope.dataMessage.message— text contentenvelope.dataMessage.attachments— file attachmentsenvelope.syncMessage.sentMessage— messages sent from other linked devices
- Must handle both
dataMessageandsyncMessageto avoid missing self-sent messages - Health watchdog: warns if no WS activity for 10 minutes
Sending Messages (REST API):
- POST to
/v2/sendwith JSON:{number: <bot_phone>, recipients: [<phone>], message: <text>} - Long messages split at paragraph/line boundaries (5000 char max per message)
- Multi-part messages get
[1/N]prefixes - Attachment sending via base64-encoded data in the request body
Setup Requirements:
- Docker running the
bbernhard/signal-cli-rest-api:latestcontainer - A dedicated phone number registered with Signal (or a Signal username)
- Container mode must be
json-rpcfor WebSocket message delivery - Health check: GET
/v1/accountsto verify registration, GET/v1/aboutto verify mode
Key Patterns from Nightwire:
- Message deduplication via SHA-256 hash of (timestamp, text) with 60s TTL — needed because Signal sends both dataMessage and syncMessage for self-sent messages
- Phone number allowlist for authorization (E.164 format + Signal UUID support)
- Loop prevention: ignore messages starting with the bot's own prefix
- Attachment download: GET
/v1/attachments/<id>with ID validation (regex to prevent SSRF) - Startup "ready" notification to all authorized users
Key Design Decisions
- Docker dependency is acceptable — signal-cli-rest-api is the standard bridge. The alternative (raw signal-cli Java process) is much harder to manage.
- WebSocket for receiving — Polling would miss messages; WebSocket is real-time and efficient.
- REST for sending — Simple, reliable, no persistent connection needed.
- Phone number as user ID — Signal uses phone numbers (E.164) as primary identifiers, with UUIDs as an alternative.
- json-rpc mode required — The bridge supports
normalandjson-rpcmodes; onlyjson-rpcenables WebSocket.
Current State in Hermes Agent
Existing gateway platforms (gateway/platforms/):
telegram.py— python-telegram-bot librarydiscord.py— discord.py librarywhatsapp.py— WhatsApp Business API bridgeslack.py— Slack Bolt SDKhomeassistant.py— Home Assistant integrationbase.py— Abstract base class (867 lines) with:connect()/disconnect()lifecyclesend_message()/send_typing_indicator()on_message()callback registration- Image caching utilities for vision tool
- Message splitting for long responses
- Session management integration
No existing Signal support. No open issues requesting it (issue #73 is Matrix, not Signal).
The WhatsApp adapter is the closest analog — it also uses a REST API bridge (WhatsApp Business API) with similar send/receive patterns.
Implementation Plan
Skill vs. Tool Classification
This is a core code change (new gateway platform adapter), not a skill or tool. It follows the exact same pattern as the existing Telegram/Discord/WhatsApp/Slack adapters in gateway/platforms/.
What We'd Need
gateway/platforms/signal.py— New platform adapter implementing the base class- Docker Compose snippet — For running signal-cli-rest-api alongside Hermes
- Configuration — New
Platform.SIGNALenum value, config entries ingateway/config.py - Setup documentation — How to register a Signal number, configure the bridge, and connect
Phased Rollout
Phase 1: Basic Messaging (MVP)
- WebSocket receiver for incoming messages
- REST API sender for outgoing messages
- Message splitting for long responses (Signal has ~6000 char limit but shorter is better)
- Phone number allowlist authorization
- Message deduplication (SHA-256 hash with TTL)
- Session management integration (phone number as session key)
- Config: signal_api_url, phone_number, allowed_numbers
- Deliverables:
gateway/platforms/signal.py+ Docker Compose example
Phase 2: Rich Features
- Image attachment handling (download → cache → vision tool integration)
- Group message support (Signal groups)
- Reaction handling (Signal reactions)
- Read receipts / typing indicators
- Linked device sync message handling
- Voice message support (download → TTS/STT integration)
Phase 3: Security Hardening
- Signal username support (in addition to phone numbers)
- Disappearing messages awareness (respect Signal's disappearing message timer)
- Secure attachment storage with auto-cleanup
- Verified safety number tracking
Pros & Cons
Pros
- End-to-end encryption — Signal is the gold standard for secure messaging. Perfect for users who handle sensitive code, security research, or confidential projects.
- Mobile-first development — Signal is primarily mobile, enabling true "code from your phone" workflows. Hermes + Signal = manage servers, deploy code, review PRs from anywhere.
- Clean bridge pattern — signal-cli-rest-api is mature (5+ years), well-maintained, and the standard solution. WebSocket + REST is a clean architecture.
- Minimal dependencies — Only needs aiohttp (already in Hermes deps) + Docker for the bridge.
- WhatsApp parity — Fills a gap for users who prefer Signal over WhatsApp for privacy reasons. Similar REST bridge architecture.
- Reference implementation — Nightwire's bot.py (1676 lines) provides a battle-tested reference for Signal bridge patterns, message deduplication, attachment handling, etc.
Cons / Risks
- Docker dependency — Requires running a separate Docker container. More infrastructure than Telegram/Discord which are pure-library integrations.
- Phone number requirement — Need a dedicated phone number for the bot. Can't use the same number as the user's personal Signal.
- Bridge reliability — signal-cli-rest-api is a community project. Signal protocol changes could break it (though this has been rare historically).
- No official bot API — Signal doesn't have an official bot platform like Telegram. signal-cli is a third-party client.
- Signal's anti-spam — Signal may rate-limit or ban numbers that send too many messages, especially to non-contacts. Bot behavior needs to be cautious.
Open Questions
- Should we support signal-cli native mode? The Docker bridge is cleanest, but some users might want to run signal-cli directly (Java dependency). Phase 1 should target Docker bridge only.
- Group messaging? Nightwire ignores group messages. Should Hermes support Signal groups (useful for team channels)?
- Phone number vs Signal username? Signal recently added usernames. Should we support both from the start?
- Multi-user sessions? With phone number allowlist, multiple authorized users could talk to the same Hermes instance. How should sessions be scoped?
- Docker Compose vs standalone? Should we provide a full docker-compose.yml that runs both signal-cli-rest-api and Hermes together?
References
- HackingDave/nightwire — Signal-based AI coding bot (MIT)
- bbernhard/signal-cli-rest-api — Signal bridge Docker container
- AsamK/signal-cli — Underlying Signal CLI client (Java)
- Hermes
gateway/platforms/base.py— Abstract platform adapter base class - Hermes
gateway/platforms/whatsapp.py— Most similar existing adapter (REST bridge pattern) - Matrix Protocol Support for Messaging Gateway #73 — Matrix Protocol Support (related: another privacy-focused messaging platform)