Skip to main content

BanBot - XMPP Multi-Room Ban Management Bot - calver / Build Status

BanBot is an XMPP bot for centralized ban management across multiple MUC rooms (Multi-User Chat).

It provides admin-room based moderation, protects configured MUCs from unwanted users, and supports temporary bans, domain bans, ban synchronization, audit logging, ignorelists, health checks, protections, RTBL/PubSub integration, and optional OMEMO support for encrypted commands and replies.


Features

  • 🛡️ Central admin room for all administrative commands
  • 🧩 Dynamic addition/removal of protected rooms
  • 🔒 Optional OMEMO support: encrypted commands receive encrypted replies
  • ❌ Ban, temporary ban, unban, banlist, bansearch, why, and redaction commands
  • 🌐 Domain-based bans (*.domain.tld) to ban all users from a domain
  • ⏱️ Automatic temporary ban expiration with human-readable durations
  • 📊 Smart duplicate ban handling with automatic conversion between permanent and temporary bans
  • 🐞 Nick-only ban support with best-effort JID upgrade when the user rejoins
  • ⚠️ Admin/owner protection for direct, nick-based, and domain-based bans
  • 🚫 Global ignorelist/whitelist for exact JIDs and domain-based ban protection
  • 📦 Startup and manual synchronization of room bans/admins
  • 🏥 Health checks, reconnect awareness, admin-right monitoring, and dynamic !status
  • 🛡️ RTBL subscriptions via PubSub for SHA-256 JID hashes and plaintext domain bans
  • 🔄 Periodic RTBL refresh with quiet/no-change behavior and snapshot reconciliation
  • ♻️ RTBL snapshot reconciliation with stale local ban cleanup
  • 📡 Optional own RTBL publish feed for local bans
  • 🧾 SQLite audit log and structured JSON event logs
  • 💾 CSV import/export with managed safety backups
  • 🗄️ Managed ZIP backup archives with manifest, restore command, verification, and automatic startup backups
  • ✅ Startup/runtime config validation with safe !reloadconfig
  • 🚦 Rate limiting for public protected-room commands
  • 🧯 Protections for flood spam, first-message media, mention limits, wordlists, join waves, trusted reporters, and policy-change notifications
  • 📜 Optional public room policy text via !rules / !policy
  • ⬆️ Optional GitHub release checks
  • 🖼️ Avatar/vCard support via XEP-0054, XEP-0084, and XEP-0153
  • 🧪 Extensive pytest suite, coverage, property tests, mutation-testing support, and Drone CI

Installation / Quickstart

Requires Python 3.12+. The project is developed and tested with Python 3.13.

Recommended hardened systemd deployment

Keep the application checkout separate from mutable configuration and data:

/srv/adminbot/muc_banbot/       repository + virtualenv
/etc/muc_banbot/config.py       runtime configuration
/var/lib/muc_banbot/            database, backups, exports and OMEMO state

Bootstrap the service account and checkout, pin a tagged release, then use the preservation-first deploy helper:

sudo useradd -m -s /bin/bash adminbot -d /srv/adminbot
sudo -u adminbot git clone https://git.envs.net/envs/muc_banbot.git /srv/adminbot/muc_banbot
cd /srv/adminbot/muc_banbot

git fetch --tags
LATEST_TAG="$(git tag --sort=-v:refname | head -n1)"
git checkout "$LATEST_TAG"

./scripts/deploy.sh install --dry-run
sudo ./scripts/deploy.sh install

The deploy frontend uses the shared envs-xmpp operations layer. On a fresh checkout it bootstraps the exact deployment-tooling version into $XDG_CACHE_HOME/envs-xmpp/deploy/ (or ~/.cache/envs-xmpp/deploy/) before continuing; no manual pre-install is required. ENVS_XMPP_DEPLOY_SOURCE can point to a local checkout or wheel for pre-release testing.

On a fresh install the helper creates /etc/muc_banbot/config.py once, with absolute mutable paths below /var/lib/muc_banbot, and then stops so credentials can be edited safely. Hardened deployments must keep DB_FILE, DB_BACKUP_DIR, EXPORT_DIR, and OMEMO_STORAGE_FILE below the configured data directory; old relative source-tree paths are rejected by deploy.sh check. The expected permission baseline is /etc/muc_banbot 0750, config.py 0600, and /var/lib/muc_banbot 0700, owned by the service user/group. Rerun sudo ./scripts/deploy.sh install after editing the config. Existing config, database/data and systemd unit files are never overwritten automatically. A bare ./scripts/deploy.sh only prints help.

Useful read-only checks:

./scripts/deploy.sh status
sudo ./scripts/deploy.sh check

The recommended contrib/muc_banbot.service uses Type=notify, WatchdogSec=60, ProtectSystem=strict, disables Python bytecode writes in /etc, and grants write access only to /etc/muc_banbot and /var/lib/muc_banbot. See docs/deployment.md for the complete deployment and migration notes.

PyPI installation

BanBot is also published on PyPI as muc-banbot. For local testing, development environments, or non-systemd installs it can be installed directly into a virtualenv:

python3 -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip
pip install muc-banbot

# Optional OMEMO support:
# pip install "muc-banbot[omemo]"

python -m pip show muc-banbot

To run the wheel directly, create an operator config.py in the working directory (or point MUC_BANBOT_CONFIG at one). The installed sample can be copied without a source checkout:

python - <<'PY'
from pathlib import Path
import config_sample

Path("config.py").write_bytes(Path(config_sample.__file__).read_bytes())
PY
chmod 600 config.py
$EDITOR config.py
muc_banbot

The wheel includes the default avatar as a packaged read-only asset, so the default AVATAR_PATH = "avatar.png" also works outside a source checkout. An existing working-directory avatar.png still takes precedence, and configured subpaths or absolute paths keep their historical operator-controlled semantics. The PyPI package does not create /etc/muc_banbot, /var/lib/muc_banbot, or a systemd unit; the tagged Git checkout plus ./scripts/deploy.sh remains the recommended production deployment path.

Legacy/source-tree installation (still supported)

The historical installation layout remains supported for operators who prefer manual control or are not ready to migrate existing systems:

sudo useradd -m -s /bin/bash adminbot -d /srv/adminbot
sudo su - adminbot

cd /srv/adminbot
git clone https://git.envs.net/envs/muc_banbot.git
cd muc_banbot

git fetch --tags
LATEST_TAG="$(git tag --sort=-v:refname | head -n1)"
git checkout "$LATEST_TAG"

python3 -m venv venv
source venv/bin/activate
python -m pip install --upgrade pip
pip install -e .

# Optional OMEMO support:
# pip install -e ".[omemo]"

install -m 0600 config_sample.py config.py
$EDITOR config.py

muc_banbot

In this mode relative paths such as DB_FILE = "banbot.db" and DB_BACKUP_DIR = "data/backups" continue to resolve below the checkout. The legacy python muc_banbot.py launcher also remains supported. For systemd, use contrib/muc_banbot-legacy.service or keep your existing unit. MUC_BANBOT_CONFIG remains available for custom layouts.

main is the development branch. Production deployments should use stable vX.Y.Z release tags.

Updating to a New Release

The deploy helper is the recommended update path:

cd /srv/adminbot/muc_banbot
./scripts/deploy.sh update --dry-run
sudo ./scripts/deploy.sh update
# Explicit release when desired:
# sudo ./scripts/deploy.sh update --to v2.6.4

It refuses tracked local Git modifications, never deploys main automatically, queries remote release tags without bulk-overwriting local tags, fetches only the selected stable tag, asks separately before stopping/starting the service, and creates a consistent SQLite pre-update backup while the service is stopped. For legacy source-tree layouts it additionally protects operator files inside the checkout across the tag switch.

The previous fully manual update workflow remains supported:

cd /srv/adminbot/muc_banbot
git fetch --tags
LATEST_TAG="$(git tag --sort=-v:refname | head -n1)"
git checkout "$LATEST_TAG"
source venv/bin/activate
pip install -e .
# Optional: pip install -e ".[omemo]"

sudo systemctl restart muc_banbot

If release notes mention new configuration options, compare the active config with config_sample.py and add settings you want to customize.


Minimal Configuration

Copy config_sample.py to config.py and set at least:

JID = "adminbot@example.org"
PASSWORD = "secret"
RESOURCE = "service"
ADMIN_ROOM = "admin@conference.example.org"
NICK = "BanBot"
DB_FILE = "banbot.db"

Most runtime settings can be reloaded with !reloadconfig. Startup-only settings such as the bot account/room identity, database path, RTBL enable/publish setup, and OMEMO setup require a restart.

See docs/configuration.md for the full configuration reference.


Important Commands

Examples assume the default command prefix !.

Command Description
!help [all|page|last] / !help <command> Show available commands or focused help for every command topic, including subtopics such as room invite and rtbl publish
!status Show bot health, uptime, rooms, bans, RTBL, DB state, and protection status
!tasks [all|failed] Show supervised background workers, restart/backoff state, restart counts, and runtime/systemd watchdog health
!config [all|page|last] / !config show [all|page|last] Show active configuration grouped in config_sample.py section order; secrets are hidden
!config search/find <query> Search config option names and displayed values
!config diff [all|page|last] Show current values that differ from config_sample.py defaults
!config set <KEY> <value> Change a runtime-writable configuration value
!config unset <KEY> Reset a runtime-writable configuration value to config_sample.py default
!reload / !reloadconfig Validate and reload runtime configuration
!restart confirm Stop the bot so a supervisor can restart it
!checkupdate / !updatecheck Check whether a newer release is available
!whoami Show your affiliation, role, and permissions
!audit [all/page/last/query] Show audit log entries
!backup Create a managed full ZIP backup archive
!backup list [all/page/last] List managed full backup archives
!backup show <file/latest> Inspect one managed backup archive
!backup verify <file/latest> Verify a managed backup archive
!backup delete/remove/del/rm <file/latest> Delete a managed full backup archive
!restore <file/latest> confirm Restore a managed full backup archive
!room add <room> Add a protected room
!room remove/delete/del/rm <room> Remove a protected room
!room list [all/page] List protected rooms with join state and bot affiliation
!room rejoin <room/all> Retry joining one or all protected rooms
!room invite list [all/page/last] List pending room invites
!room invite accept/decline/remove/delete/del/rm <id> Accept or decline a pending room invite
!policy / !rules show/set/clear/delete/remove/enable/disable Manage public room policy text
!ban <jid/nick/domain> [comment] Add a permanent ban or update an existing ban reason
!tempban <jid/nick> <10m/2h/1d> [comment] Add or update a temporary ban; omitted comments preserve the old reason
!unban <jid/nick/domain> Remove a ban
!redact <jid> [reason] / !redact id ... / !redact cleanup Redact indexed messages or clean old redaction index entries
!protections list [all/page/last] List protection enabled/disabled and observe state
!protection enable/disable <name> Toggle a protection
!protections <name> config/set Show or edit one protection config
!report <nick/jid> [reason] Trusted reporter command when enabled
!banlist / !blacklist [all/page/last] Show active bans
!banlist rtbl / !blacklist rtbl [all/page/last] Show raw RTBL hash/domain entries
!bansearch <query> [all/page/last] Search bans by target, issuer, comment, or RTBL reason
!why <nick/jid> Explain why a user is banned
!ignore [list/all/page/last] Show the global ignorelist
!ignore add/remove/delete/del/rm <jid/domain> Manage protected exact JIDs and domains
!whitelist [list/all/page/last/add/remove/delete/del/rm] Alias for !ignore ...
!sync Rejoin rooms, verify admin rights, and enforce active bans
!syncadmins Update admin list from the admin room
!syncbans Sync bans from rooms into the database and enforce them
!omemo status Inspect OMEMO readiness and storage state
!omemo devices List visible admin-room recipients and local storage hints
!omemo reset [confirm] Rotate local OMEMO storage after confirmation
!rtbl list [all|page|last], !rtbl add/delete/remove/refresh Manage RTBL subscriptions
!rtbl publish status/sync Manage the bot's own RTBL publish feed
!export [list/show/delete/remove/del/rm] Manage CSV ban exports
!import <file> [dryrun] Import bans from CSV with validation and optional dry-run

For paginated commands, the standalone all argument disables paging and prints the complete result set. Examples: !audit all, !banlist all, !banlist rtbl all, !bansearch all spam, !ignore list all, !whitelist all, and !room list all.

Full command reference: docs/commands.md.


Room Invite Service

When ROOM_INVITES_ENABLED=True, BanBot can receive MUC invites for potential protected rooms. Invites are announced in the admin room and must be accepted or declined with !room invite commands. Pending invites older than ROOM_INVITE_MAX_AGE_DAYS are expired automatically; set it to 0 to keep them indefinitely. BanBot does not auto-join invited rooms.

Message Redaction

Optional redaction support indexes room-assigned stanza IDs for messages BanBot sees in protected rooms. Message bodies are not stored. Admins can redact all known messages from a bare JID with !redact <jid> [reason] or target a specific stanza ID with !redact id <room_jid> <stanza_id> [reason]. If a server applies a moderation request without returning a usable IQ result or live confirmation, BanBot verifies the resulting moderation tombstone through the room's MAM archive before counting the request as successful. Servers without verifiable MAM results remain reported as Unconfirmed rather than failed.

See docs/commands.md and docs/configuration.md.

Protections

BanBot includes an optional protection system for common MUC spam and abuse patterns. Protections can be listed, enabled, disabled, and tuned at runtime from the admin room:

!protections list all
!protection enable flood
!protections flood config
!protections flood set max_messages 10

Currently available protections cover flood spam, repeated/similar messages, first-message media spam, excessive mentions, monitored words from new joiners, join waves, trusted reporter workflows, and policy-change notifications.

Use conservative settings first and enable individual protections per need. For active rooms, notify or short tempban settings are useful while tuning thresholds; stronger actions such as permanent bans should only be enabled after the behavior is verified for your community.

See docs/protections.md for all protection names, aliases, actions, configuration keys, and operational notes.

OMEMO

BanBot supports optional OMEMO replies. OMEMO dependencies are not required for normal plaintext operation. If OMEMO_ENABLED=True but the optional Python/system libraries are missing, BanBot starts with OMEMO disabled and logs a clear warning.

The behavior is dynamic:

plaintext command  -> plaintext reply
OMEMO command      -> OMEMO reply

Encrypted MUC replies are sent to current occupants with visible real JIDs as far as possible. Occupants with unusable OMEMO devices are skipped; plaintext fallback is controlled by configuration. Admins can inspect local OMEMO state with !omemo status, show current visible admin-room OMEMO recipients with !omemo devices, and rotate the local OMEMO store with !omemo reset confirm when the bot identity changed or devices got stale. !omemo devices also shows conservative local storage hints, but those hints are diagnostic only and may be stale.

See docs/omemo.md.


RTBL / PubSub

BanBot can subscribe to RTBL PubSub nodes containing SHA-256 bare-JID hashes and plaintext domain bans. Successful refreshes reconcile local RTBL cache state with the current node snapshot. Removed RTBL items are cleaned up locally, and stale issuer=rtbl bans are automatically unbanned.

!rtbl add xmppbl.org muc_bans_sha256
!rtbl add xmppbl.org spam_source_domains
!rtbl refresh
!banlist rtbl all

BanBot can also publish local non-RTBL bans to its own RTBL feed.

See docs/rtbl.md and docs/rtbl_pubsub-setup.md.


Tests and CI

Install dev dependencies and run tests:

python3 -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install -r requirements-dev.txt
pytest

Run with coverage:

pytest --cov=banbot --cov-report=term-missing

The test suite also includes Hypothesis-based property tests for pure helper logic such as duration parsing, human-readable time formatting, JID/domain normalization, RTBL utilities, paging helpers, and ban-target normalization.

Drone CI runs the offline pytest suite with coverage on pushes and tags to main. Live XMPP/Prosody and OMEMO integration tests are opt-in and skipped by default.

See docs/testing.md for the full testing workflow and tests/README.md for the test-suite layout.


Documentation

The full documentation is split into focused guides. Start with the documentation index, or jump directly to a topic:


Security Notes

  • The bot account must have admin/owner rights in every protected room.
  • The admin room is the single source of truth for command permissions.
  • Admins/owners are protected from manual, nick-based, domain-based, and RTBL-applied bans.
  • Domain bans such as *.domain.tld reject overly generic targets such as *.com.
  • CSV imports create a managed full backup before writing data, but dry-runs do not create backups or change the database.
  • The ignorelist protects exact JIDs from all bans and domains from domain-based/RTBL domain matches.
  • If RTBL publishing is enabled, ensure the configured PubSub nodes are not writable by arbitrary users.

Moderation inspection and editing

  • !baninfo <target> shows the complete current ban metadata.
  • !history <target> [all|page|last] shows the audit-backed moderation history.
  • !banedit <target> reason <text> changes a reason.
  • !banedit <target> duration <duration> resets a tempban from now.
  • !banedit <target> extend|reduce <duration> adjusts a tempban.
  • !banedit <target> permanent converts a tempban to a permanent ban.
  • !banedit <target> temp <duration> converts or resets a ban to a tempban.
  • !banedit <nick> jid <user@domain.tld> converts a nick-only ban into a JID ban.

Protection observe mode can be enabled with !protections <name> observe on. Matching events are announced and audited, but no kick, ban, redaction, warning, or lockdown is applied. Disable it with observe off or enforce. !protections list marks action-capable protections with [observe] and the notification-only PolicyChangeNotification with [notify-only]; attempting to enable observe mode for a notification-only protection is rejected.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

muc_banbot-3.0.0.tar.gz (359.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

muc_banbot-3.0.0-py3-none-any.whl (271.4 kB view details)

Uploaded Python 3

File details

Details for the file muc_banbot-3.0.0.tar.gz.

File metadata

  • Download URL: muc_banbot-3.0.0.tar.gz
  • Upload date:
  • Size: 359.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for muc_banbot-3.0.0.tar.gz
Algorithm Hash digest
SHA256 c4674517bcd3931aa2f3042bf61c074d682b6a208004d2644a2ee024eab6f314
MD5 475d94c81e626df1f00a95066547e106
BLAKE2b-256 0b5a159ece63542c0cf2b29c02320a73bef5a15bc6df3408eb95c3959098e492

See more details on using hashes here.

Provenance

The following attestation bundles were made for muc_banbot-3.0.0.tar.gz:

Publisher: release.yml on envs-net/muc_banbot

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file muc_banbot-3.0.0-py3-none-any.whl.

File metadata

  • Download URL: muc_banbot-3.0.0-py3-none-any.whl
  • Upload date:
  • Size: 271.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for muc_banbot-3.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 6c711b9b0a60cfdc85dc71fa82a6a32665478ff16d1cf649a818ea585a9c210a
MD5 44f3697b5c629e235a52ed9d23e06caf
BLAKE2b-256 42fbb8ceff60f035d9d189f3c2421a47d167e3f76f087f7227d77fc4956ad14c

See more details on using hashes here.

Provenance

The following attestation bundles were made for muc_banbot-3.0.0-py3-none-any.whl:

Publisher: release.yml on envs-net/muc_banbot

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

3.0.0 This release

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page