# Mobile push notification service

Zulip's iOS and Android mobile apps support receiving push
notifications from Zulip servers to let users know when new messages
have arrived. This is an important feature to having a great
experience using the Zulip mobile apps.

For technical reasons (explained below), in order to deliver mobile
push notifications in the app store versions of our mobile apps, you
will need to register your Zulip server with the Zulip mobile push
notification service. This service will forward push notifications
generated by your server to the Zulip mobile app automatically.

## How to sign up

Starting with Zulip 1.6 for both Android and iOS, Zulip servers
support forwarding push notifications to a central push notification
forwarding service. Accessing this service requires outgoing HTTPS
access to the public Internet; if that is restricted by a proxy, you
will need to [configure Zulip to use your outgoing HTTP
proxy](deployment.md#customizing-the-outgoing-http-proxy)
first.

You can enable this for your Zulip server as follows:

1. Uncomment the
   `PUSH_NOTIFICATION_BOUNCER_URL = 'https://push.zulipchat.com'` line
   in your `/etc/zulip/settings.py` file (i.e. remove the `#` at the
   start of the line), and [restart your Zulip
   server](settings.md#making-changes). If you
   installed your Zulip server with a version older than 1.6, you'll
   need to add the line (it won't be there to uncomment).

1. If you're running Zulip 1.8.1 or newer, you can run the
   registration command:

   ```bash
   # As root:
   su zulip -c '/home/zulip/deployments/current/manage.py register_server'
   # Or as the zulip user, you can skip the `su zulip -c`:
   /home/zulip/deployments/current/manage.py register_server

   # docker-zulip users can run this inside the container with `docker exec`:
   docker exec -it -u zulip <container_name> /home/zulip/deployments/current/manage.py register_server
   ```

   This command will print the registration data it would send to the
   mobile push notifications service, ask you to accept the terms of
   service, and if you accept, register your server. If you have trouble,
   email support@zulip.com with the output of this command.

1. If you or your users have already set up the Zulip mobile app,
   you'll each need to log out and log back in again in order to start
   getting push notifications.

Congratulations! You've successfully set up the service.

If you'd like to verify that everything is working, you can do the
following. Please follow the instructions carefully:

- [Configure mobile push notifications to always be sent][mobile-notifications-always]
  (normally they're only sent if you're idle, which isn't ideal for
  this sort of testing).
- On an Android device, download and log in to the
  [Zulip Android app](https://play.google.com/store/apps/details?id=com.zulipmobile).
  If you were already logged in before configuring the server, you'll
  need to log out first, since the app only registers for push
  notifications on login.
- Hit the home button, so Zulip is running in the background, and then
  have **another user** send you a **private message** (By default,
  Zulip only sends push notifications for private messages sent by other
  users and messages mentioning you). A push notification should appear
  in the Android notification area.

[mobile-notifications-always]: https://zulip.com/help/test-mobile-notifications

## Updating your server's registration

Your server's registration includes the server's hostname and contact
email address (from `EXTERNAL_HOST` and `ZULIP_ADMINISTRATOR` in
`/etc/zulip/settings.py`, aka the `--hostname` and `--email` options
in the installer). You can update your server's registration data by
running `manage.py register_server` again.

If you'd like to rotate your server's API key for this service
(`zulip_org_key`), you need to use
`manage.py register_server --rotate-key` option; it will automatically
generate a new `zulip_org_key` and store that new key in
`/etc/zulip/zulip-secrets.conf`.

## Why this is necessary

Both Google's and Apple's push notification services have a security
model that does not support mutually untrusted self-hosted servers
sending push notifications to the same app. In particular, when an
app is published to their respective app stores, one must compile into
the app a secret corresponding to the server that will be able to
publish push notifications for the app. This means that it is
impossible for a single app in their stores to receive push
notifications from multiple, mutually untrusted, servers.

Zulip's solution to this problem is to provide a central push
notification forwarding service, which allows registered Zulip servers
to send push notifications to the Zulip app indirectly (through the
forwarding service).

## Security and privacy

Use of the push notification bouncer is subject to the Zulip Cloud [Terms of
Service](https://zulip.com/policies/terms), [Privacy
Policy](https://zulip.com/policies/privacy) and [Rules of
Use](https://zulip.com/policies/rules). By using push notifications, you agree
to these terms.

We've designed this push notification bouncer service with security
and privacy in mind:

- A central design goal of the Push Notification Service is to
  avoid any message content being stored or logged by the service,
  even in error cases.
- The Push Notification Service only stores the necessary metadata for
  delivering the notifications to the appropriate devices, and nothing
  else:
  - The APNS/FCM tokens needed to securely send mobile push
    notifications to iOS and Android devices, one per device
    registered to be notified by your Zulip server.
  - User ID numbers generated by your Zulip server, needed to route
    a given notification to the appropriate set of mobile devices.
    These user ID numbers are opaque to the Push Notification
    Service and Kandra Labs.
- The Push Notification Service receives (but does not store) the
  contents of individual mobile push notifications:

  - The numeric message ID generated by your Zulip server.
  - Metadata on the message's sender (name and avatar URL).
  - Metadata on the message's recipient (stream name + ID, topic,
    private message recipients, etc.).
  - A timestamp.
  - The message's content.

  There's a `PUSH_NOTIFICATION_REDACT_CONTENT` setting available to
  disable any message content being sent via the push notification
  bouncer (i.e. message content will be replaced with
  `***REDACTED***`). Note that this setting makes push notifications
  significantly less usable.

  We plan to
  [replace that setting with end-to-end encryption](https://github.com/zulip/zulip/issues/6954)
  which would eliminate that usability tradeoff and additionally allow
  us to not have any access to the other details mentioned in this
  section.

- All of the network requests (both from Zulip servers to the Push
  Notification Service and from the Push Notification Service to the
  relevant Google and Apple services) are encrypted over the wire with
  SSL/TLS.
- The code for the push notification forwarding service is 100% open
  source and available as part of the
  [Zulip server project on GitHub](https://github.com/zulip/zulip).
- The push notification forwarding servers are professionally managed
  by a small team of security expert engineers.

If you have any questions about the security model, contact
support@zulip.com.

## Submitting statistics

Systems using the Mobile Push Notifications Service will, by default,
submit basic usage statistics (e.g. Zulip version, number of users,
number of messages sent) to the service. These statistics help the
Zulip open source project understand how many people are using Zulip,
and help us allocate resources towards supporting self-hosted
installations.

Our use of these statistics is governed by the same [Terms of
Service](https://zulip.com/policies/terms) and [Privacy
Policy](https://zulip.com/policies/privacy) that covers the Mobile Push
Notifications Service itself. If your organization does not want to submit these
statistics, you can disable this feature at any time by setting
`SUBMIT_USAGE_STATISTICS=False` in `/etc/zulip/settings.py`.

## Rate limits

The Mobile Push Notifications Service API has a very high default rate
limit of 1000 requests per minute. A Zulip server makes requests to
this API every time it sends a push notification, which is fairly
frequent, but we believe it to be unlikely that a self-hosted
installation will hit this limit.

This limit is primarily intended to protect the service against DoS
attacks (intentional or otherwise). If you hit this limit or you
anticipate that your server will require sending more push
notifications than the limit permits, please [contact
support](https://zulip.com/help/contact-support).

## Sending push notifications directly from your server

This section documents an alternative way to send push notifications
that does not involve using the Mobile Push Notifications Service at
the cost of needing to compile and distribute modified versions of the
Zulip mobile apps.

We don't recommend this path -- patching and shipping a production
mobile app can take dozens of hours to set up even for an experienced
developer, and even more time to maintain. And it doesn't provide
material privacy benefits -- your organization's push notification
data would still go through Apple/Google's servers, just not Kandra
Labs'. Our view is the correct way to optimize for privacy is
end-to-end encryption of push notifications. But in the interest of
transparency, we document in this section roughly what's involved in
doing so.

As we discussed above, it is impossible for a single app in their
stores to receive push notifications from multiple, mutually
untrusted, servers. The Mobile Push Notification Service is one of
the possible solutions to this problem. The other possible solution
is for an individual Zulip server's administrators to build and
distribute their own copy of the Zulip mobile apps, hardcoding a key
that they possess.

This solution is possible with Zulip, but it requires the server
administrators to publish their own copies of
the Zulip mobile apps (and there's nothing the Zulip team can do to
eliminate this onerous requirement).

The main work is distributing your own copies of the Zulip mobile apps
configured to use APNS/FCM keys that you generate. This is not for
the faint of heart! If you haven't done this before, be warned that
one can easily spend hundreds of dollars (on things like a DUNS number
registration) and a week struggling through the hoops Apple requires
to build and distribute an app through the Apple app store, even if
you're making no code modifications to an app already present in the
store (as would be the case here). The Zulip mobile app also gets
frequent updates that you will have to either forgo or republish to
the app stores yourself.

If you've done that work, the Zulip server configuration for sending
push notifications through the new app is quite straightforward:

- Create an
  [FCM push notifications](https://firebase.google.com/docs/cloud-messaging)
  key in the Google Developer console and set `android_gcm_api_key` in
  `/etc/zulip/zulip-secrets.conf` to that key.
- Register for a
  [mobile push notification certificate][apple-docs]
  from Apple's developer console. Set `APNS_SANDBOX=False` and
  `APNS_CERT_FILE` to be the path of your APNS certificate file in
  `/etc/zulip/settings.py`.
- Set the `APNS_TOPIC` and `ZULIP_IOS_APP_ID` settings to the ID for
  your app (for the official Zulip apps, they are both `org.zulip.Zulip`).
- Restart the Zulip server.

[apple-docs]: https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/APNSOverview.html
