Signal (signal-cli)
Status: external CLI integration. Gateway는signal-cli와 HTTP JSON-RPC + SSE로 통신합니다.
Prerequisites
- 서버에 OpenClaw가 설치되어 있어야 합니다(아래 Linux 흐름은 Ubuntu 24에서 테스트됨).
- gateway가 실행되는 host에서
signal-cli를 사용할 수 있어야 합니다. - SMS 등록 경로를 사용할 경우, verification SMS를 한 번 받을 수 있는 전화번호가 필요합니다.
- 등록 중 Signal captcha(
signalcaptchas.org)를 처리할 수 있도록 브라우저 접근이 필요합니다.
Quick setup (beginner)
- bot에는 별도의 Signal 번호를 사용하는 것을 권장합니다.
signal-cli를 설치합니다(JVM build를 쓴다면 Java 필요).- 설정 경로 하나를 선택합니다.
- Path A (QR link):
signal-cli link -n "OpenClaw"를 실행하고 Signal로 스캔합니다. - Path B (SMS register): captcha + SMS verification으로 전용 번호를 등록합니다.
- Path A (QR link):
- OpenClaw를 설정하고 gateway를 재시작합니다.
- 첫 DM을 보낸 뒤 pairing을 승인합니다(
openclaw pairing approve signal <CODE>).
| Field | Description |
|---|---|
account | Bot phone number in E.164 format (+15551234567) |
cliPath | Path to signal-cli (signal-cli if on PATH) |
dmPolicy | DM access policy (pairing recommended) |
allowFrom | Phone numbers or uuid:<id> values allowed to DM |
What it is
signal-cli를 통한 Signal 채널입니다(embedded libsignal 아님).- routing은 deterministic합니다. 응답은 항상 Signal로 다시 돌아갑니다.
- DMs는 agent의 main session을 공유하고, groups는 격리됩니다(
agent:<agentId>:signal:group:<groupId>).
Config writes
기본적으로 Signal은/config set|unset으로 트리거된 config update 쓰기를 허용합니다(commands.config: true 필요).
다음으로 비활성화할 수 있습니다.
The number model (important)
- gateway는 Signal device(
signal-cliaccount)에 연결됩니다. - bot을 개인 Signal account에서 실행하면, 자신의 메시지는 loop protection 때문에 무시됩니다.
- “내가 bot에 문자 보내고 bot이 답하게” 하려면 별도의 bot 번호를 사용하세요.
Setup path A: link existing Signal account (QR)
signal-cli를 설치합니다(JVM 또는 native build).- bot account를 link합니다.
signal-cli link -n "OpenClaw"를 실행한 뒤 Signal에서 QR을 스캔합니다.
- Signal을 설정하고 gateway를 시작합니다.
channels.signal.accounts에 account별 config와 optional name을 둡니다. 공통 패턴은 gateway/configuration을 참고하세요.
Setup path B: register dedicated bot number (SMS, Linux)
기존 Signal app account를 link하지 않고 전용 bot 번호를 쓰고 싶을 때 사용하는 방식입니다.- SMS를 받을 수 있는 번호를 준비합니다(유선전화는 voice verification도 가능).
- account/session 충돌을 피하려면 전용 bot 번호를 사용하세요.
- gateway host에
signal-cli를 설치합니다.
signal-cli-${VERSION}.tar.gz)를 쓴다면 먼저 JRE 25+를 설치하세요.
Signal server API가 바뀌면 오래된 release가 깨질 수 있으므로 signal-cli를 계속 업데이트해야 합니다.
- 번호를 등록하고 검증합니다.
https://signalcaptchas.org/registration/generate.html을 엽니다.- captcha를 완료한 뒤, “Open Signal”의
signalcaptcha://...link target을 복사합니다. - 가능하면 브라우저 세션과 같은 external IP에서 실행합니다.
- captcha token은 빨리 만료되므로, 바로 다시 registration을 실행합니다.
- OpenClaw를 설정하고 gateway를 재시작한 뒤 채널을 확인합니다.
- DM sender를 pairing합니다.
- bot 번호로 아무 메시지나 보냅니다.
- 서버에서 code를 승인합니다:
openclaw pairing approve signal <PAIRING_CODE>. - 휴대폰에서 “Unknown contact”를 피하려면 bot 번호를 연락처로 저장합니다.
signal-cli로 전화번호 account를 등록하면, 그 번호의 기존 Signal app session이 인증 해제될 수 있습니다. 기존 휴대폰 app 구성을 유지해야 한다면 전용 bot 번호를 쓰거나 QR link mode를 선택하세요.
Upstream references:
signal-cliREADME:https://github.com/AsamK/signal-cli- Captcha flow:
https://github.com/AsamK/signal-cli/wiki/Registration-with-captcha - Linking flow:
https://github.com/AsamK/signal-cli/wiki/Linking-other-devices-(Provisioning)
External daemon mode (httpUrl)
signal-cli를 직접 관리하고 싶다면(slow JVM cold starts, container init, shared CPUs 등), daemon을 따로 실행하고 OpenClaw가 그쪽을 보게 할 수 있습니다.
channels.signal.startupTimeoutMs를 설정하세요.
Access control (DMs + groups)
DMs:- 기본값:
channels.signal.dmPolicy = "pairing". - 알 수 없는 sender는 pairing code를 받고, 승인될 때까지 메시지는 무시됩니다(code는 1시간 후 만료).
- 승인 명령:
openclaw pairing list signalopenclaw pairing approve signal <CODE>
- pairing은 Signal DMs의 기본 token exchange 방식입니다. 자세한 내용: Pairing
sourceUuid만 있는 sender는channels.signal.allowFrom에uuid:<id>형태로 저장됩니다.
channels.signal.groupPolicy = open | allowlist | disabled.channels.signal.groupAllowFrom은allowlist일 때 누가 그룹에서 트리거할 수 있는지 제어합니다.- runtime 참고:
channels.signal블록 전체가 없으면,channels.defaults.groupPolicy가 있어도 group check는groupPolicy="allowlist"로 fallback합니다.
How it works (behavior)
signal-cli는 daemon으로 실행되고, gateway는 SSE로 이벤트를 읽습니다.- inbound message는 공통 channel envelope로 정규화됩니다.
- 응답은 항상 같은 번호 또는 같은 그룹으로 돌아갑니다.
Media + limits
- outbound text는
channels.signal.textChunkLimit(기본값 4000) 기준으로 chunking됩니다. - optional newline chunking:
channels.signal.chunkMode="newline"으로 설정하면 길이 기준 chunking 전에 빈 줄 단위(문단 경계)로 나눕니다. - attachment를 지원합니다(
signal-cli에서 base64로 가져옴). - 기본 media cap:
channels.signal.mediaMaxMb(기본값 8). - media 다운로드를 건너뛰려면
channels.signal.ignoreAttachments를 사용합니다. - group history context는
channels.signal.historyLimit(또는channels.signal.accounts.*.historyLimit)을 사용하고, 없으면messages.groupChat.historyLimit으로 fallback합니다. 비활성화하려면0으로 설정합니다(기본값 50).
Typing + read receipts
- Typing indicators: OpenClaw는
signal-cli sendTyping으로 typing signal을 보내고, 응답 생성 중에는 이를 갱신합니다. - Read receipts:
channels.signal.sendReadReceipts가 true이면 허용된 DM에 대해 read receipt를 전달합니다. - Signal-cli는 group의 read receipt는 제공하지 않습니다.
Reactions (message tool)
channel=signal로message action=react를 사용합니다.- 대상은 sender의 E.164 또는 UUID입니다(pairing output의
uuid:<id>사용, bare UUID도 가능). messageId는 reaction 대상 메시지의 Signal timestamp입니다.- group reaction에는
targetAuthor또는targetAuthorUuid가 필요합니다.
channels.signal.actions.reactions: reaction action enable/disable(기본값 true).channels.signal.reactionLevel:off | ack | minimal | extensive.off/ack는 agent reaction을 비활성화합니다(message tool react는 오류 반환).minimal/extensive는 agent reaction을 활성화하고 guidance level을 설정합니다.
- account별 override:
channels.signal.accounts.<id>.actions.reactions,channels.signal.accounts.<id>.reactionLevel.
Delivery targets (CLI/cron)
- DMs:
signal:+15551234567(또는 plain E.164). - UUID DMs:
uuid:<id>(또는 bare UUID). - Groups:
signal:group:<groupId>. - Usernames:
username:<name>(계정이 지원하는 경우).
Troubleshooting
먼저 이 순서대로 확인하세요.- daemon에는 연결되지만 reply가 없음:
httpUrl,account, receive mode 등 account/daemon 설정을 확인하세요. - DMs가 무시됨: sender가 pairing 승인 대기 상태입니다.
- group message가 무시됨: group sender 제한 또는 mention gating 때문에 전달이 막힙니다.
- 편집 후 config validation error가 남음:
openclaw doctor --fix를 실행하세요. - diagnostics에 Signal이 보이지 않음:
channels.signal.enabled: true인지 확인하세요.
Security notes
signal-cli는 account key를 로컬에 저장합니다(보통~/.local/share/signal-cli/data/).- 서버를 옮기거나 재구성하기 전에 Signal account state를 백업하세요.
- DM access를 넓게 열 의도가 없다면
channels.signal.dmPolicy: "pairing"을 유지하세요. - SMS verification은 등록이나 복구 시에만 필요하지만, 번호나 account 제어권을 잃으면 재등록이 복잡해질 수 있습니다.
Configuration reference (Signal)
전체 설정 문서: Configuration Provider options:channels.signal.enabled: 채널 시작 enable/disable.channels.signal.account: bot account의 E.164.channels.signal.cliPath:signal-cli경로.channels.signal.httpUrl: 전체 daemon URL(host/port보다 우선).channels.signal.httpHost,channels.signal.httpPort: daemon bind(default127.0.0.1:8080).channels.signal.autoStart: daemon auto-spawn(httpUrl이 없으면 기본값 true).channels.signal.startupTimeoutMs: 시작 대기 timeout(ms, 최대 120000).channels.signal.receiveMode:on-start | manual.channels.signal.ignoreAttachments: attachment 다운로드 건너뛰기.channels.signal.ignoreStories: daemon의 stories 무시.channels.signal.sendReadReceipts: read receipt 전달.channels.signal.dmPolicy:pairing | allowlist | open | disabled(기본값: pairing).channels.signal.allowFrom: DM allowlist(E.164 또는uuid:<id>).open에는"*"가 필요합니다. Signal에는 username이 없으므로 phone/UUID id를 사용합니다.channels.signal.groupPolicy:open | allowlist | disabled(기본값: allowlist).channels.signal.groupAllowFrom: group sender allowlist.channels.signal.historyLimit: context에 포함할 최대 group message 수(0이면 비활성화).channels.signal.dmHistoryLimit: user turn 기준 DM history limit. user별 override:channels.signal.dms["<phone_or_uuid>"].historyLimit.channels.signal.textChunkLimit: outbound chunk 크기(chars).channels.signal.chunkMode:length(기본값) 또는newline. 길이 기준 chunking 전에 빈 줄 단위(문단 경계)로 나눕니다.channels.signal.mediaMaxMb: inbound/outbound media cap(MB).
agents.list[].groupChat.mentionPatterns(Signal은 native mention을 지원하지 않음).messages.groupChat.mentionPatterns(global fallback).messages.responsePrefix.