Skip to content

Feature: Signal Messenger Gateway Platform via signal-cli-rest-api (inspired by Nightwire) #405

@teknium1

Description

@teknium1

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 number
    • envelope.dataMessage.message — text content
    • envelope.dataMessage.attachments — file attachments
    • envelope.syncMessage.sentMessage — messages sent from other linked devices
  • Must handle both dataMessage and syncMessage to avoid missing self-sent messages
  • Health watchdog: warns if no WS activity for 10 minutes

Sending Messages (REST API):

  • POST to /v2/send with 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:latest container
  • A dedicated phone number registered with Signal (or a Signal username)
  • Container mode must be json-rpc for WebSocket message delivery
  • Health check: GET /v1/accounts to verify registration, GET /v1/about to 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

  1. Docker dependency is acceptable — signal-cli-rest-api is the standard bridge. The alternative (raw signal-cli Java process) is much harder to manage.
  2. WebSocket for receiving — Polling would miss messages; WebSocket is real-time and efficient.
  3. REST for sending — Simple, reliable, no persistent connection needed.
  4. Phone number as user ID — Signal uses phone numbers (E.164) as primary identifiers, with UUIDs as an alternative.
  5. json-rpc mode required — The bridge supports normal and json-rpc modes; only json-rpc enables WebSocket.

Current State in Hermes Agent

Existing gateway platforms (gateway/platforms/):

  • telegram.py — python-telegram-bot library
  • discord.py — discord.py library
  • whatsapp.py — WhatsApp Business API bridge
  • slack.py — Slack Bolt SDK
  • homeassistant.py — Home Assistant integration
  • base.py — Abstract base class (867 lines) with:
    • connect() / disconnect() lifecycle
    • send_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

  1. gateway/platforms/signal.py — New platform adapter implementing the base class
  2. Docker Compose snippet — For running signal-cli-rest-api alongside Hermes
  3. Configuration — New Platform.SIGNAL enum value, config entries in gateway/config.py
  4. 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

  1. 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.
  2. Group messaging? Nightwire ignores group messages. Should Hermes support Signal groups (useful for team channels)?
  3. Phone number vs Signal username? Signal recently added usernames. Should we support both from the start?
  4. Multi-user sessions? With phone number allowlist, multiple authorized users could talk to the same Hermes instance. How should sessions be scoped?
  5. Docker Compose vs standalone? Should we provide a full docker-compose.yml that runs both signal-cli-rest-api and Hermes together?

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions