Friday, September 25, 2015

XMPP (Jabber) as a Message Broker

For a long while I’ve relied on Jabber/XMPP support within Google Talk to communicate with back-end systems like my Garage Security monitor. Garage Security could push notifications to me when motion was detected, and I could reply back to ask for camera snapshots or current temperatures. It’s almost as if I was using XMPP and Google’s Talk servers as a message bus; everything was a request/response pair that I could receive as notifications in a nice lil’ mobile interface. This was a superior approach to having a peer-to-peer communication channel over the Internet at large - I could keep my firewall completely closed and instead publish events to a trusted broker over at Google HQ. I essentially treated Google Talk like a hosted RabbitMQ instance.

This "XMPP broker” approach continued to work after Google moved from Google Talk to Hangouts and dropped full XMPP support (notably for federation), however things appear to have become a bit more difficult when two different systems (like Garage Security and my new Sprinkler Switch) want to share the same Hangouts user ID. Previously both systems would receive an inbound message, so I would filter by a token in the message body. If I asked for “garage status,” Garage Security would catch the “garage” keyword and respond while Sprinkler Switch would just ignore it. As Hangouts has turned the XMPP support decidedly more text-message-ish, it seems now the last system to authenticate will starve out the previous system, and only one system will actually receive the messages.

This is not outside of the XMPP spec it seems, and the protocol itself specifies two ways for the systems themselves to deal with the issue:

  1. When connecting to the XMPP server set the priority for your connection. A higher priority is more likely to get inbound messages.
  2. Specify a resource name within your XMPP user identifier. This allows a system to be uniquely addressable with the same username.

The first option doesn’t necessarily help my situation - I want both systems to receive inbound messages. The second option is possible using XMPP’s definition of user IDs… where a user identifier is actually the composite of:

  • The username that is used for authentication
  • The domain that the user resides within
  • The resource that uniquely identifies who is signing in
Using this schema, I could provide SleekXMPP a JabberID (its native user identifier) of and have it uniquely identify Garage Security, while uniquely identifies Sprinkler Switch. It’s not entirely unlike the routing key in AMQP or a topic name in JMS… chuckleface could be considered your message type, could be considered your exchange, and garage could be considered your ID. Or something like that. It makes sense in my head at least.

Hangouts, however, just cares about chat messages. It could give two craps about my resource name. There’s no way to specify that in a contact either… with Hangouts you specify an e-mail address which in turn becomes a username and a domain. That’s fine for chat… but when I want to address an individual system I’m kinda outta luck. Hangouts will just reply back to the last resource that sent it a message - no way to specify a specific resource.

I've posted a demo using Python on GitHub which lets you build a quick XMPP client. An example might be:

>>> from jabtest import Jabber
>>> jab1 = Jabber('', 'apikeyh4x0r5', 'testone')
Opened XMPP Connection
>>> jab2 = Jabber('', 'apikeyh4x0r5', 'testone')
Opened XMPP Connection
>>> jab1.send_msg('', 'Testing One')
Sending message: Testing One

I don't have a fantastic solution for now... so in the interim I've disabled Jabber support for Sprinkler Switch.