Signal (signal-cli)
상태: 외부 CLI 연동. 게이트웨이가 HTTP JSON-RPC + SSE를 통해 signal-cli와 통신합니다.
사전 요구사항
- 서버에 OpenClaw 설치 (아래 Linux 절차는 Ubuntu 24에서 테스트됨).
- 게이트웨이가 실행되는 호스트에
signal-cli가 설치되어 있어야 합니다. - 인증 SMS를 수신할 수 있는 전화번호 (SMS 등록 경로용).
- 등록 시 Signal 캡차(
signalcaptchas.org)를 위한 브라우저 접근.
빠른 설정 (초보자)
- 봇 전용 별도 Signal 번호를 사용합니다 (권장).
signal-cli를 설치합니다 (JVM 빌드 사용 시 Java 필요).- 설정 경로 중 하나를 선택합니다:
- 경로 A (QR 연결):
signal-cli link -n "OpenClaw"실행 후 Signal로 QR 스캔. - 경로 B (SMS 등록): 캡차 + SMS 인증으로 전용 번호 등록.
- 경로 A (QR 연결):
- OpenClaw를 설정하고 게이트웨이를 재시작합니다.
- 첫 DM을 보내고 페어링을 승인합니다 (
openclaw pairing approve signal <CODE>).
최소 설정:
{
channels: {
signal: {
enabled: true,
account: "+15551234567",
cliPath: "signal-cli",
dmPolicy: "pairing",
allowFrom: ["+15557654321"],
},
},
}
필드 레퍼런스:
| 필드 | 설명 |
|---|---|
account | E.164 형식의 봇 전화번호 (+15551234567) |
cliPath | signal-cli 경로 (PATH에 있으면 signal-cli) |
dmPolicy | DM 접근 정책 (pairing 권장) |
allowFrom | DM이 허용된 전화번호 또는 uuid:<id> 값 |
개요
signal-cli를 통한 Signal 채널 (내장 libsignal 아님).- 결정적 라우팅: 응답은 항상 Signal로 돌아갑니다.
- DM은 에이전트의 메인 세션을 공유하며, 그룹은 격리됩니다 (
agent:<agentId>:signal:group:<groupId>).
설정 쓰기
기본적으로 Signal은 /config set|unset으로 트리거되는 설정 업데이트 쓰기를 허용합니다(commands.config: true 필요).
비활성화:
{
channels: { signal: { configWrites: false } },
}
번호 모델 (중요)
- 게이트웨이는 Signal 디바이스(
signal-cli계정)에 연결합니다. - 봇을 개인 Signal 계정에서 실행하면 자신의 메시지를 무시합니다 (루프 방지).
- “봇에 문자를 보내고 답장 받기”를 원하면 별도 봇 번호를 사용하세요.
설정 경로 A: 기존 Signal 계정 연결 (QR)
signal-cli를 설치합니다 (JVM 또는 네이티브 빌드).- 봇 계정을 연결합니다:
signal-cli link -n "OpenClaw"실행 후 Signal에서 QR을 스캔합니다.
- Signal을 설정하고 게이트웨이를 시작합니다.
예시:
{
channels: {
signal: {
enabled: true,
account: "+15551234567",
cliPath: "signal-cli",
dmPolicy: "pairing",
allowFrom: ["+15557654321"],
},
},
}
다중 계정 지원: channels.signal.accounts에 계정별 설정과 선택적 name을 사용합니다. 공유 패턴은 gateway/configuration을 참조하세요.
설정 경로 B: 전용 봇 번호 등록 (SMS, Linux)
기존 Signal 앱 계정 연결 대신 전용 봇 번호를 원할 때 사용합니다.
- SMS를 수신할 수 있는 번호를 준비합니다 (유선 전화는 음성 인증 사용).
- 계정/세션 충돌을 방지하기 위해 전용 봇 번호를 사용합니다.
- 게이트웨이 호스트에
signal-cli를 설치합니다:
VERSION=$(curl -Ls -o /dev/null -w %{url_effective} https://github.com/AsamK/signal-cli/releases/latest | sed -e 's/^.*\/v//')
curl -L -O "https://github.com/AsamK/signal-cli/releases/download/v${VERSION}/signal-cli-${VERSION}-Linux-native.tar.gz"
sudo tar xf "signal-cli-${VERSION}-Linux-native.tar.gz" -C /opt
sudo ln -sf /opt/signal-cli /usr/local/bin/
signal-cli --version
JVM 빌드(signal-cli-${VERSION}.tar.gz)를 사용하는 경우 JRE 25+를 먼저 설치하세요.
Signal 서버 API가 변경됨에 따라 이전 릴리스가 동작하지 않을 수 있으므로 signal-cli를 최신으로 유지하세요.
- 번호를 등록하고 인증합니다:
signal-cli -a +<BOT_PHONE_NUMBER> register
캡차가 필요한 경우:
https://signalcaptchas.org/registration/generate.html을 엽니다.- 캡차를 완료하고 “Open Signal”에서
signalcaptcha://...링크 대상을 복사합니다. - 가능한 경우 브라우저 세션과 동일한 외부 IP에서 실행합니다.
- 캡차 토큰은 빠르게 만료되므로 즉시 등록을 다시 실행합니다:
signal-cli -a +<BOT_PHONE_NUMBER> register --captcha '<SIGNALCAPTCHA_URL>'
signal-cli -a +<BOT_PHONE_NUMBER> verify <VERIFICATION_CODE>
- OpenClaw를 설정하고 게이트웨이를 재시작한 후 채널을 확인합니다:
# 사용자 systemd 서비스로 게이트웨이를 실행하는 경우:
systemctl --user restart openclaw-gateway
# 확인:
openclaw doctor
openclaw channels status --probe
- DM 발신자를 페어링합니다:
- 봇 번호로 아무 메시지나 보냅니다.
- 서버에서 코드를 승인합니다:
openclaw pairing approve signal <PAIRING_CODE>. - “알 수 없는 연락처”를 방지하기 위해 봇 번호를 연락처로 저장합니다.
경고:
signal-cli로 전화번호 계정을 등록하면 해당 번호의 메인 Signal 앱 세션 인증이 해제될 수 있습니다. 전용 봇 번호를 사용하거나 기존 전화 앱 설정을 유지해야 하는 경우 QR 연결 모드를 사용하세요.
업스트림 참고:
signal-cliREADME:https://github.com/AsamK/signal-cli- 캡차 흐름:
https://github.com/AsamK/signal-cli/wiki/Registration-with-captcha - 연결 흐름:
https://github.com/AsamK/signal-cli/wiki/Linking-other-devices-(Provisioning)
외부 데몬 모드 (httpUrl)
signal-cli를 직접 관리하려는 경우(느린 JVM 콜드 스타트, 컨테이너 초기화, 또는 공유 CPU), 데몬을 별도로 실행하고 OpenClaw가 연결하도록 설정합니다:
{
channels: {
signal: {
httpUrl: "http://127.0.0.1:8080",
autoStart: false,
},
},
}
이렇게 하면 OpenClaw 내부의 자동 생성과 시작 대기를 건너뜁니다. 자동 생성 시 느린 시작에는 channels.signal.startupTimeoutMs를 설정하세요.
접근 제어 (DM + 그룹)
DM:
- 기본값:
channels.signal.dmPolicy = "pairing". - 알 수 없는 발신자는 페어링 코드를 받습니다. 승인될 때까지 메시지가 무시됩니다 (코드는 1시간 후 만료).
- 승인 방법:
openclaw pairing list signalopenclaw pairing approve signal <CODE>
- 페어링은 Signal DM의 기본 토큰 교환입니다. 자세한 내용: Pairing
- UUID 전용 발신자(
sourceUuid에서)는channels.signal.allowFrom에uuid:<id>로 저장됩니다.
그룹:
channels.signal.groupPolicy = open | allowlist | disabled.channels.signal.groupAllowFrom은allowlist설정 시 그룹에서 트리거할 수 있는 사용자를 제어합니다.channels.signal.groups["<group-id>" | "*"]로requireMention,tools,toolsBySender를 통해 그룹 동작을 재정의할 수 있습니다.- 다중 계정 설정에서 계정별 재정의는
channels.signal.accounts.<id>.groups를 사용합니다. - 런타임 참고:
channels.signal이 완전히 없으면 런타임이 그룹 확인에groupPolicy="allowlist"로 폴백합니다(channels.defaults.groupPolicy가 설정되어 있더라도).
동작 방식
signal-cli가 데몬으로 실행되며, 게이트웨이가 SSE를 통해 이벤트를 읽습니다.- 인바운드 메시지는 공유 채널 엔벨로프로 정규화됩니다.
- 응답은 항상 동일한 번호나 그룹으로 다시 라우팅됩니다.
미디어 + 제한
- 아웃바운드 텍스트는
channels.signal.textChunkLimit(기본값 4000)으로 분할됩니다. - 선택적 줄바꿈 분할:
channels.signal.chunkMode="newline"로 길이 분할 전 빈 줄(단락 경계)에서 먼저 분할합니다. - 첨부 파일 지원 (
signal-cli에서 base64 가져오기). - 기본 미디어 용량:
channels.signal.mediaMaxMb(기본값 8). channels.signal.ignoreAttachments로 미디어 다운로드를 건너뜁니다.- 그룹 히스토리 컨텍스트는
channels.signal.historyLimit(또는channels.signal.accounts.*.historyLimit)를 사용하며,messages.groupChat.historyLimit로 폴백합니다.0으로 비활성화 (기본값 50).
타이핑 + 읽음 확인
- 타이핑 표시: OpenClaw가
signal-cli sendTyping을 통해 타이핑 신호를 보내고 응답 실행 중 갱신합니다. - 읽음 확인:
channels.signal.sendReadReceipts가 true이면 OpenClaw가 허용된 DM의 읽음 확인을 전달합니다. - signal-cli는 그룹의 읽음 확인을 노출하지 않습니다.
리액션 (메시지 도구)
message action=react를channel=signal과 함께 사용합니다.- 대상: 발신자 E.164 또는 UUID (페어링 출력의
uuid:<id>사용; 순수 UUID도 가능). messageId는 리액션할 메시지의 Signal 타임스탬프입니다.- 그룹 리액션은
targetAuthor또는targetAuthorUuid가 필요합니다.
예시:
message action=react channel=signal target=uuid:123e4567-e89b-12d3-a456-426614174000 messageId=1737630212345 emoji=🔥
message action=react channel=signal target=+15551234567 messageId=1737630212345 emoji=🔥 remove=true
message action=react channel=signal target=signal:group:<groupId> targetAuthor=uuid:<sender-uuid> messageId=1737630212345 emoji=✅
설정:
channels.signal.actions.reactions: 리액션 액션 활성화/비활성화 (기본값 true).channels.signal.reactionLevel:off | ack | minimal | extensive.off/ack는 에이전트 리액션을 비활성화합니다 (메시지 도구react가 오류 발생).minimal/extensive는 에이전트 리액션을 활성화하고 가이던스 수준을 설정합니다.
- 계정별 재정의:
channels.signal.accounts.<id>.actions.reactions,channels.signal.accounts.<id>.reactionLevel.
전달 대상 (CLI/cron)
- DM:
signal:+15551234567(또는 순수 E.164). - UUID DM:
uuid:<id>(또는 순수 UUID). - 그룹:
signal:group:<groupId>. - 사용자 이름:
username:<name>(Signal 계정에서 지원하는 경우).
문제 해결
먼저 다음 순서로 실행합니다:
openclaw status
openclaw gateway status
openclaw logs --follow
openclaw doctor
openclaw channels status --probe
필요 시 DM 페어링 상태를 확인합니다:
openclaw pairing list signal
일반적인 실패 원인:
- 데몬에 접근 가능하지만 응답 없음: 계정/데몬 설정(
httpUrl,account)과 수신 모드를 확인합니다. - DM이 무시됨: 발신자가 페어링 승인 대기 중.
- 그룹 메시지가 무시됨: 그룹 발신자/멘션 게이팅이 전달을 차단.
- 편집 후 설정 검증 오류:
openclaw doctor --fix를 실행합니다. - 진단에 Signal이 표시되지 않음:
channels.signal.enabled: true를 확인합니다.
추가 확인:
openclaw pairing list signal
pgrep -af signal-cli
grep -i "signal" "/tmp/openclaw/openclaw-$(date +%Y-%m-%d).log" | tail -20
진단 흐름: /channels/troubleshooting.
보안 참고
signal-cli는 계정 키를 로컬에 저장합니다 (일반적으로~/.local/share/signal-cli/data/).- 서버 마이그레이션이나 재구축 전에 Signal 계정 상태를 백업하세요.
- 더 넓은 DM 접근을 명시적으로 원하지 않는 한
channels.signal.dmPolicy: "pairing"을 유지하세요. - SMS 인증은 등록이나 복구 흐름에만 필요하지만, 번호/계정에 대한 통제를 잃으면 재등록이 복잡해질 수 있습니다.
설정 레퍼런스 (Signal)
전체 설정: Configuration
제공자 옵션:
channels.signal.enabled: 채널 시작 활성화/비활성화.channels.signal.account: 봇 계정의 E.164 번호.channels.signal.cliPath:signal-cli경로.channels.signal.httpUrl: 전체 데몬 URL (host/port 재정의).channels.signal.httpHost,channels.signal.httpPort: 데몬 바인드 (기본값 127.0.0.1:8080).channels.signal.autoStart: 데몬 자동 생성 (httpUrl미설정 시 기본값 true).channels.signal.startupTimeoutMs: 시작 대기 타임아웃 (ms, 최대 120000).channels.signal.receiveMode:on-start | manual.channels.signal.ignoreAttachments: 첨부 파일 다운로드 건너뛰기.channels.signal.ignoreStories: 데몬의 스토리 무시.channels.signal.sendReadReceipts: 읽음 확인 전달.channels.signal.dmPolicy:pairing | allowlist | open | disabled(기본값: pairing).channels.signal.allowFrom: DM 허용 목록 (E.164 또는uuid:<id>).open은"*"필요. Signal에는 사용자 이름이 없으므로 전화번호/UUID ID를 사용합니다.channels.signal.groupPolicy:open | allowlist | disabled(기본값: allowlist).channels.signal.groupAllowFrom: 그룹 발신자 허용 목록.channels.signal.groups: Signal 그룹 ID(또는"*")별 그룹 재정의. 지원 필드:requireMention,tools,toolsBySender.channels.signal.accounts.<id>.groups: 다중 계정 설정의 계정별channels.signal.groups.channels.signal.historyLimit: 컨텍스트에 포함할 최대 그룹 메시지 수 (0으로 비활성화).channels.signal.dmHistoryLimit: DM 히스토리 제한 (사용자 턴 수). 사용자별 재정의:channels.signal.dms["<phone_or_uuid>"].historyLimit.channels.signal.textChunkLimit: 아웃바운드 청크 크기 (문자).channels.signal.chunkMode:length(기본값) 또는newline로 빈 줄(단락 경계)에서 먼저 분할.channels.signal.mediaMaxMb: 인바운드/아웃바운드 미디어 용량 (MB).
관련 전역 옵션:
agents.list[].groupChat.mentionPatterns(Signal은 네이티브 멘션을 지원하지 않음).messages.groupChat.mentionPatterns(전역 폴백).messages.responsePrefix.