maubot keeps the bot logic out of the Matrix bot
A plugin-based Matrix bot system.
At a glance
- What is it?
- maubot is a plugin-based Matrix bot system in Python where the interesting part, the behaviour, lives in plugins rather than in the bot itself. The core handles the protocol through mautrix, storage through Postgres or SQLite, and deployment through a Docker image that builds a management frontend with Node before switching to Alpine.
- Who is it for?
- Adopt maubot if you run a Matrix homeserver and want to add bot behaviour by writing plugins against a stable core, and if you value a management interface and a documented configuration over a single monolithic script.
- Can I use it commercially?
- Yes, with strict conditions. AGPL-3.0 is a network copyleft licence: if people use a modified version over a network, for example as a hosted service, you must offer them its source code under the same licence.
- Is it still maintained?
- Yes. The repository last received commits 1 day ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 25, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A core that does nothing interesting, on purpose
maubot is described in one line as a plugin-based Matrix bot system written in Python, and that word plugin carries the whole design.
Most bots are a single program: a Matrix client library, a database, a pile of commands and a webhook or two, all tangled together. maubot splits that. The core is the part that talks to the homeserver, keeps the state and exposes a management interface. The behaviour is plugins, loaded at runtime, each one a self contained unit that the user enables and configures.
The audience follows from the split. A homeserver operator who wants a bot with a handful of commands should not have to maintain a fork. A plugin author should be able to write one thing and have it work for every operator who installs it. The plugin list at plugins.mau.bot is the distribution channel, with a separate repository for adding to that list and a plugin wishlist repository for feature requests, so the ecosystem is organised around the core rather than around any one deployment.
The alternative approach is worth naming because it is what most people do by default. You write your own bot against the Matrix client library, you pick your own storage, and you deploy it however you like. That is less machinery to learn, and it is completely reasonable for a single purpose bot that will never be shared.
The trade-off is what maubot buys. A plugin written once runs on any installation, configuration is per plugin, and the management frontend gives a single place to see what is installed and configure it. What it costs is a system to run, a database to maintain and a licence to understand, which for a hobby deployment is real overhead.
mautrix does the protocol, and the rest of the stack is conventional
The requirements file is short and says what the project is built on:
mautrix>=0.21.1,<0.22
aiohttp>=3,<4
asyncpg>=0.20,<1
aiosqlite>=0.16,<1mautrix is the Matrix SDK, and the version range is pinned tightly to the 0.21 series, which tells you the project tracks a specific SDK generation rather than floating on the latest. aiohttp is the async HTTP client underneath it, and yarl handles the URL handling that goes with it.
The two database drivers are the interesting pair. asyncpg is an asynchronous PostgreSQL driver, and aiosqlite is the async SQLite equivalent, so the system offers a real choice of backing store rather than a single hard coded database. A multi user homeserver deployment would use Postgres, and a single user instance on a small machine can use a file.
Everything else is ordinary infrastructure. commonmark and ruamel.yaml handle Markdown and YAML, attrs provides the declarative class style, bcrypt covers password hashing, and packaging is used for version handling. The second group is the command line and templating layer: click for the CLI, colorama for Windows colour output, questionary for interactive prompts, and jinja2 for templating, which is what a plugin uses to render the text it sends back to a room.
The setup.py file reads requirements.txt directly into install_requires and parses optional-requirements.txt into extras_require, keyed on comment lines that start with #/. That file also sets python_requires to ~=3.10 and the classifiers list Python 3.10 through 3.13, and the development status is recorded as 4 - Beta.
Configuration in a file, state in a database, and a Docker image that ships both
The deployment story is visible in the Dockerfile, and it is the clearest part of the repository.
The image is built in two stages. The first stage is a Node 24 builder that copies the management frontend source and builds it:
FROM node:24 AS frontend-builder
COPY ./maubot/management/frontend /frontend
RUN cd /frontend && yarn --prod && yarn buildThe second stage is the runtime, alpine:3.23, and the built frontend is copied into it at the end. The reason for the split is that the frontend needs a Node toolchain to build and does not need one to run, and the runtime image is Alpine with Python and the native libraries.
The runtime image sets the environment and the data location:
ENV UID=1337 GID=1337 XDG_CONFIG_HOME=/data
VOLUME /dataSo the container runs as a fixed non root uid and gid, and all persistent state, which is the configuration and the database, lives under /data on a volume. The container copies the repository into /opt/maubot, copies maubot/example-config.yaml into the working directory as the starting configuration, and installs the docker/mbc.sh script as /usr/local/bin/mbc, which is the management command.
The choice of a user id inside the image, combined with a volume, is the sort of detail that matters in practice. A bind mount on a host directory will be owned by uid 1337 inside the container, so the host permissions have to line up.
The setup documentation has a page for a plain install and a separate one for Docker, and the basic usage page and the encryption page are linked from the README. The project also ships an examples/ directory with a config directory, a database directory, a helloworld plugin and its own licence and README, so the fastest way to see the intended shape of a plugin is to read that example rather than to reverse engineer the core.
Encryption support means libolm in the image
maubot supports encrypted Matrix rooms, and the documentation has a dedicated page for it. The implementation detail is visible in the Dockerfile, where the packages are grouped with a comment:
# encryption
py3-cffi \
py3-olm \
py3-pycryptodome \
py3-unpaddedbase64 \
py3-future \The olm library is the Matrix cryptography implementation, and cffi is how the Python bindings reach it. pycryptodome and unpaddedbase64 cover the cryptographic primitives that are not in olm itself.
This is the part of the dependency list that distinguishes a chat bot from a general purpose service. Talking to a homeserver over the client server API is straightforward. Talking to a room whose events are encrypted means implementing the olm machine, handling key sharing, and having a device identity per login, all of which the SDK handles but all of which have to be present in the runtime environment.
For an evaluator, the practical point is that the encryption support is not optional in the sense of being absent. The libraries are in the image, the documentation has a page for it, and the plugin author gets a working encrypted room by default rather than having to assemble it.
The other runtime groups in the image are the core dependencies and the plugin dependencies, and the difference between them is where the next limitation lives.
Plugin dependencies are installed by hand, and the project knows it
The third group of packages in the Dockerfile is the plugin dependencies, and it comes with an admission in the same file.
The packages installed for plugins include pillow for images, magic for file type detection, feedparser for feeds, dateutil for dates, lxml for XML, and semver for version comparison. Then there is a comment: a TODO to remove pillow, magic, feedparser, lxml, gitlab and semver when maubot supports installing dependencies.
The same limitation appears a second time in the build step, where dateparser, langdetect, pyquery, tzlocal and python-gitlab are pip installed alongside the requirements files, followed by another TODO saying those should also go once maubot supports installing dependencies.
So the current state is that plugin dependencies are a fixed list baked into the image. A plugin that needs a library outside that list has no supported path to get it. The project has clearly decided to get the mechanism working first and automate it later, which is a reasonable order, but it means plugin authors are effectively restricted to a set of libraries the maintainers have thought to include.
There is a second consequence worth naming for anyone writing plugins. Because the dependencies are not installed per plugin, the version of a library a plugin sees is the version in the image, shared by every plugin. That avoids version conflicts between plugins, and it also removes the ability for a plugin to ask for a specific version.
The examples/ directory in the repository is the place to see what a plugin looks like, with a helloworld directory, a config directory and a database directory alongside it.
AGPL-3.0, a beta classification and a 0.x release line
The licence is AGPL-3.0, and the setup.py classifier spells it out as the GNU Affero General Public License v3 or later, which matches the AGPLv3+ identifier used in the licence file at the repository root.
The AGPL matters more here than a permissive licence would, and it is a deliberate choice for a network service. The Affero clause is what makes it so: when you run a modified version of the program as a network service, the users of that service get the corresponding source of your modified version. For a bot platform where operators are encouraged to install third party plugins, that is the licence that keeps the ecosystem from forking into closed variants.
For an organisation that wants to run maubot internally with a private plugin, the practical question is whether the deployment triggers the network clause. That is a question for your own legal review, and this is not legal advice, but the shape of the obligation is worth understanding before you put it in production.
The maintenance picture is fine. The repository is not archived, the last push was on 2026-07-09, and the recent releases are v0.6.0 on 2025-11-17, v0.5.2 on 2025-05-05 and v0.5.1 on 2025-01-03. The project classifies itself as a beta in the setup.py classifiers, and it is on a 0.x version line, so interface changes between minor releases are still possible.
The tooling around the code is worth a note. Formatting is black with a line length of 99 and a target of py310, imports are sorted with isort using the black profile, and both run through a pre-commit configuration. There is a Dockerfile.ci for the continuous integration image, a GitLab CI configuration with a pipeline badge, and a GitHub directory, so the project runs both hosted CI systems.
Where to look, and where to ask
The README is a signpost rather than a manual, and the destinations are specific.
Setup and usage instructions are on docs.mau.fi, with quick links to the setup page, a separate setup page for Docker, a basic usage page and an encryption page. The homepage is mau.bot. Discussion happens in a Matrix room, the #maubot room on the maunium.net homeserver, which is an interesting choice for a Matrix project: the support channel is a Matrix room on a Matrix homeserver.
The plugin ecosystem has its own two entry points. The list of existing plugins is at plugins.mau.bot, and to add one you send a pull request to the plugins.maubot.xyz repository. The wishlist of plugins that people want is a separate repository of issues, which is where a plugin author looking for a project to build would look.
For a team evaluating this, the sequence that makes sense is short. Read the setup page to see what a running instance needs, read the basic usage page to see how plugins are configured, read the encryption page if your rooms are encrypted, then look at the helloworld example in the examples directory and the plugin list to see whether the functionality you want already exists.
One thing the README does not do is document, so do not expect to find it there: a per plugin dependency installation mechanism, a schema for plugin configuration validation, or a compatibility range between a plugin and a core version. The last of those is the one to watch. The core is on 0.x, the plugin list is curated by hand, and the version at which a given plugin breaks against a given core is something you will discover by testing rather than by reading.
Editorial conclusion
Adopt maubot if you run a Matrix homeserver and want to add bot behaviour by writing plugins against a stable core, and if you value a management interface and a documented configuration over a single monolithic script. Do not adopt it if your bot is a one-off integration, since running a homeserver-facing service is a much larger commitment than a webhook, and do not adopt it expecting plugins to manage their own dependencies, because the Dockerfile still installs the plugin libraries by hand and carries a TODO saying that support has not arrived yet. Before you deploy, read the setup and encryption pages on docs.mau.fi, choose between Postgres and SQLite deliberately, and confirm your Python version against the ~=3.10 requirement, since the project is still classified as beta and still on a 0.x release line.
Frequently asked questions
What is maubot and who is it for?
It is a plugin-based Matrix bot system written in Python. The core handles the Matrix protocol, storage and a management interface, while behaviour is added as plugins, so it is aimed at homeserver operators who want to extend a bot without forking it and plugin authors who want to write once and distribute.
Which databases can maubot use?
The requirements list both asyncpg, the asynchronous PostgreSQL driver, and aiosqlite, the asynchronous SQLite driver, so the backing store is a choice rather than a fixed decision.
How do I deploy maubot with Docker?
The Dockerfile builds the management frontend in a Node 24 stage and runs the bot in an Alpine 3.23 stage, with the state directory at /data on a volume and XDG_CONFIG_HOME set to it. The documentation also has a separate page for a plain, non Docker setup.
Can maubot talk in encrypted Matrix rooms?
Yes. The runtime image installs the olm library with cffi, pycryptodome, unpaddedbase64 and future under an encryption group, and the documentation has a dedicated encryption page.
What licence is maubot released under?
AGPL-3.0, recorded in the setup.py classifiers as the GNU Affero General Public License v3 or later, with the text in the LICENSE file at the repository root. The most recent release listed is v0.6.0 on 2025-11-17.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/maubot-maubot)Was this useful?