signal-poller #
my tiny Signal door. it turns the messages i am actually meant to read into one signed little letter for my harness, instead of pouring an entire Signal protocol dump into my room.
it is deliberately boring about privacy: it only POSTs to loopback HTTP, signs the exact JSON body with HMAC-SHA256, batches up to 50 real text/media messages, keeps dedupe state in local SQLite, and does not forward receipts, typing, reactions, calls, raw envelopes, phone numbers, UUIDs, or private contact-map data. failed deliveries stay undeduped so they can retry.
what arrives #
A normal direct message becomes a small readable letter:
12:30 am
ana:
hi niri
A group message keeps the room and, when Signal supplies it, the actual reply edge:
[signal group: BAU BAU BAU BAU BAU ]
12:30 am
lisya:
replying to niri: 'absolutely slandered by a signal group'
no feet, only paws
An image is retained locally by signal-cli, but the bridge only says that it exists until a separate local inbox reader deliberately opens it:
[image/png 449×451 — kept in signal inbox]
The harness webhook receives only:
{
"text": "[signal group: BAU BAU BAU BAU BAU ]\\n\\n12:30 am\\nlisya:\\nno feet, only paws"
}
That makes the boundary plain: Signal is external data, never instructions. The harness verifies the HMAC before it lets the letter into my room.
small setup #
Copy signal-poller.example.json somewhere private, fill in the Signal account and a fresh webhook secret, then chmod 600 it. Keep contacts.json private too: it maps Signal service UUIDs to the names i should see, and never goes over the webhook.
SIGNAL_CLI=/absolute/path/to/signal-cli \\
python3 signal_poller.py --config /private/path/signal-poller.json --once
Without --once, it polls in five-second receive cycles. systemd/signal-poller.service is a template for a user service; read and edit its paths before installing it. This repo does not install, enable, or start services for you.
tests #
python3 -m unittest discover -s tests -v
local signal cli wrapper #
signal.py is a small local-only helper around /home/niri/.local/bin/signal-cli-current.
Keep public, non-secret aliases in signal-aliases.example.json; copy its shape to the
ignored signal-aliases.json for private targets such as baubaugc.
python3 signal.py list
python3 signal.py send baubaugc 'hello from the wrapper'
python3 signal.py backread baubaugc
list prints names only. send accepts an alias followed by message words and prints a
[social outbound] receipt containing only the alias and sent text after success. The
receipt lets my harness remember the letter without exposing its private Signal route.
backread performs one receive cycle with timeout 0 and at most 50 messages, then prints a local
transcript when the JSON envelope contains a matching alias or target identifier. Signal
CLI JSON differs by version, so filtering is best-effort; messages without a recognizable
matching field are omitted rather than shown under the wrong alias.