Skip to content

Account Audit

Detect and remove deleted or abandoned Signal accounts from community groups.

When users delete their Signal app, change phones without transferring their account, or abandon their account, the account becomes unregistered on Signal’s servers. These dead accounts:

  • Inflate group member counts with inactive users
  • Can cause delivery errors when the bot tries to message them
  • Make it harder to understand actual community size
  • May hold spots in groups with member limits

Sends a read receipt to each account. Signal’s server responds with UNREGISTERED_FAILURE for deleted accounts. This is the most reliable method because Signal definitively knows whether an account exists.

Calls trustIdentity on each account. Failures may indicate inactive accounts, but can also happen for active users with key changes (new device, reinstalled Signal). Less reliable - use sendReceipt when possible.

Admin-only command. Must be used in a group chat for audit trail visibility.

!audit Show help
!audit scan Scan all members (sendReceipt method)
!audit scan --limit 500 Scan with limit
!audit scan-group Scan current group only (faster)
!audit trust Scan using trust method
!audit report Show latest scan results
!audit remove Preview removal (dry run)
!audit remove confirm Execute removal (notifies affected groups)
!audit remove confirm --quiet Execute removal silently (no group notifications)
  1. Scan: !audit scan (or !audit scan-group for a quick check)
  2. Review: Read the results. Check the unregistered count looks reasonable.
  3. Preview: !audit remove to see exactly what would happen
  4. Execute: !audit remove confirm to remove unregistered users from all groups

By default, after removal each affected group receives a message explaining the cleanup:

Account Cleanup: 3 inactive Signal accounts were removed from this group. These accounts are no longer registered on Signal (the user deleted Signal or deactivated their account). This is a routine maintenance action to keep our groups up to date.

Use --quiet to suppress these notifications. The admin’s current group is always skipped (they see the full report instead).

All audit removals are logged to the admin activity feed at irregulars.io, including:

  • Who ran the removal
  • How many accounts were removed
  • How many group memberships were affected
  • Whether group notifications were sent

Scan result:

Account Audit Complete
Scanned: 2,301 members (43s)
Registered: 2,288 (99.4%)
Unregistered: 13 (0.6%)
Unregistered accounts:
d629a1b2... | 30 groups | (no name)
a1b2c3d4... | 12 groups | OldUser123
...
-> !audit remove -- preview removal
-> !audit remove confirm -- execute removal

Removal preview:

Removal Preview
13 unregistered accounts will be removed from groups:
d629a1b2... | (no name) | 30 groups
a1b2c3d4... | OldUser123 | 12 groups
...
Total: 13 users across ~150 group memberships
-> !audit remove confirm -- to execute

For scheduled or bulk operations, a standalone CLI script is available that connects directly to signal-cli (doesn’t need the bot running, just the signal-cli daemon).

Terminal window
# From inside the Docker container or with SSH tunnel to signal-cli + postgres
npx ts-node src/scripts/account-audit.ts <command> [options]
# Commands
scan Scan for unregistered accounts (sendReceipt method)
scan-trust Scan for trust failures
list Show cached scan result files
remove Preview removal from latest scan
remove --confirm Execute removal
# Options
--limit N Max users to scan (default: 5000)
--group-id ID Filter to specific group
--output FORMAT summary (default), csv, detailed
--delay MS Delay between checks (default: 50)
--dry-run Preview without executing
--confirm Required to execute removal
--cache-dir PATH Results storage (default: /app/data)
Terminal window
# Full scan
npx ts-node src/scripts/account-audit.ts scan
# Scan with limit and CSV output
npx ts-node src/scripts/account-audit.ts scan --limit 1000 --output csv
# Preview removal
npx ts-node src/scripts/account-audit.ts remove
# Execute removal
npx ts-node src/scripts/account-audit.ts remove --confirm
# List cached results
npx ts-node src/scripts/account-audit.ts list

To run the CLI script from your local machine against the production server:

Terminal window
# Open tunnels to signal-cli (7583) and postgres (5432)
ssh -L 5433:signal-bot-postgres:5432 -L 7583:localhost:7583 root@100.87.20.88
# In another terminal
cd src
DB_HOST=localhost DB_PORT=5433 npx ts-node src/scripts/account-audit.ts scan --limit 10

Scan results are saved to JSON files at /app/data/account-audit-YYYY-MM-DD-HHMM.json. The list and remove commands read from the latest cached file, so you can scan once and then review/remove without rescanning.

  • Always scan before removing. Never run blind removal.
  • Review results first. The remove command without confirm shows a preview.
  • sendReceipt is safe. It only sends a read receipt - it doesn’t modify any groups.
  • Removal is reversible in the sense that users can be re-added, but it does remove them from all groups immediately.
  • Rate limiting: The bot command scans at ~50ms delay per user. A scan of 2000 users takes about 2 minutes.
FilePurpose
src/src/utils/account-auditor.tsCore library (shared by CLI + bot)
src/src/scripts/account-audit.tsCLI script
src/src/bot/command-handler.tsBot command handler
src/src/bot/signal-jsonrpc-client.tssendReceipt() RPC method

This tool consolidates and replaces the following standalone scripts in scripts/:

  • find-unregistered-users.js - sendReceipt-based detection + removal
  • remove-failed-users.js - trust-based detection + removal
  • list-failed-users.js - trust-based listing
  • check-group-members.js - per-group trust check + removal
  • trust-all-users.js - bulk trust all users
  • list-group-signal.js - list group members

The AccountAuditor class uses dependency injection - it takes generic rpcSend and dbQuery functions, so the same logic works with:

  • Bot command: Uses the bot’s SignalJsonRpcClient methods and PostgresClient
  • CLI script: Uses raw TCP sockets and pg.Pool