nio splits a Matrix client into a sans I/O core and an asyncio layer
A Python Matrix client library, designed according to sans I/O (http://sans-io.readthedocs.io/) principles
At a glance
- What is it?
- nio is a Python client library for the Matrix protocol built around sans I/O principles, with an encryption layer that needs libolm or vodozemac. It suits builders of bots and bridges who want typed events and full sync handling, and it is the wrong choice for anything needing cross-signing or key backup.
- Who is it for?
- Adopt nio when you are building a bot, bridge or service that must handle the full Matrix event stream with typed responses, and check first that the features you need are on the implemented list, since cross-signing, server-side key backup and user deactivation are marked as missing. Confirm the encryption story for your platform before committing, because the e2ee extra pulls in vodozemac and the older path needs libolm 3.x built as a C library.
- Can I use it commercially?
- Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
- 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 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The base layer does no network IO, which is the whole architectural bet
nio is described as a multilayered Matrix client library, and the layering is the design decision rather than an implementation detail. The underlying base layer does not do any network IO on its own. On top of that sits a full-fledged batteries-included asyncio layer using aiohttp. File IO happens only when end-to-end encryption is enabled, which matters for anyone who wants to reason about where bytes are read and written. The sans I/O approach means the protocol logic can be exercised without opening a socket, and a client can be driven by an arbitrary transport. The trade-off is that you must pick a layer. Someone wanting a full asynchronous client takes the upper one and gets sync handling, file transfer and a connection manager for free. Someone writing a test, a proxy or a server-side relay that needs to reason about raw events works against the lower one, where the events are data. The library is explicitly described as still a work in progress, so the boundary between the two layers is worth inspecting for the specific call you need.
Encryption is an optional extra with a system level dependency
End-to-end encryption is listed as supported, and the installation instructions show that it is not in the default package. A plain install has no encryption support at all. The e2ee build requires python-olm, which in turn requires the libolm C library at version 3.x, and the documentation gives the package names per platform: libolm-dev on Debian and Ubuntu, libolm-devel on Fedora, and libolm through brew on macOS. The Make sure version 3 is installed line is emphatic, because a mismatched libolm is the usual source of trouble. The current packaging metadata tells a more recent story than that older libolm path. The e2ee optional dependency group lists atomicwrites, cachetools, peewee and vodozemac, so the modern backend is vodozemac, a pure Python implementation, while the README text still describes the libolm route. Two encryption-related extras also sit outside the core: encrypted file uploads and downloads are supported, and file IO only occurs when E2EE is on.
A base install gives you aiohttp, h11, h2 and a JSON schema layer
The runtime dependencies listed in pyproject.toml are compact and tell you a lot about the shape of the client. aiohttp at 3.10 or newer provides the HTTP client, aiofiles handles asynchronous file access, h11 and h2 are the HTTP protocol implementations underneath, jsonschema at 4.14 or newer handles event validation, unpaddedbase64 and pycryptodome cover the cryptographic primitives, and aiohttp-socks adds SOCKS proxy support. The presence of jsonschema is the interesting one for anyone building on top: Matrix events are validated against the specification's schemas, which is how a client ends up with typed events rather than untyped dictionaries. That combination, an event type system validated against schemas plus a proxy option, is what separates a library meant for bots from a thin HTTP wrapper. The package version in the metadata is 0.26.0 and the supported Python range starts at 3.10, with classifiers running through 3.14.
The feature checklist marks three things as not done
The README carries a checklist of implemented and unimplemented features, and the three crosses are the most useful lines in it. Cross-signing support is not implemented. Server-side key backups, described as room key backup and Secure Backup, are not implemented. User deactivation is not implemented, and it is tracked as an issue. In-room emoji verification is also listed as missing, even though manual and emoji verification are both marked as supported, so the gap is specific to the in-room flow rather than to emoji verification as a whole. Everything else on the list is present: space parents and children, custom authentication types, threading, knocking, kick, ban and unban, typing notifications, message redaction, token based login, user registration, read receipts, live syncing, m.reactions and m.tags. For an engineer choosing a library, that list is the single most decision-relevant artefact in the project, and it is worth reading against your own requirement list before anything else, because three of the missing items sit directly in the security path.
Installing nio is one pip command, and the e2ee extra is a second
The base install is deliberately plain, and the README gives the command directly:
$ pip install matrix-nioThat install carries no end-to-end encryption support, which the documentation states right after the command. If you need it, install libolm first as described for your platform, then request the extra:
$ pip install matrix-nio[e2e]For a container based deployment, a docker image with the e2ee enabled version of nio is provided in the docker directory of the repository, which saves you from reproducing the native library step. A third option exists for distribution packaging: the repository contains a contrib directory with an Arch Linux PKGBUILD template, and the Makefile has an arch-git-pkg target that builds a source distribution with poetry, copies the PKGBUILD into dist and runs makepkg. The project also ships a uv.lock file, a tox.ini and a Makefile whose test target runs python3 -m pytest with benchmarks disabled, plus a typecheck target that runs mypy over the nio package.
The example files show the shapes of a real client
Six example scripts sit in the examples directory, and between them they cover the flows a new user has to learn. basic_client.py is the entry point, a client that connects and reads events. send_image.py covers uploading a file, which is the path that depends on the encryption layer. manual_encrypted_verify.py and verify_with_emoji.py show the two verification flows that the feature list marks as supported. restore_login.py deals with re-establishing a session. The examples README is the index. That selection is a useful signal about the library's shape. There is no example of a full interactive chat client, which is consistent with nio being aimed at bots, bridges and services rather than end users, and it means the examples give you protocols to copy rather than an application to run. The project documentation site collects further examples, including how others are using the library, and the full API reference lives on readthedocs.
Two licence files sit in the tree, and that needs reading
The packaging metadata points the licence field at a file called LICENSE.md, and that file is the one to read. The repository also contains an apache_license.txt at the top level, and the README badge links to LICENSE.md while displaying an ISC label. The hosting service reports the licence as a custom one that it could not classify, which is a signal that the terms do not match a standard identifier the service recognises. The practical advice is to open LICENSE.md and determine for yourself which terms apply, and to check which file the build actually ships, since the metadata names one of the two. This is not a legal opinion, just a practical note: a library that will be vendored into a larger application should have its licence confirmed before that happens, and a mismatch between the badge, the metadata and the files present is a reason to slow down and check rather than to assume the most permissive reading.
Where nio is the wrong tool
The missing features decide this for many projects, and they are not minor gaps. A client that needs cross-signing cannot use nio as it stands, and neither can one that needs to restore room keys from a server-side backup, which rules out many long-lived bots that must survive a device loss. User deactivation is absent too. Beyond those, the sans I/O split creates a second cost. If your application does not need live syncing and the batteries-included asyncio layer, you are carrying aiohttp and the full client machinery for a handful of API calls, and a much smaller HTTP wrapper would serve. The library is also a project without GitHub releases, so there is no tagged release stream to pin against; the version to track is the 0.26.0 recorded in pyproject.toml. Its own documentation calls the work in progress, and the last push was on 2026-10-20, so development is current but the API surface carries no compatibility promise from a release tag. Readers who need the missing security features should treat that as the deciding factor rather than the async conveniences.
Editorial conclusion
Adopt nio when you are building a bot, bridge or service that must handle the full Matrix event stream with typed responses, and check first that the features you need are on the implemented list, since cross-signing, server-side key backup and user deactivation are marked as missing. Confirm the encryption story for your platform before committing, because the e2ee extra pulls in vodozemac and the older path needs libolm 3.x built as a C library. Finish by reading LICENSE.md directly, because the project ships both that file and an apache_license.txt.
Frequently asked questions
How do I install nio with end-to-end encryption?
A plain pip install of matrix-nio has no end-to-end encryption support. For the e2ee build you need the encryption backend installed first, historically the libolm C library at version 3.x via libolm-dev, libolm-devel or brew libolm, and then you run pip install matrix-nio[e2e]. A docker image with e2ee enabled is provided in the docker directory.
What does sans I/O mean for nio?
nio is a multilayered Matrix client library whose underlying base layer does not do any network IO on its own, with a batteries-included asyncio layer using aiohttp built on top of it. The base layer lets you drive the protocol with an arbitrary transport.
Does nio support cross-signing and room key backup?
No. The README feature list marks cross-signing support and server-side key backups, also called Secure Backup, as not implemented, along with user deactivation and in-room emoji verification. Manual and emoji verification are listed as supported.
What Python version does nio require?
The packaging metadata sets requires-python to 3.10 or newer, with classifiers for Python 3.10 through 3.14. The recorded package version is 0.26.0.
What is nio licensed under?
The packaging metadata points the licence field at LICENSE.md, and the repository also contains an apache_license.txt. The README badge displays an ISC label, and the hosting service reports the licence as one it could not classify, so the terms in LICENSE.md are the thing to check directly.
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/matrix-nio-matrix-nio)Was this useful?