Signal (signal-cli)

Estado: integración de CLI externo. El Gateway se comunica con signal-cli vía HTTP JSON-RPC + SSE.

Requisitos previos

  • OpenClaw instalado en tu servidor (el flujo de Linux a continuación se probó en Ubuntu 24).
  • signal-cli disponible en el host donde se ejecuta el gateway.
  • Un número de teléfono que pueda recibir un SMS de verificación (para la ruta de registro por SMS).
  • Acceso a navegador para el captcha de Signal (signalcaptchas.org) durante el registro.

Configuración rápida (principiante)

  1. Usa un número de Signal separado para el bot (recomendado).
  2. Instala signal-cli (se requiere Java si usas el build JVM).
  3. Elige una ruta de configuración:
    • Ruta A (enlace QR): signal-cli link -n "OpenClaw" y escanea con Signal.
    • Ruta B (registro SMS): registra un número dedicado con captcha + verificación SMS.
  4. Configura OpenClaw y reinicia el gateway.
  5. Envía un primer DM y aprueba el emparejamiento (openclaw pairing approve signal <CODE>).

Configuración mínima:

{
  channels: {
    signal: {
      enabled: true,
      account: "+15551234567",
      cliPath: "signal-cli",
      dmPolicy: "pairing",
      allowFrom: ["+15557654321"],
    },
  },
}

Referencia de campos:

CampoDescripción
accountNúmero del bot en formato E.164 (+15551234567)
cliPathRuta a signal-cli (signal-cli si está en PATH)
dmPolicyPolítica de acceso DM (pairing recomendado)
allowFromNúmeros o valores uuid:<id> permitidos para DM

Qué es

  • Canal de Signal vía signal-cli (no libsignal embebido).
  • Enrutamiento determinista: las respuestas siempre vuelven a Signal.
  • Los DMs comparten la sesión principal del agente; los grupos están aislados (agent:<agentId>:signal:group:<groupId>).

Escrituras de configuración

Por defecto, Signal permite escribir actualizaciones de configuración activadas por /config set|unset (requiere commands.config: true).

Deshabilitar con:

{
  channels: { signal: { configWrites: false } },
}

El modelo de número (importante)

  • El gateway se conecta a un dispositivo Signal (la cuenta de signal-cli).
  • Si ejecutas el bot en tu cuenta personal de Signal, ignorará tus propios mensajes (protección contra bucles).
  • Para “le envío un mensaje al bot y me responde”, usa un número de bot separado.

Ruta de configuración A: enlazar cuenta Signal existente (QR)

  1. Instala signal-cli (build JVM o nativo).
  2. Enlaza una cuenta de bot:
    • signal-cli link -n "OpenClaw" y luego escanea el QR en Signal.
  3. Configura Signal e inicia el gateway.

Ejemplo:

{
  channels: {
    signal: {
      enabled: true,
      account: "+15551234567",
      cliPath: "signal-cli",
      dmPolicy: "pairing",
      allowFrom: ["+15557654321"],
    },
  },
}

Soporte multi-cuenta: usa channels.signal.accounts con configuración por cuenta y name opcional. Consulta gateway/configuration para el patrón compartido.

Ruta de configuración B: registrar número de bot dedicado (SMS, Linux)

Usa esto cuando quieras un número de bot dedicado en lugar de enlazar una cuenta existente de la app Signal.

  1. Obtén un número que pueda recibir SMS (o verificación por voz para líneas fijas).
    • Usa un número de bot dedicado para evitar conflictos de cuenta/sesión.
  2. Instala signal-cli en el host del gateway:
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

Si usas el build JVM (signal-cli-${VERSION}.tar.gz), instala primero JRE 25+. Mantén signal-cli actualizado; el proyecto advierte que versiones antiguas pueden fallar a medida que las APIs del servidor de Signal cambian.

  1. Registra y verifica el número:
signal-cli -a +<BOT_PHONE_NUMBER> register

Si se requiere captcha:

  1. Abre https://signalcaptchas.org/registration/generate.html.
  2. Completa el captcha, copia el enlace signalcaptcha://... de “Open Signal”.
  3. Ejecuta desde la misma IP externa que la sesión del navegador cuando sea posible.
  4. Ejecuta el registro de nuevo inmediatamente (los tokens de captcha expiran rápido):
signal-cli -a +<BOT_PHONE_NUMBER> register --captcha '<SIGNALCAPTCHA_URL>'
signal-cli -a +<BOT_PHONE_NUMBER> verify <VERIFICATION_CODE>
  1. Configura OpenClaw, reinicia el gateway, verifica el canal:
# Si ejecutas el gateway como servicio de usuario systemd:
systemctl --user restart openclaw-gateway

# Luego verifica:
openclaw doctor
openclaw channels status --probe
  1. Empareja tu remitente de DM:
    • Envía cualquier mensaje al número del bot.
    • Aprueba el código en el servidor: openclaw pairing approve signal <PAIRING_CODE>.
    • Guarda el número del bot como contacto en tu teléfono para evitar “Contacto desconocido”.

Importante: registrar una cuenta de número de teléfono con signal-cli puede des-autenticar la sesión principal de la app Signal para ese número. Prefiere un número de bot dedicado, o usa el modo de enlace QR si necesitas mantener la configuración existente de tu app.

Referencias upstream:

  • README de signal-cli: https://github.com/AsamK/signal-cli
  • Flujo de captcha: https://github.com/AsamK/signal-cli/wiki/Registration-with-captcha
  • Flujo de enlace: https://github.com/AsamK/signal-cli/wiki/Linking-other-devices-(Provisioning)

Modo daemon externo (httpUrl)

Si quieres gestionar signal-cli tú mismo (arranques en frío lentos del JVM, init de contenedor, o CPUs compartidas), ejecuta el daemon por separado y apunta OpenClaw a él:

{
  channels: {
    signal: {
      httpUrl: "http://127.0.0.1:8080",
      autoStart: false,
    },
  },
}

Esto omite el auto-spawn y la espera de inicio dentro de OpenClaw. Para arranques lentos al auto-spawn, configura channels.signal.startupTimeoutMs.

Control de acceso (DMs + grupos)

DMs:

  • Predeterminado: channels.signal.dmPolicy = "pairing".
  • Los remitentes desconocidos reciben un código de emparejamiento; los mensajes se ignoran hasta que se aprueben (los códigos expiran después de 1 hora).
  • Aprobar vía:
    • openclaw pairing list signal
    • openclaw pairing approve signal <CODE>
  • El emparejamiento es el intercambio de token predeterminado para DMs de Signal. Detalles: Emparejamiento
  • Los remitentes solo UUID (de sourceUuid) se almacenan como uuid:<id> en channels.signal.allowFrom.

Grupos:

  • channels.signal.groupPolicy = open | allowlist | disabled.
  • channels.signal.groupAllowFrom controla quién puede activar en grupos cuando allowlist está configurado.
  • channels.signal.groups["<group-id>" | "*"] puede anular el comportamiento de grupo con requireMention, tools y toolsBySender.
  • Usa channels.signal.accounts.<id>.groups para anulaciones por cuenta en configuraciones multi-cuenta.
  • Nota de ejecución: si channels.signal falta completamente, el runtime recurre a groupPolicy="allowlist" para verificaciones de grupo (incluso si channels.defaults.groupPolicy está configurado).

Cómo funciona (comportamiento)

  • signal-cli se ejecuta como daemon; el gateway lee eventos vía SSE.
  • Los mensajes entrantes se normalizan en el envelope compartido del canal.
  • Las respuestas siempre se enrutan de vuelta al mismo número o grupo.

Multimedia + límites

  • El texto saliente se fragmenta a channels.signal.textChunkLimit (predeterminado 4000).
  • Fragmentado por nueva línea opcional: configura channels.signal.chunkMode="newline" para dividir en líneas en blanco (límites de párrafo) antes del fragmentado por longitud.
  • Adjuntos soportados (base64 obtenido de signal-cli).
  • Límite de multimedia predeterminado: channels.signal.mediaMaxMb (predeterminado 8).
  • Usa channels.signal.ignoreAttachments para omitir la descarga de multimedia.
  • El contexto de historial de grupo usa channels.signal.historyLimit (o channels.signal.accounts.*.historyLimit), recurriendo a messages.groupChat.historyLimit. Configura 0 para deshabilitar (predeterminado 50).

Escritura + confirmaciones de lectura

  • Indicadores de escritura: OpenClaw envía señales de escritura vía signal-cli sendTyping y las refresca mientras se ejecuta una respuesta.
  • Confirmaciones de lectura: cuando channels.signal.sendReadReceipts es true, OpenClaw reenvía confirmaciones de lectura para DMs permitidos.
  • Signal-cli no expone confirmaciones de lectura para grupos.

Reacciones (herramienta de mensajes)

  • Usa message action=react con channel=signal.
  • Destinos: E.164 del remitente o UUID (usa uuid:<id> de la salida de emparejamiento; el UUID sin formato también funciona).
  • messageId es el timestamp de Signal del mensaje al que reaccionas.
  • Las reacciones en grupo requieren targetAuthor o targetAuthorUuid.

Ejemplos:

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=✅

Configuración:

  • channels.signal.actions.reactions: habilitar/deshabilitar acciones de reacción (predeterminado true).
  • channels.signal.reactionLevel: off | ack | minimal | extensive.
    • off/ack deshabilita reacciones del agente (la herramienta de mensajes react dará error).
    • minimal/extensive habilita reacciones del agente y establece el nivel de guía.
  • Anulaciones por cuenta: channels.signal.accounts.<id>.actions.reactions, channels.signal.accounts.<id>.reactionLevel.

Destinos de entrega (CLI/cron)

  • DMs: signal:+15551234567 (o E.164 sin formato).
  • DMs por UUID: uuid:<id> (o UUID sin formato).
  • Grupos: signal:group:<groupId>.
  • Nombres de usuario: username:<name> (si es compatible con tu cuenta de Signal).

Resolución de problemas

Ejecuta esta secuencia primero:

openclaw status
openclaw gateway status
openclaw logs --follow
openclaw doctor
openclaw channels status --probe

Luego confirma el estado de emparejamiento de DM si es necesario:

openclaw pairing list signal

Fallos comunes:

  • Daemon accesible pero sin respuestas: verifica la configuración de cuenta/daemon (httpUrl, account) y el modo de recepción.
  • DMs ignorados: el remitente está pendiente de aprobación de emparejamiento.
  • Mensajes de grupo ignorados: el control de remitente/mención de grupo bloquea la entrega.
  • Errores de validación de configuración después de ediciones: ejecuta openclaw doctor --fix.
  • Signal falta en los diagnósticos: confirma channels.signal.enabled: true.

Verificaciones adicionales:

openclaw pairing list signal
pgrep -af signal-cli
grep -i "signal" "/tmp/openclaw/openclaw-$(date +%Y-%m-%d).log" | tail -20

Para el flujo de diagnóstico: /channels/troubleshooting.

Notas de seguridad

  • signal-cli almacena las claves de cuenta localmente (normalmente ~/.local/share/signal-cli/data/).
  • Haz respaldo del estado de la cuenta de Signal antes de migración o reconstrucción del servidor.
  • Mantén channels.signal.dmPolicy: "pairing" a menos que quieras explícitamente acceso DM más amplio.
  • La verificación por SMS solo es necesaria para flujos de registro o recuperación, pero perder el control del número/cuenta puede complicar el re-registro.

Referencia de configuración (Signal)

Configuración completa: Configuración

Opciones del proveedor:

  • channels.signal.enabled: habilitar/deshabilitar inicio del canal.
  • channels.signal.account: E.164 de la cuenta del bot.
  • channels.signal.cliPath: ruta a signal-cli.
  • channels.signal.httpUrl: URL completa del daemon (anula host/port).
  • channels.signal.httpHost, channels.signal.httpPort: bind del daemon (predeterminado 127.0.0.1:8080).
  • channels.signal.autoStart: auto-spawn del daemon (predeterminado true si httpUrl no está configurado).
  • channels.signal.startupTimeoutMs: timeout de espera de inicio en ms (máximo 120000).
  • channels.signal.receiveMode: on-start | manual.
  • channels.signal.ignoreAttachments: omitir descarga de adjuntos.
  • channels.signal.ignoreStories: ignorar historias del daemon.
  • channels.signal.sendReadReceipts: reenviar confirmaciones de lectura.
  • channels.signal.dmPolicy: pairing | allowlist | open | disabled (predeterminado: pairing).
  • channels.signal.allowFrom: lista de acceso de DM (E.164 o uuid:<id>). open requiere "*". Signal no tiene nombres de usuario; usa IDs de teléfono/UUID.
  • channels.signal.groupPolicy: open | allowlist | disabled (predeterminado: allowlist).
  • channels.signal.groupAllowFrom: lista de acceso de remitentes de grupo.
  • channels.signal.groups: anulaciones por grupo indexadas por ID de grupo de Signal (o "*"). Campos soportados: requireMention, tools, toolsBySender.
  • channels.signal.accounts.<id>.groups: versión por cuenta de channels.signal.groups para configuraciones multi-cuenta.
  • channels.signal.historyLimit: máximo de mensajes de grupo para incluir como contexto (0 deshabilita).
  • channels.signal.dmHistoryLimit: límite de historial de DM en turnos de usuario. Anulaciones por usuario: channels.signal.dms["<phone_or_uuid>"].historyLimit.
  • channels.signal.textChunkLimit: tamaño de fragmento saliente (caracteres).
  • channels.signal.chunkMode: length (predeterminado) o newline para dividir en líneas en blanco (límites de párrafo) antes del fragmentado por longitud.
  • channels.signal.mediaMaxMb: límite de multimedia entrante/saliente (MB).

Opciones globales relacionadas:

  • agents.list[].groupChat.mentionPatterns (Signal no soporta menciones nativas).
  • messages.groupChat.mentionPatterns (respaldo global).
  • messages.responsePrefix.