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:
signal-clion$PATH(or an explicitbinaryconfig value).- 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 registerstarts SMS or voice verification. Complete withsignal-cli verify <code>. The number ends up owned by the daemon. - Link as a secondary device.
signal-cli linkprints atsdevice://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-cliemits areceiveJSON-RPC notification per arriving message. rousseau parses it, drops anything not in the allowlist, and hands the body to theHandler. - Outbound. rousseau writes a JSON-RPC
sendrequest tosignal-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. |