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.
- 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.
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.