Signal
Same tools, same access model. No bot API, so the plumbing is different.
Signal runs as a provider next to Telegram and Discord, or on its own. There is no bot API: hotline talks to a locally running signal-cli daemon, linked to your Signal account as a secondary device.
signal-cli is a third-party client, not an official Signal product. This is the part an official hosted product structurally can't ship, and the reason hotline covers Signal at all.
Setup
-
Install signal-cli. On macOS:
brew install signal-cli. On Linux, download a release from github.com/AsamK/signal-cli/releases (it is not in apt) and putsignal-clion your PATH. Java 17+ required. -
Link it to your account as a secondary device:
signal-cli link -n hotlineIt prints a
sgnl://linkdevice?...URI. Render it as a QR code (qrencode -t ansiutf8 'sgnl://...') and scan it from your phone under Settings → Linked Devices. Registration stays on your phone; hotline never touches it. -
Run the daemon and keep it running (tmux, or a systemd user service with
ExecStart=signal-cli -a +15551234567 daemon --http 127.0.0.1:8080):signal-cli -a +15551234567 daemon --http 127.0.0.1:8080 -
Point hotline at it in the shared
.env:# ~/.claude/channels/tele-go/.env SIGNAL_ACCOUNT=+15551234567 # the linked account, E.164 SIGNAL_DAEMON_URL=http://127.0.0.1:8080 # optional, this is the default SIGNAL_ACCOUNT_WORK=… # signal:work, if you run named instances -
Enable the provider and run:
HOTLINE_PROVIDERS=telegram,discord,signal hotlineIn a Claude Code setup,
HOTLINE_PROVIDERSbelongs in theenvblock of your.mcp.json; see Multiple providers. -
Message the account from another Signal account. Unknown senders get a pairing code; approve it from your terminal:
hotline pair <code> --provider signal
--provider signal points pair/deny/status at the Signal state (<stateDir>/signal/); named instances use --provider signal:work with state under <stateDir>/signal/instances/work/.
Behavior notes
- Senders are identified by phone number (E.164); the allowlist holds numbers. DM chat_ids are the peer's number, group chat_ids are
group:<id>. Add those togroupsin the Signalaccess.json. - Signal has no inline buttons. Buttons render as numbered text options, and replying with the number sends the chosen label back to Claude. Same round trip, typed instead of tapped.
- The permission relay is text-only: prompts arrive as a message, answer with
yes <code>orno <code>. - Reactions and edits are native (signal-cli
sendReactionandsend --edit-timestamp). Bubbles are paced with Signal's typing indicator. Messages split at 2000 chars, where Signal clients switch to long-text attachments. - Message ids are Signal timestamps (Signal's message identity); inbound ids carry the author as
<timestamp>:<number>so reactions target correctly. - Inbound images are fetched from the daemon into the inbox; other attachments surface an id for
download_attachment(50MB cap both ways). - The daemon's HTTP endpoint has no authentication. Keep it on 127.0.0.1.