Slack's bait and switch

| categories: xmpp, foss, slack, converse.js | View Comments

Slack has finally decided to close down their IRC and XMPP gateways.

True to form, you can only read their announcement if you already have a Slack account and are logged in to a workspace.

Here's the gist of their announcement:

As Slack has evolved over the years, we've built features and capabilities —
like Shared Channels, Threads, and emoji reactions (to name a few) — that the
IRC and XMPP gateways aren't able to handle. Our priority is to provide a
secure and high-quality experience across all platforms, and so the time has
come to close the gateways.

They're of course being economical with the truth here.

Perhaps their XMPP gateway can't handle "Shared Channels" and "Threads", but that's because they purposefully stopped working on it.

A "Shared Channel" simply means a chatroom which people from outside your workspace can participate in. If a workspace is mapped to a members-only chatroom, then making something a shared channel simply means updating the members list or making the chatroom open (so anybody can join it).

Threads can be implemented by adding a <thread> element in the message stanza, as documented in XEP-201.

And emoji... there's nothing in XMPP that prevents people from sending emoji.


UPDATE: Several readers have pointed out that "emoji reactions" and emoji are different things. Emoji reactions can be tacked to a particular message.

Still, there's nothing fundamental about XMPP that prevents emoji reactions, and work is currently underway to add support for them.

The protocol is designed to be eXtensible (hence the X in XMPP) and new features are continuously being added.

Classic bait and switch

We all know the real reason Slack has closed off their gateways. Their business model dictates that they should.

Slack's business model is to record everything said in a workspace and then to sell you access to their record of your conversations.

They're a typical walled garden, information silo or Siren Server

So they have to close everything off, to make sure that people can't extract their conversations out of the silo.

We saw it with Google, who built Gtalk on XMPP and even federated with other XMPP servers, only to later stop federation and XMPP support in favour of trying to herd the digital cattle into the Google+ enclosure.

Facebook, who also built their chat app on XMPP at first allowed 3rd party XMPP clients to connect and then later dropped interoperability.

Twitter, although not using or supporting XMPP, had a vibrant 3rd party client ecosystem which they killed off once they felt big enough.

Slack, like so many others before them, pretend to care about interoperability, opening up just so slightly, so that they can lure in people with the promise of "openness", before eventually closing the gate once they've achieved sufficient size and lock-in.

On Federation

When we talk about "federation" in networks, we mean the ability to communicate between different service providers.

For example, email is federated. You can set up your own email server, and then send emails to people with their own email servers, or to people with Gmail or Yahoo! accounts.

You can email any other email address in the world, regardless of where that email address is hosted.

If email never existed, and a company like Slack today would come out with this brand new concept of "Electronic Mail", let's call it digimail, do you think they would standardise the digimail protocol and allow you to send messages to other digimail purveyors?

We all know the answer to that. They won't, and neither would Google, Microsoft or Facebook.

Heck, Facebook is actively trying to replace email since years.

The reason email is federated, is because it was developed before surveillance capitalism was a thing and because it was established and entrenched long before these companies came around.

There's a reason why your email address is still the de facto way to sign up for any service on the web (sometimes with one or two degrees of separation), and it's because of federation.

XMPP is designed to allow federation. Think about that. Instead of having to sign up to various different chat providers, all which try to lock you in and monetize your conversations, you could instead have one chat account, and use that to chat with anybody else, regardless of which chat provider they are using.

Alas, that's the dream, but because XMPP came much later to the scene, it didn't develop the critical mass as email has, and here we are. With dozens of chat apps, all non-interoperable and closed off.

What would it take for XMPP to take off?

One of the sad things that has come out of Slack's meteoric rise to success, has been how many free and open source projects have jumped over to using it (after previously using IRC or XMPP).

In so doing, they have closed off their discussions from search engines and they prevent people from accessing their past archives.

Slack has many cool features, and they work very well, I'm not going to deny it.

However, the XMPP Software Foundation has done a lot of work in recent years to enable protocol extensions that provide features that people have come to expect from chat applications, for example:

Unfortunately XMPP clients have been lagging far behind in various respects.

One of the main problems is funding. The modern digital economy is largely set up around surveillance capitalism and user lock-in.

So attempts to create software that doesn't follow these precepts, often end up unfunded or underfunded.

However, our "weakness", is also our strength.

XMPP clients, and the XMPP network can provide something that Slack never can. Federation, free and open software, interoperability, extensibility and user choice.


For the last few years I've been working in my spare time on making a JavaScript XMPP chat client, called converse.js.

Originally the idea was to make a Gtalk like chat client that you integrate in your website, and it can still be used like that.

A screenshot of converse.js as overlayed chatboxes

However, in the last year I've updated it further so that it can also be used as a fullscreen application, like Slack is used.

You can try the fullscreen version at

A screenshot of converse.js as a fullscreen application

If you have no-one to chat to, then come join the chat room.

This link will take you directly there (via

Converse.js still lacks lots of features that Slack has, but that's not because XMPP itself can't support those features.

What converse.js however does have, is that it's free and open source software, based on a standard protocol and it can be extended, updated and improved upon, by anyone.

We're actively working on adding new features and more and more people are joining in.

Moreover, anybody can host it and you can integrate it into any website.

Ultimately, I believe in the power and utility of interoperability and software freedom, even though the current trend is to close off and lock down.

These information silos are as powerful as we make them. If enough projects choose standardised protocols and FOSS software, we will be able to create viable alternatives that foster freedom instead of lock-in.


Hacker News discussion

This blog post triggered a lively discussion on Hacker news, you can read that here:

Read and Post Comments

A badge for linking to your XMPP chat

| categories: xmpp, converse.js | View Comments

You've probably noticed the coloured badges at the top of project READMEs on Github and Gitlab.

These badges indicate various metrics about the project's current state and its supporting infrastructure.

For example, the README page for converse.js contains the following badge:

Bountysource bounties

It indicates how many open bounties there are for converse.js on Bountysource.

There are dozens such badges available nowadays, just take a look at

Some projects have badges that link to a chatroom, hosted by Gitter, Slack etc.

Converse.js, being an XMPP-based project, has its own XMPP chatroom at

So I created a badge for its README, which points to that chatroom and also indicates how many users are currently in it.

It looks like this:

Link to the Converse.js chatroom on

The Markdown syntax for showing the badge looks like this:

[![Link to XMPP chat](](

The target URL for this badge is, which means that it takes the user to and then once the user has logged in, the room will be opened.

To go to a different room, just specify a different JID (Jabber ID) in the URL parameters.

The image URL is

Here you specify the room JID with the room URL parameter.

The image is an SVG which is generated by a tiny Flask app, which includes a chatbot written with SleekXMPP that checks how many users are in a particular chatroom.

The code is open source, and hosted on Github at jcbrand/xmpp-chat-badge.

If you're using this badge for your project, I'd be happy to hear about it.

You can let me know in the converse.js XMPP chatroom at (or through the web with or by using this website's contact form.

Read and Post Comments

The 2018 XSF Summit

| categories: xmpp | View Comments

This year I again attended the XMPP Software Foundation's summit in Brussels. The summit spans two days and is held at Cisco's offices.

It's a rare opportunity to get community members together to discuss and make decisions around protocol extensions (the so-called XEPs). On the second day interesting discussions were had regarding the state of XMPP today, for example how and by whom its used, how to foster and support client development and the merits of federation (i.e. communication between different messaging providers).

We heard that XMPP is heavily used and respected within the Telecoms industry, and that there's a lot of "hidden" use of XMPP that even most of the Summit participants were unaware of.

The distinction was made between XMPP, which represents the protocol, and Jabber®, which represents the ideal of federated instant messaging as used by end users.

Besides what some people may think, XMPP is alive and well, and used extensively in many applications and in various sectors.

Jabber®, on the other hand, not so much. Mindshare has moved largely to siloed (i.e. non-federated and non-standardized) messaging apps like Slack et al.

The Tigase guys Andrzej Wójcik and Daniel Wisnewski gave a demo of the new Internet-of-things module of the Tigase XMPP server which also drew attention at the realtime communications stand at FOSDEM.

Minutes for the XSF Summit

I volunteered to be the minute taker this year, for which I used my favorite notes-taking app: Vimwiki.

With Vimwiki you can generate HTML from your wiki files, so I was able to make the notes available online roughly as I was typing them. which I did by pushing the generated HTML to a git repo and then pulling again via cron on a server and serving with Nginx.

Me writing the notes at the summit.

I N C E P T I O N (an original piece by Guus der Kinderen)

I also wanted to make the minutes available on the XMPP wiki, but Vimwiki's syntax is a little bit different than the Mediawiki syntax.

Pandoc came to the rescue, and with a one-liner I could generate Mediawiki files from all my notes.

So here are the 2018 XSF Summit minutes on the XMPP wiki:

Final thoughts

It was fun to meet up again with kindred spirits and to see some familiar and friendly faces from the XMPP world.

This year was also the first time I attended the fancy XSF dinner. I had the sea bass with endive and beurre blanc.

See you folks next year and thanks for all the fish!

The XSF summit dinner.
Read and Post Comments

Converse 3.3 has been released

| categories: xmpp, converse.js | View Comments

Last night I released version 3.3.0 of Converse.js, and as often happens with big releases, I made a quick bugfix release (3.3.1) today.

The bugfix release turns off some CSS3 animations for new messages which caused degraded performance on Firefox. On Chrome the animations render smoothly, so if you'd like you can still turn them on with the show_message_load_animation config option.

What's in the release?

Maintaining a long-term open source front-end JavaScript library almost feels like a Sisyphean task sometimes. As soon as you've rolled the big stone up the hill, the whole JS ecosystem, best practices and tooling changes and you find yourself at the bottom of the hill again.

This release is therefore heavy on changes under the hood, with the aim of modernizing and improving the quality of the code.

Besides that, I also spent time squashing lots of small usability bugs and on improving performance.

Converse.js now uses a Virtual DOM

Various views, such as the registration form, are now rendered by means of a Virtual DOM. I wrote a new Backbone view called Backbone.VDOMView for this, and blogged about it here:

No more jQuery

Looking at the git log, I started rewriting code to not use jQuery in January 2017.

So this change has been a year in the works. I often asked myself whether I should spend time on this and not rather do something else, like adding new features, especially since removing jQuery has taken a lot of time.

However, there were some good reasons, or perhaps motivations, for me to remove jQuery.

Before ES6 promises were available, I used $.Deferred. However, jQuery's deferreds weren't compatible with Promises, so when ES6 Promises came around, I had to rewrite lots of code to use Promises.

I used $.show and $.hide quite a bit, and then it turned out that the way jQuery was doing it (by adding/removing display: none to the DOM element) is not good practice and also very slow.

So I started writing my own utility functions to replace jQuery's.

The last straw for me was when jQuery 3 came out, and half of Converse.js's ~240 tests failed once I plugged it in.

After spending some time trying to figure out what backward incompatible changes they made and how I should update the code, I decided to instead rip jQuery out entirely.

It's still used in the tests, but it's no longer included in any build.

Since removing it, I've noticed a remarkable reduction in time to run the tests.

By looking at how quickly the tests run now, the code seems to run much faster without jQuery.

Less weight

Besides removing jQuery, I also updated Converse.js to load translations at runtime, and only the exact translation JSON file that's needed.

Due to these changes, the unminified built has shrunk from 3.38MB to 2.8MB, and the minified build from 1.66MB to 1.2MB.

And this is while adding the virtual DOM code.

Route to a specific chat room via the URL

It's now possible to directly link to a specific chat room, or to the registration page (instead of the login page) via the URL.

For example, the URL will take you immediately to the Converse.js "Discuss" chat room, once you've logged in.

What else?

Lots of other bugfixes and improvements have been added in this release. For more details, check the changelog.

Notable absent from this release are some desired features, such as file sharing, message corrections, message receipts and the like.

I would love to add some of these often requested features, however I had to get the house in order so to speak, by modernizing the code and squashing lots and lots of little usability and performance bugs.

That said, Converse.js takes up a LOT of my free time and not a single line of code in this release was paid for.

If you or your company make use of converse.js, please consider sponsoring it on Patreon or Liberapay.


Thanks goes out to everyone who's made pull requests and bug reports over the last months.

And thanks also to the folks who hang out in the Converse.js Discusss chat room and who have there provided valuable feedback.

Read and Post Comments

Customizing and whitelisting SASL authentication mechanisms in Strophe.js

| categories: xmpp, strophe.js, foss, sasl | View Comments


If you've decided to read this fairly technical blogpost, then you probably have at least a rough idea what SASL is about and why one would want to create custom SASL auth mechanisms or whitelist the supported mechanisms.

I'll therefore provide just a very brief recap of the topics involved:

The Simple Authentication and Security Layer or SASL RFC 4422 is a framework for adding authentication support to connection-based protocols.

It provides an abstraction layer for authentication mechanisms, so that protocols, such as XMPP don't have to deal with the intricacies and complexities of supporting multiple authentication mechanisms.

It therefore makes auth mechanisms pluggable (if they are SASL compatible).

Strophe.js has supported SASL since a long time, but it didn't provide an easy way add custom SASL mechanisms, or to whitelist the mechanisms to be used.

Until now... or rather, since the 1.2.9 release.

Creating a custom SASL auth mechanism

To create a custom SASL authentication mechanism is fairly simple.

You can glean what's required by simply looking at how the default mechanisms are created.

See for example how the SASLPlain mechanism is defined.

And look as the SASLMechanism prototype to see the interface that the mechanism supports.

Perty much it boils down to creating constructor, settings its prototype to an invoked Strophe.SASLMechanism instance, providing its name, a boolean to indicate whether it should proactively respond without an initial server challenge, and an integer value specifying its priority amongst the supported mechanisms.

The default mechanisms and their respective priorities are:

  • EXTERNAL - 60
  • SCRAM-SHA1 - 40
  • DIGEST-MD5 - 30
  • PLAIN - 20
  • ANONYMOUS - 10

Then it's a matter of implementing onChallenge and any of the other methods provided by the SASLMechanism prototype.

onChallenge is called once the server challenges the client to authenticate itself or proactively if the mechanism requires that the client initiates authentication (configured with the isClientFirst parameter of Strophe.SASLMechanism).

So, lets create a fictional auth mechanism called SASL-FOO which works similarly to SASL-PLAIN, except that the password is encrypted with double-encoding ROT13 (hint: this is a joke).

We would then create the authentication mechanism like so:

Strophe.SASLFoo = function() {};
Strophe.SASLFoo.prototype = new Strophe.SASLMechanism("FOO", true, 60);

Strophe.SASLFoo.prototype.onChallenge = function(connection) {
    var auth_str = connection.authzid;
    auth_str = auth_str + "\u0000";
    auth_str = auth_str + connection.authcid;
    auth_str = auth_str + "\u0000";
    auth_str = auth_str + DoubleROT13(connection.pass);
    return utils.utf16to8(auth_str);

Whitelisting the supported SASL auth mechanisms

Now with SASL-FOO in hand, we can whitelist the supported authentication mechanisms by specifying a list of mechanisms in the options map passed in when we instantiate a new Strohe.Connection.

var service = '';
var options = {
    'mechanisms': [
var conn = new Strophe.Connection(service, options);

Bonus: Whitelisting SASL auth mechanisms in Converse.js

Due to the above changes it'll also be possible to whitelist SASL mechanisms in Converse.js (version 2.0.1 and upwards).

This is done via the connection_options configuration setting:


    connection_options: {
        'mechanisms': [
Read and Post Comments

Next Page »