ircv3-specifications
ircv3-specifications copied to clipboard
Add Web Push Extension
A primary goal of this spec is to not tie the extension to any proprietary push service. This is achieved by using the standard Web Push protocol. Web Push also provides encryption of the payload.
IRC clients are responsible for providing a push endpoint to the IRC server. This is usually provided by the platform (e.g. by the browser for the Web). IRC servers don't need to register or anything like that, all they need is send an HTTP request when they wish to deliver a push notification.
See the spec for more details.
Not mentioned in the spec is the integration with proprietary push services. For instance, Android apps can't (unfortunately) use Web Push: they must use Firebase Cloud Messaging (FCM). Since FCM is also used on Chrome for their Web Push implementation, I've tried to see if there was a way to figure out some Web Push parameters from the FCM ones, but it sounds like the Web Push functionality is limited to the Chrome API keys.
My approach is to introduce a relay which listens for Web Push messages and forwards them to FCM. This does require an additional moving part that IRC clients need to rely on, however this sounds like a reasonable trade-off to me. Since the relay doesn't see any clear-text privacy-sensitive information (payload is encrypted by the IRC server, no client IP address is visible), I could provide a free relay service for client developers who don't want to setup their own.
┌────────────┐ ┌────────────┐
│ │ Subscribe │ │
│ Android ├─────────────►│ │
│ IRC client │ │ IRC server │
│ │ │ │
│ │ │ │
└────────────┘ └─────┬──────┘
▲ │
│ │
Push │ │Push
notification │ │notification
│ ▼
┌─────┴─────┐ ┌──────────┐
│ │ │ │
│ Firebase │◄─────────────┤ Web Push │
│ Messaging │ │ Relay │
│ │ │ │
└───────────┘ └──────────┘
So far, I've implemented proof-of-concepts for soju and two client platforms:
- gamja (Web): the implementation is pretty straightforward, it's mostly a matter of piping the JS API to IRC.
- Android: this requires a relay, and requires to implement Web Push decryption logic in the client. This isn't too bad, the Web Push encryption and decryption logic is mostly symmetrical. So as long as a library to send Web Push notifications exists, it shouldn't be too hard to turn it the other way around and make it decrypt notifications.
References:
- Ergo discussion: https://github.com/ergochat/ergo/issues/1527
- Web Push JS API spec: https://w3c.github.io/push-api/
- soju PoC: https://git.sr.ht/~emersion/soju/commit/be53ae00d124ad462ec8611a5571ec07e0156a16
- gamja PoC: https://git.sr.ht/~emersion/gamja/commit/e324638ae895aa409c84063c20b36065953bc78e
- Web Push to Firebase Cloud Messaging relay: https://git.sr.ht/~emersion/pushgarden
- Android client PoC (remember, this is just a PoC, for now the code is terrible, but it works!): https://git.sr.ht/~emersion/PushGardenTestApp
This is an incomplete specification. I opened this PR to gather feedback on the approach. Let me know what you think!
- [x]
WEBPUSH UNREGISTER - [x] ~~Figure out how to specify "messages of interest"~~
- [ ] Expand on TTLs
- [ ] Expand on rate limiting
- [x] Allow servers to drop message tags to fit the notification payload size limit
- [x] ~~Consider turning the cap into an
ISUPPORT~~ - [x] Consider sending the VAPID public key as an
ISUPPORT - [x] Error codes
Sorry if this is obvious, but if the server needs to wait for the client to send the endpoint, what is the use of the webpush capability? Also it's not present in soju and gamja's commits.
It's used to signal the client that the commands are supported. Could use an ISUPPORT I suppose… but need to make sure servers don't want to change behavior when push notifications are enabled.
capability doesn't prevent server from changing the behavior after cap was enabled. There's even CAP DEL especially for that
capability doesn't prevent server from changing the behavior after cap was enabled. There's even CAP DEL especially for that
Nah, I mean that a cap would inform the server that the client supports the ext. For instance the chat history spec uses this to make servers not send playback on connect when the chathistory cap is enabled.
Okay, and why this information is needed upon connect? I understand why it's needed for chathistory
As said above, I'm not necessarily opposed to making this an ISUPPORT instead of a cap. We just need to make sure that no server is interested in knowing whether the client supports the ext or not.
Two issues that came up in discussion with @kylef:
- Signal only sends a ping message (i.e. with no user data) over the push channel, and then the client reconnects to the server and downloads the actual message data. This was felt to be unworkable in the IRC context, since iOS is imposing increasingly onerous restrictions on what can be done in a push notification handler (it's too hard to connect to an IRC server, and soon it may be outright impossible).
- However, RFC8030 seems to impose a de facto size limit of 4096 bytes on push messages. This is shorter than the maximum message length under message-tags, so provisions for the server shortening the message are necessary. (The server might have a list of tags to prioritize?) Given this, we might want to specify an alternative format for the message (JSON would allow the server to send extensible metadata with the push message, although at this time I don't have a candidate use case).
Yeah, the size limit is going to be an issue. FCM alone also has a 4096 bytes limit for the payload.
we might want to specify an alternative format for the message
I don't think JSON would help, the limit still applies regardless of the format of the payload. Also, servers can extend messages already with tags. Am I missing something?
Sorry, that was unclear. I meant something like: abandon RFC1459-and-derivatives entirely in the format of the push message, parse prefix/tags/command/params directly into JSON.
How is this going to help with the size limit of the push notification payload?
Do you intent to add a command to view the list of subscriptions? I could see how as a user this could be useful to debug problems and be able to manually clean up or unsubscribe old clients for which you do not know the subscription URL for. This could very well be something that a NickServ or similar service could facilitate for a user and not be included in the spec though.
Yeah, if it's just a debugging tool, I think I'd rather let servers include it as a special service or custom commands. Also need to investigate if/how subscription expiration can be integrated.
Might be relevant: https://www.andrewjvpowell.com/articles/dear-privacy-aware-android-app-devs-please-use-unifiedpush/
UnifiedPush is basically Web Push without any kind of encryption. I've been discussing with them to add encryption, they seem open to the idea (they considered Web Push in the past but didn't understand fully how encryption works).
My plan is to go ahead and ship this as a soju vendored extension, then gather field testing feedback and report back what I've learnt here.
The vendored extension has been merged in soju and goguma.
Goguma and pushgarden now support Apple Push Notification service, as more evidence that this approach works on all platforms.