Skip to main content

BanBot - XMPP Multi-Room Ban Management Bot

PyPI Python GitHub Release Release CI Drone License: GPL-3.0

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 monotonic automated-protection arbitration; explicit admin moderation can still convert 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.

Verify the installed application and shared core without connecting to XMPP:

muc_banbot --version
# muc_banbot 3.2.0 (envs-xmpp 1.3.0)

For a structured 72-hour post-release observation checklist, see docs/production-observation.md.

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 [full] Show compact structured bot/runtime/XMPP/moderation/database health; full adds problem and inventory diagnostics
!tasks [all|full|failed|stale|restarting|restarted|problems] [scope <name>] Show shared task health, filtered/paged diagnostics, and watchdog state
!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/rooms list [joined/offline/problems] [all/page/last] List protected rooms with join/admin health using shared filters
!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, promote a tempban to permanent, or update an existing reason
!tempban <jid/nick> <10m/2h/1d> [comment] Add/update a temporary ban; explicit human moderation may convert permanent → temporary, while 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. Task and room inventories additionally support focused problems views; !status full expands diagnostics without dumping every healthy room/task into the default status.

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.

Overlapping automated protections are monotonic: a later protection may strengthen or extend an existing ban, but it cannot downgrade a permanent ban or shorten a temporary one. Distinct protection reasons are retained, while human and RTBL reasons remain authoritative. The short message-action cooldown suppresses equal/weaker duplicate actions but allows a stronger action to proceed, and observe-only matches do not consume that enforcement cooldown.

See docs/protections.md for all protection names, aliases, actions, arbitration rules, 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.

Metadata

Release files for muc-banbot 3.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for muc-banbot 3.2.0
File Size Uploaded
muc_banbot-3.2.0.tar.gz 418.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for muc-banbot 3.2.0
File Interpreter ABI Platform
muc_banbot-3.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 730.4 kB

Release files / muc_banbot-3.2.0.tar.gz

Download URL muc_banbot-3.2.0.tar.gz
Size 418.5 kB
Tags Source
SHA-256 checksum
How to use checksums
e08228019c3fd646a950b6e7bfa8fcb139423e6b329f317b2488ceb0af1d34a0
BLAKE2b-256 checksum
How to use checksums
fbe1540d13881c5052063d5899eb8d9f3ab0040f8e58f287af1b247516eac617
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 20, 2026.

Transparency log

Release files / muc_banbot-3.2.0-py3-none-any.whl

Download URL muc_banbot-3.2.0-py3-none-any.whl
Size 311.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
1acc25df7b7796aefd47a9a9461360c851ef30889cf5081a6286c8f39e2e1f35
BLAKE2b-256 checksum
How to use checksums
8a108c3916f44af4e198e32d3241a6f518ab8cfa711409a087b91b5b3e9f89d0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 20, 2026.

Transparency log

Release history Release notifications | RSS feed

3.5.1

2 release files

3.5.0

2 release files

3.4.0

2 release files

3.3.0

2 release files

This release

3.2.0 This release

2 release files

3.1.1

2 release files

3.0.0

2 release 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