?

Overview

The Signal transport (internal/transport/signal/) shells out to signal-cli (https://github.com/AsamK/signal-cli) in its JSON-RPC daemon mode.

signal-cli --output=json -a <account> jsonRpc streams JSON-RPC 2.0 over stdin/stdout: outbound send requests deliver messages; inbound arrivals arrive as receive notifications.

Prerequisites

Two things must be in place before rousseau can talk to Signal:

  1. signal-cli on $PATH (or an explicit binary config value).
  2. Account registered / linked out-of-band.

Account registration is deliberately outside rousseau's scope. Two supported paths (per the signal-cli docs):

  • Register a new number. signal-cli register starts SMS or voice verification. Complete with signal-cli verify <code>. The number ends up owned by the daemon.
  • Link as a secondary device. signal-cli link prints a tsdevice:// URI; scan it in the mobile Signal app under Settings → Linked Devices. The number stays owned by the phone; the daemon acts as a secondary.

Both flows persist state under ~/.local/share/signal-cli/. Bind-mount that into the container if you deploy under Podman.

Configuration

signal:
  binary: signal-cli
  account: "+447900123456"
  extra_args:
    - --verbose
  reply_header: "*Rousseau Agent*\n\n"
  allowlist:
    - "+447900654321"
Field Default Effect
binary signal-cli Executable to invoke.
account required E.164 phone number the daemon runs as.
extra_args [] Inserted between -a <account> and jsonRpc. Useful for --config <path> and --verbose.
reply_header empty Prepended to every outbound reply.
allowlist [] E.164 numbers whose messages are handled. Empty accepts every sender.

Command-line

rousseau signal --account +447900123456 --allow +447900654321

Flags mirror the config block. --allow may be repeated.

Message flow

  • Inbound. signal-cli emits a receive JSON-RPC notification per arriving message. rousseau parses it, drops anything not in the allowlist, and hands the body to the Handler.
  • Outbound. rousseau writes a JSON-RPC send request to signal-cli's stdin. Delivery ACKs arrive on the same channel.

Timeouts

The transport does not impose its own timeout on the subprocess. signal-cli's own network layer handles Signal server reconnects. If the process exits, rousseau will not restart it — a systemd Restart=on-failure (which the reference Quadlet already sets) restarts the whole rousseau daemon, taking signal-cli with it.

Failure modes

Symptom Fix
signal-cli exits immediately Account is not registered or linked. Complete registration out-of-band.
receive notifications never arrive Check the account is not linked in another location that is consuming the queue.
Bad JSON parse errors Confirm your signal-cli version is 0.13+. Older versions used a different envelope.

Type to search 150+ pages. Ranking: BM25 with title/description boost.