XMPP
XMPP is more than 20 years old now, and I have been using it
while looking for ways to get distributed messaging. New
protocols and technologies are fun, but XMPP has good
documentation, works, and has plenty of software, so I find it
sufficient for messaging.
Its specifications (see RFC 6120, RFC 6121, RFC 7622,
and extensions) are well-written and thought through, a small
core with optional extensions is a nice approach, there is
usable software, there are users. Though the situation with
software could have been better. The protocol is not perfect
either, and the "What's wrong with XMPP? (And how we can fix
it?)" slides summarise it rather well.
Desirable features
XEP-0443: XMPP Compliance Suites 2021 lists rather useful and
important XEPs; I'd like to focus on some of those, and a few
more here. My preferences and requirements change over time, as
does this section.
- Message handling acknowledgements
- XEP-0198, needed for reliable message delivery.
- Message carbons
-
Some users tend to switch between resources (clients,
connections, or devices) frequently, leading to a confusion
caused by messages sent to a wrong resource altogether, and/or
incomplete message history in some of the client
instances. Message carbons (XEP-0280) help to solve that by
delivering incoming and outgoing messages to all the connected
resources.
- Client authentication
-
SASL EXTERNAL authentication mechanism is needed for key-based
authentication, which is not an essential feature, but nice
and handy.
-
SCRAM is fine for password-based authentication, and required
by the current standard, but not every client supports it.
- Reusability
-
Many implementations are not easily reusable from common
languages, so that's another aspect I'm paying attention
to. Ideally, there should be a C API and a sensible way to
hook it up to an external event loop.
- End-to-end encryption (and authentication)
-
The desired properties here are debatable. Forward secrecy may
be useful, although it adds complexity, which is a barrier to
actually using encryption at all (one of those "perfect is the
enemy of good" cases). Plausible deniability may be useful in
some scenarios (not against governments or courts, but
possibly against blackmail), though also adds
complexity. Efficient encrypted multi-user chats are
challenging; with a large enough group of users it is
increasingly likely that one of them would leak the data
anyway, though it may still be better than constant and
certain exposure.
-
OpenPGP (obsolete XEP-0027, newer XEP-0373 and XEP-0374),
which is commonly used for other purposes, has nice software
such as gpg-agent, supported by smart cards, has
infrastructure for key distribution. No forward secrecy or
plausible deniability baked in, and a relatively high
overhead, but still "pretty good privacy", as the name
says. Key distribution is tricky and awkward, but it always
is.
-
OTR (deferred XEP-0364), which is generic, IM-oriented, and
seemingly nice, but has (and leads to) annoying issues,
partially caused by nuances of XMPP resource handling by
clients. Leads to message loss all too often, with different
client combinations. But supports forward secrecy and
plausible deniability.
-
OMEMO (deferred XEP-0384), which provides forward secrecy and
deniability like OTR, offline and multi-user messages like
OpenPGP, using a relatively new double ratchet algorithm
developed for Signal, with some vendor-specific bits in the
XEP (and its homepage is just a single page with little
information on the Conversations website). The properties are
nice, but no standalone specification or white paper in sight
(it just relies on Signal protocol's specification). But there
is a handy OMEMO integration tracker. Though it seems that the
client incompatibilities lead to message loss without
user-visible traces at the moment, even after attempts to
disable OMEMO; the kind of thing that draws users away from
XMPP, not just encryption.
-
MLS will probably be standardized in the future, likely there
will be a XEP, and hopefully it would solve the issues present
in other options. Although it is going to be quite complex.
-
Sometimes I think that maybe shared secret cryptography with
manually managed keys would be a fitting option for IM: it
would simplify implementations, trust chains normanlly aren't
used anyway, plausible deniability is desirable, a safe
channel is needed for key exchange either way (though transfer
of shared secrets would alter the requirements:
confidentiality is needed in a basic case, in addition to
authentication). And more complex protocols can be employed
separately to establish those secrets automatically.
- Encrypted voice calls
-
Jingle calls with DTLS-SRTP. RTP alone would suffice over
IPsec or other encryption at lower levels, but it is rarely
available.
XMPP Protocol Guidelines provide a nice summary of useful features as
well.
Implementations
Below is a summary, based on both descriptions/documentation and
source code of the implementations (since rather often it is
either not documented, or the documentation is imprecise). The
list started with the software (clients, servers, libraries)
I've either used or considered using (with some implied
constraints, such as running on Linux, aiming IM, desktop
computers, and not web interfaces), so it is neither complete
nor general, and libraries are listed together with clients
using them. Maybe it will grow though.
Implementation |
SCRAM |
EXTERNAL |
Acks |
Carbons |
C API |
Encrypted calls |
Language |
libpurple | + | - | + | + | + | - | C |
Prosody | + | + | + | + | n/a | + | Lua |
jabberd2 | + | - | - | - | n/a | ? | C |
bitlbee | + | - | - | + | n/a | - | C |
weechat+jabber.py/xmpppy | - | - | ? | - | n/a | - | C/Python |
mcabber/loudmouth | - | - | - | - | + | - | C |
Conversations | + | + | + | + | n/a | + | Java |
ChatSecure/XMPPFramework | + | - | + | ± | - | ? | Java |
Profanity/libstrophe | + | + | + | ± | + | - | C |
Psi/iris | + | ? | + | + | - | - | C++ |
Vacuum-IM/eyeCU | + | - | - | + | n/a | - | C++ |
pontarius-xmpp | + | - | - | - | - | - | Haskell |
Gajim/nbxmpp | + | + | + | + | - | - | Python |
Dino | + | ? | + | + | n/a | + | Vala |
gloox | - | + | + | + | - | - | C++ |
Swift/Swiften | + | + | + | + | - | ? | C++ |
SleekXMPP/slixmpp | + | + | + | + | - | - | Python |
rexmpp | + | + | + | + | + | + | C |
Kaidan/QXmpp | + | - | + | + | - | ? | C++ |
And brief comments:
- Prosody
-
Has community modules, which should be enabled explicitly.
Fortunately, it is nicely extensible, and there are plenty of
modules. That's the server I'm using currently.
- jabberd2
-
Implements a very dated version of XEP-0198 (about 10 years
old; it didn't even have a "draft" status back then);
reported, but it also doesn't seem to be under active
development, and has various other issues.
- ejabberd
-
Not included into the table above. The list of XEPs supported
by ejabberd is fairly complete, but I remember it consuming
too much memory for a small server, and generally it resembles
enterprise software.
- libpurple (Pidgin, bitlbee, etc)
-
Doesn't focus heavily on XMPP, so lacks many features. I
submitted a patch for XEP-0198 there (included since version
2.14), but just for acknowledgements; stream resumption and
proper stream closing are tricky (closing can't be done
reliably, since the generic API relies on immediate
disconnects when requested). And while C API is provided, it
relies on Glib main loop, callbacks, C structures, Glib
signals – making it rather hard to write bindings for other
languages.
- bitlbee's own XMPP implementation
-
Seems to have even more limited SASL support than that of
libpurple, and also has issues similar to those of libpurple,
caused by its API (which bitlbee reuses for its implementation
as well): XMPP streams aren't getting closed properly there
either, potentially leading to message loss in conditions
where it shouldn't happen.
- weechat's jabber.py
-
Uses a Python library (xmpppy, which had no releases for a
while, but is maintained again), which implements a
now-obsolete RFC even for the core; there are just PLAIN and
DIGEST-MD5 authentication mechanisms (SCRAM is required to be
available for authentication by the current specification).
- mcabber's loudmouth library
-
Doesn't seem to support client certificates, or XEP-0198, or
SCRAM.
- Emacs's jabber.el
-
Uses sasl.el, but that doesn't include EXTERNAL. Also no
XEP-0198. And it looks abandoned, too.
- Conversations
-
An Android client in Java which I've checked out of curiosity,
does support client certificates and XEP-0198, DTLS-SRTP
calls, and many other things. It also has
documented observations on implementing XMPP. Though has some
warts, such as long if-else-if chains, disconnecting
after
Integer.MAX_VALUE
of stanzas is reached,
and apparently more. I use this one on Android, and usually
recommending it, though as of 2024, its website is blocked in
Russia, along with its XMPP server.
- ChatSecure's XMPPFramework
-
Doesn't seem to support SASL EXTERNAL, but supports
XEP-0198. But it is just for mobile devices (and just for iOS,
as of recently). Carbons are implemented in ChatSecure, but
not in XMPPFramework.
- Profanity/libstrophe
-
Both look nice at a glance, libstrophe supports SASL EXTERNAL
since version 0.11, and XEP-0198 since version 0.12
(while XEP-0198 implementation was stuck between libstrophe
and Profanity for more than 5 years by 2021). Message carbons
are implemented in profanity. It almost checks all the boxes I
wanted back in 2020, but the API is still synchronous.
- Psi/iris
-
A scary mass of C++, but relatively feature-rich.
- Vacuum-IM and eyeCU
-
Also in C++, with SASLFeatureFactory and such. SCRAM support
was added in 2019. No XEP-0198 in sight.
- pontarius-xmpp
-
A Haskell client library, but checked it at once: there are
just PLAIN, DIGEST-MD5, and SCRAM-SHA-1. No XEP-0198, and
apparently it is tricky to add there.
- Gajim/nbxmpp
-
Supports plenty of XEPs, including 0198, and various SASL
mechanisms, apparently including EXTERNAL. OTR and OMEMO are
available via plugins, and OpenPGP – by default. Pretty good
overall, apart from UI (which is actually fine, just not
Emacs), and no apparent way to hook it into Emacs. There is a
D-Bus interface, though it doesn't include events such as new
message receiving.
- Dino
-
Supports XEP-0198, OMEMO; no SASL EXTERNAL or OTR in sight,
though supports OMEMO and OpenPGP. The UI is very simplified,
with no XML console and with just glib-based logging, but
probably it is fine in some cases, and it is relatively
feature-rich. Supports calls with DTLS-SRTP. As of 2023, I use
it as an alternative desktop client.
- gloox
-
A C++ library without C API (hence not easily reusable
directly): supports a bunch of XEPs (including 0198), as well
as SASL EXTERNAL, but no SCRAM (which is required by the
standard).
- Swiften
-
Also a C++ library without C API, claims to be pretty good,
seems to support XEP-0198, as well as SASL EXTERNAL and
SCRAM-*. Much more of C++ than I'd be comfortable with, and
apparently not much of documentation (had to check sources to
find out what it supports – as with many client programs, but
libraries are expected to provide better docs), but looks good
functionality-wise.
- SleekXMPP and slixmpp
-
A Python library and its fork: also not reusable as a library
from arbitrary programs, but supports EXTERNAL and SCRAM
methods, and many pluggable XEPs. Used by Poezio.
- Telepathy's Gabble
-
While not included into the comparison (and probably wouldn't
look well there: seems to lack at least XEP-0198, and PRs
hanging with no activity for months), it is noteworthy for its
reusability via a D-Bus interface. There is a related wocky
library, though I am not sure what it does or supports.
- Kaidan/QXmpp
-
Another feature-rich C++ library (and a client based on it),
but no C API, and depends on Qt (including its event loop,
apparently).
Improving the software
As it often happens, the existing implementations are imperfect,
and their architectures are restrictive, yet there are too many
of them – leading to a lot of effort duplication, and multiple
half-baked implementations.
In this situation it does not seem wise to make new clients,
libraries, or protocols, and perhaps more effort should be put
into incremental improvement – which may be hard and boring, but
still better than poorly reinventing the wheel yet again.
Although as of 2020, I started writing rexmpp, a reusable XMPP
library providing an asynchronous C API and often-missing but
important functionality. By 2024, I am probably still its only
user, but it is an interesting experience, motivating to study
various aspects of the protocol and related technologies more
thoroughly.
Server configuration
I'm using Prosody, since it supports plenty of extensions and
doesn't require too many resources, and generally nice/handy to
use. In addition to all the "recommended" and "nice to have"
modules in the configuration, I'm enabling "proxy65" to get
reliable file transfer (for clients who are behind NAT)
with XEP-0234: Jingle File Transfer (XEP-0260: Jingle SOCKS5
Bytestreams Transport Method; ICE-TCP would have worked as well,
but only ICE-UDP seems to be specified/used for XMPP; XEP-0261:
Jingle In-Band Bytestreams Transport Method should work too, but
clients seem to fail to use it). The "smacks" module is
important for reliable message delivery, "cloud_notify" may be
handy for "pull notifications" on mobile phones, and
"turncredentials" or "turn_external" helps to point a TURN
server if there is one for clients (which should provide
relatively reliable Jingle calls; coturn is easy to set). The
"muc" component should be enabled to provide multi-user chats
(XEP-0045), and "http_upload" or "http_file_share" -- for HTTP
file upload (XEP-0363). Message Archive Management (XEP-0313) is
also handy for clients that support it.
Unfortunately Jingle XTLS was abandoned, and those SOCKS5
connections wouldn't be encrypted at all on their own (neither
would ICE-UDP ones, but at least there's SRTP for encrypted
audio/video calls). In-band bytestreams may be preferable for
encrypted (even though less efficient) file transfers, but
that's up to clients. XEP-0391: Jingle Encrypted Transports is
another solution to this.
DANE TLSA records can be useful to set, if DNSSEC is used. One
nice option for an authoritative name server (that supports both
DNSSEC with automatic signing and dynamic DNS updates) is knot
DNS.
The private server setup documentation contains more precise
instructions and configuration, while the workstation setup
documentation includes configuration for a self-hosted XMPP
server.
Spam
The strategy for dealing with spam is similar to that with
dealing with other network abuse: a bit of technical means may
help, but generally it is about contacting server administrators
(XEP-0157) or their hosters, and blacklisting their domains or
subnets if it does not help. See also: JabberSPAM/blacklist, my
notes on network abuse.
As a server operator, I just do not run a public server (though
it might be nice to do, but it is mostly the potential legal
issues that stop me), but for those who do run them, there
is The Jabber Spam Fighting Manifesto.