This repo is currently run as the Docker Compose project pf_signal_bot.
docker-compose -p pf_signal_bot up -d --builddocker-compose -p pf_signal_bot psInvoke-RestMethod -Uri http://localhost:8000/healthExpected health:
{"status":"ok","signal_gateway":"ok","database":"ok"}Invoke-WebRequest -Uri http://localhost:8080/v1/accounts -UseBasicParsingThe bot number in .env must match the account returned by Signal REST. Use E.164 format with a leading +.
Set username and display name:
docker-compose -p pf_signal_bot run --rm app python scripts/update_signal_profile.py --username PFSignalBot --name "PF Signal Bot"Set avatar from an image file:
docker-compose -p pf_signal_bot run --rm app python scripts/update_signal_profile.py --avatar .\bot-avatar.pngSignal may return the final username with a discriminator, depending on availability.
The receiver service keeps a WebSocket open to Signal REST:
docker logs --tail 120 pf_signal_bot-receiver-1Expected containers include:
pf_signal_bot-app-1pf_signal_bot-worker-1pf_signal_bot-receiver-1pf_signal_bot-postgres-1pf_signal_bot-signal-cli-rest-api-1
- Add the bot Signal account to a group.
- Send any message in that group so the bot observes the group and sender membership.
- If your Signal number is configured in
SIGNAL_ADMIN_NUMBERS, authorize from Signal:
!authorize this group
You can also DM the bot:
!groups
!authorize <GROUP_ID>
!access <SIGNAL_NUMBER>
The first successful !authorize this group sends a one-time intro message into the group. To resend that intro later, an admin can send !intro this group.
- Or list observed groups through the Admin API:
$token = ((Get-Content .env | Where-Object { $_ -match '^ADMIN_API_TOKEN=' }) -split '=',2)[1].Trim()
Invoke-RestMethod -Uri http://localhost:8000/admin/groups -Headers @{ Authorization = "Bearer $token" }- Authorize the group through the Admin API:
Invoke-RestMethod -Method Post -Uri http://localhost:8000/admin/groups/<GROUP_ID>/authorize -Headers @{ Authorization = "Bearer $token" }After that, DMs from users recently observed in that authorized group are eligible for replies. DMs from users only seen in ingest-only groups, ignored groups, or no groups are denied.
See ADMIN_APPROVAL_WORKFLOW.md for the full Signal-admin workflow.
Group auto-replies are gated separately. With GROUP_AUTO_REPLY_REQUIRE_MENTION=true, the bot ingests group traffic but only replies in a group when the message mentions the bot through Signal mention metadata or plain text such as @pfbot.
The bot treats each DM or group mention as a fresh request by default. It only treats a message as a continuation when the user directly replies to/quotes a previous Signal message. In that case, the quoted message id/text is passed to the model as local thread context for that one answer.
Signal admin commands now include:
!status
!groups
!intro this group
!label this group <alias>
!label <group_id> <alias>
!authorize this group
!authorize <group_id_or_alias>
!ingest-only <group_id_or_alias>
!ignore <group_id_or_alias>
!reply-mode <group_id_or_alias> silent|mention|questions
!access <signal_number>
Reply modes:
silent: ingest only, never group-reply.mention: group-reply only when tagged.questions: group-reply only when tagged and the message has?.
Unauthorized users can DM why to get a non-sensitive access explanation.
Every newly stored inbound message is appended to:
exports/conversations.jsonl
exports/conversations.csv
These files are local operational exports and are ignored by git.
Use .env for high-level behavior:
BOT_SYSTEM_PROMPT=
BOT_PERSONA_FILE=The app always appends non-overridable privacy, retrieval, and threading rules after any custom prompt/persona.
docker-compose -p pf_signal_bot downDo not remove Docker volumes unless you intentionally want to delete local Signal/Postgres state.