Riot’s magical push notifications in iOS

A couple of months ago I started playing with Matrix, a federated communication/chat system. Rather than make an account on a public homeserver I installed Synapse on my own server. I installed the Riot app on my iPhone and connected directly to my server. (Synapse is the server and Riot is the client produced by the Matrix team.)

Everything worked well. A bit too well: I was getting push notifications in iOS when I received new messages. Push notifications are very handy but it was confusing. I knew that new messages were arriving at my self-hosted homeserver but there is no way that my server would be allowed to fire notifications into Apple’s push notification service.

It’s a big problem for any kind of federated network such as Matrix, GNU social or XMPP. The entire point is that you can have an account on any server while still participating in the network. If you want to support an iOS client it can’t get realtime notifications directly from the chosen server. Push notifications can only be sent from servers run by the app developer.

You can solve this by playing pass-the-parcel: your homeserver generates an event and sends it to the app developer’s server, who passes it on to Apple, who commands your iPhone to vibrate. This solves the problem nicely apart from some privacy issues which I’ll get to shortly.

But this creates a new problem. Imagine you want to write your own Matrix app for iOS, a free and open source one that you write in your free time. Your users will insist on having push notifications because it’s the 21st century. This requires you to run a push notification server. Even if you’re willing to do this it costs real money on an ongoing basis and it may be the only significant cost you have. You can’t just make it an in-app purchase because the App Store Review Guidelines explicitly forbid taking payments to enable Push Notifications.

If you want to recoup your costs you either have to charge for your app, which will reduce the take-up of the network you presumably want to support, or take a punt and hope nice users give you donations. (This doesn’t often work.)

What we really need is some benevolent company who will release a free iOS app and subsidise the cost of notifications for everybody while focusing on making money some other way. It sounds crazy but that seems to be exactly what Riot is doing.

The above diagram is straight from the Matrix specification. They’ve built mitigation for the push notification problem right into the design.

I have Riot installed on my iPhone and iPad. When I log on to the Riot web interface it shows that the installed apps have registered themselves as “Notification Targets” on my server.

Looking in the pushers table in my server database I can see that they have registered an HTTP pusher with the following URL:

https://matrix.org/_matrix/push/v1/notify

This means when I need to receive a notification my homeserver will make an HTTP POST to that URL with a JSON message describing the content of the notification. The Matrix/Riot servers will take over from there and forward it to Apple.

So that Apple knows where to send it the message contains a pushkey. This comes from the iOS device itself. The app registers for push notifications and obtains an APNS Device Token. MatrixKit then takes this token and uploads it to the homeserver when it creates the HTTP pusher.

So that explains how my push notifications magically work. It’s pretty awesome. The only downside is a small loss of privacy. One of the nice things about federated networks is that you can take total control of your data security. Communications are decentralised, accessible only on the peers that need them. Supporting iOS notifications chips away at that ideal situation, exposing at the very least some metadata to various third parties. I point the blame for that squarely at Apple’s iron grip.

The good news is that it’s easy to limit the amount of information transmitted in the notifications. While the public channels include text snippets when I’m mentioned, if I’m in an encrypted chat it simply says Message from <username>. Apple and Riot may know that I’m talking to a particular person at a particular time (more specifically, that they are talking to me) but not what was said. This is roughly the same privacy you have when you use Signal for iOS. Solution: turn on encryption. According to Matrix’s blog post this will be the default for private rooms in the future.

It’s an interesting situation. On one hand I’m happy to see Riot break the impasse of federated notifications. On the other hand it only solves the problem for Matrix, and relying on the benevolence of a company to keep a technical solution operating doesn’t fill me with confidence. It’s not ideal but at the end of the day it’s considerably better than what you get using Facebook messenger or Slack so at this point I’m happy with the trade-off!


Archived comments

Matthew Hodgson says

This is an awesome post – thanks for dispelling some of the magic :D

There’s nothing special about what Riot is doing though: any app that needs push notifs from Matrix just needs to also run its own matching server-side push server (i.e. sygnal instance) in order to relay pushes through to the app via Apple, Google etc.

In the end, it’s a matter of trust. The service that talks to APNS/GCM needs the necessary push certificate private data to prove it is authorized to send pushes to your app. You wouldn’t want to hand that private data to any random homeserver, so instead you have to run a sygnal which is then coupled to your app. Any random homeserver in Matrix can then happily hit your sygnal to relay to your app.

In other words, Riot really isn’t privileged at all here. It’s a bit counterintuitive, but yes: any app that needs to receive pushes has to also have its own dedicated sygnal running somewhere, run by the author of the app (as opposed to the admin of a homeserver). In the end this is hopefully a Good Thing.

This will probably change in future as folks investigate non-GCM/APNS push mechanisms with Matrix – e.g. the very promising work from Sergio L Pascual at https://github.com/slp/matrix-pushgw, or even using Matrix /itself/ as a push protocol (assuming a more efficient transport & encoding :)

Matthew Hodgson says

oh, and also: the plan on push notif contents via APNS/GCM is to remove all contents of the message and use then purely as a wake-up notification. The app will then wake up and query your HS to actually find out the contents that needs to be displayed via a local notification – including decrypting it via E2E if needed. Thus no data or metadata (other than the existence of a notification) will be shared with Apple or Google at all. This is how Signal does it, for instance.

Tom says

Hi Matthew, thanks for the comments! You’re right to emphasise that Riot isn’t doing anything unusual in the context of iOS, and that people are free to use a different app with a gateway they trust. I hope not to make anyone think otherwise. Great to hear about the plans for wakeup-only notifications too!

One reason I wrote the post is that for privacy-conscious people a client app for a federated network “calling home” in any sense instead of exclusively talking to your nominated server is a minor surprise. It’s nice to be clear about exactly what data is going where. As noted I’m okay with what I found and it will be even better with the changes you mentioned. All the same I expect there would be people out there with opsec requirements that mean they say “right, not using notifications then”. (You would hope they know how iOS works but maybe not. I for one was initially confused.)

This is also quite interesting for the free software community that builds its own federated networks. (GNU social comes to mind, both for the servers and various clients). People volunteer their time but can’t necessarily chump up cash to make apps work. The implicit pairing of an app with a server backend to make basic features like notifications work is an impediment to offering a good iOS experience. If you can’t offer a good iOS experience that locks you out of potential users. It’s a bummer all round really. So I think it’s interesting that Matrix has bridged the gap, offering a gratis open-source client and server while also picking up the tab for notifications when using Riot. It hasn’t fixed the broader problem but it is a notable development.

Cheers!