Account Audit
Detect and remove deleted or abandoned Signal accounts from community groups.
Why This Matters
Section titled “Why This Matters”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
Detection Methods
Section titled “Detection Methods”sendReceipt (Recommended)
Section titled “sendReceipt (Recommended)”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.
Bot Command: !accountaudit / !audit
Section titled “Bot Command: !accountaudit / !audit”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)Recommended Workflow
Section titled “Recommended Workflow”- Scan:
!audit scan(or!audit scan-groupfor a quick check) - Review: Read the results. Check the unregistered count looks reasonable.
- Preview:
!audit removeto see exactly what would happen - Execute:
!audit remove confirmto remove unregistered users from all groups
Group Notifications
Section titled “Group Notifications”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).
Activity Feed
Section titled “Activity Feed”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
Example Output
Section titled “Example Output”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 removalRemoval 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 executeCLI Script
Section titled “CLI Script”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).
# From inside the Docker container or with SSH tunnel to signal-cli + postgresnpx ts-node src/scripts/account-audit.ts <command> [options]
# Commandsscan Scan for unregistered accounts (sendReceipt method)scan-trust Scan for trust failureslist Show cached scan result filesremove Preview removal from latest scanremove --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)Examples
Section titled “Examples”# Full scannpx ts-node src/scripts/account-audit.ts scan
# Scan with limit and CSV outputnpx ts-node src/scripts/account-audit.ts scan --limit 1000 --output csv
# Preview removalnpx ts-node src/scripts/account-audit.ts remove
# Execute removalnpx ts-node src/scripts/account-audit.ts remove --confirm
# List cached resultsnpx ts-node src/scripts/account-audit.ts listSSH Tunnel for Remote Use
Section titled “SSH Tunnel for Remote Use”To run the CLI script from your local machine against the production server:
# 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 terminalcd srcDB_HOST=localhost DB_PORT=5433 npx ts-node src/scripts/account-audit.ts scan --limit 10Cached Results
Section titled “Cached Results”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.
Safety Notes
Section titled “Safety Notes”- Always scan before removing. Never run blind removal.
- Review results first. The
removecommand withoutconfirmshows 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.
Technical Details
Section titled “Technical Details”Key Files
Section titled “Key Files”| File | Purpose |
|---|---|
src/src/utils/account-auditor.ts | Core library (shared by CLI + bot) |
src/src/scripts/account-audit.ts | CLI script |
src/src/bot/command-handler.ts | Bot command handler |
src/src/bot/signal-jsonrpc-client.ts | sendReceipt() RPC method |
Replaces
Section titled “Replaces”This tool consolidates and replaces the following standalone scripts in scripts/:
find-unregistered-users.js- sendReceipt-based detection + removalremove-failed-users.js- trust-based detection + removallist-failed-users.js- trust-based listingcheck-group-members.js- per-group trust check + removaltrust-all-users.js- bulk trust all userslist-group-signal.js- list group members
Architecture
Section titled “Architecture”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
SignalJsonRpcClientmethods andPostgresClient - CLI script: Uses raw TCP sockets and
pg.Pool