</code></pre></div></div><p>A push notification server will handle the message according to the following rules:</p><ul><li>it MUST extract the public key of the sender from the signature and verify that the payload can be decrypted successfully.</li><li>it MUST verify that <codeclass="language-plaintext highlighter-rouge">token_type</code> is supported</li><li>it MUST verify that <codeclass="language-plaintext highlighter-rouge">device_token</code> is non empty</li><li>it MUST verify that <codeclass="language-plaintext highlighter-rouge">installation_id</code> is non empty</li><li>it MUST verify that <codeclass="language-plaintext highlighter-rouge">version</code> is non-zero and greater than the currently stored version for the public key and installation id of the sender, if any</li><li>it MUST verify that <codeclass="language-plaintext highlighter-rouge">grant</code> is non empty and according to the <ahref="#server-grant">specs</a></li><li>it MUST verify that <codeclass="language-plaintext highlighter-rouge">access_token</code> is a valid <ahref="https://tools.ietf.org/html/rfc4122"><codeclass="language-plaintext highlighter-rouge">uuid</code></a></li><li>it MUST verify that <codeclass="language-plaintext highlighter-rouge">apn_topic</code> is set if <codeclass="language-plaintext highlighter-rouge">token_type</code> is <codeclass="language-plaintext highlighter-rouge">APN_TOKEN</code></li></ul><p>If the message can’t be decrypted, the message MUST be discarded.</p><p>If <codeclass="language-plaintext highlighter-rouge">token_type</code> is not supported, a response MUST be sent with <codeclass="language-plaintext highlighter-rouge">error</code> set to <codeclass="language-plaintext highlighter-rouge">UNSUPPORTED_TOKEN_TYPE</code>.</p><p>If <codeclass="language-plaintext highlighter-rouge">token</code>,<codeclass="language-plaintext highlighter-rouge">installation_id</code>,<codeclass="language-plaintext highlighter-rouge">device_tokens</code>,<codeclass="language-plaintext highlighter-rouge">version</code> are empty, a response MUST be sent with <codeclass="language-plaintext highlighter-rouge">error</code> set to <codeclass="language-plaintext highlighter-rouge">MALFORMED_MESSAGE</code>.</p><p>If the <codeclass="language-plaintext highlighter-rouge">version</code> is equal or less than the currently stored version, a response MUST be sent with <codeclass="language-plaintext highlighter-rouge">error</code> set to <codeclass="language-plaintext highlighter-rouge">VERSION_MISMATCH</code>.</p><p>If any other error occurs the <codeclass="language-plaintext highlighter-rouge">error</code> should be set to <codeclass="language-plaintext highlighter-rouge">INTERNAL_ERROR</code>.</p><p>If the response is successful <codeclass="language-plaintext highlighter-rouge">success</code> MUST be set to <codeclass="language-plaintext highlighter-rouge">true</code> otherwise a response MUST be sent with <codeclass="language-plaintext highlighter-rouge">success</code> set to <codeclass="language-plaintext highlighter-rouge">false</code>.</p><p><codeclass="language-plaintext highlighter-rouge">request_id</code> should be set to the <codeclass="language-plaintext highlighter-rouge">SHAKE-256</code> of the encrypted payload.</p><p>The response MUST be sent on the [partitioned topic][./10-waku-usage.md#partitioned-topic] of the sender and MUST not be encrypted using the <ahref="../docs/stable/5-secure-transport.md">secure transport</a> to facilitate the usage of ephemeral keys.</p><p>The payload of the response is:</p><divclass="language-protobuf highlighter-rouge"><divclass="highlight"><preclass="highlight"><code><spanclass="kd">message</span><spanclass="nc">PushNotificationRegistrationResponse</span><spanclass="p">{</span>
</code></pre></div></div><p>The message MUST be wrapped in a <ahref="../stable/6-payloads.6#payload-wrapper"><codeclass="language-plaintext highlighter-rouge">ApplicationMetadataMessage</code></a> with type set to <codeclass="language-plaintext highlighter-rouge">PUSH_NOTIFICATION_REGISTRATION_RESPONSE</code>.</p><p>A client SHOULD listen for a response sent on the [partitioned topic][./10-waku-usage.md#partitioned-topic] that the key used to register.</p><p>If <codeclass="language-plaintext highlighter-rouge">success</code> is <codeclass="language-plaintext highlighter-rouge">true</code> the client has registered successfully.</p><p>If <codeclass="language-plaintext highlighter-rouge">success</code> is <codeclass="language-plaintext highlighter-rouge">false</code>:</p><ul><li>If <codeclass="language-plaintext highlighter-rouge">MALFORMED_MESSAGE</code> is returned, the request SHOULD NOT be retried without ensuring that it is correctly formed.</li><li>If <codeclass="language-plaintext highlighter-rouge">INTERNAL_ERROR</code> is returned, the request MAY be retried, but the client MUST backoff exponentially</li></ul><p>A client MAY register with multiple Push Notification Servers in order to increase availability.</p><p>A client SHOULD make sure that all the notification services they registered with have the same information about their tokens.</p><p>If no response is returned the request SHOULD be considered failed and MAY be retried with the same server or a different one, but clients MUST exponentially backoff after each trial.</p><p>If the request is successful the token SHOULD be <ahref="#advertising-a-push-notification-server">advertised</a> as described below</p><h3id="query-topic"><ahref="#query-topic"class="anchor-heading"aria-labelledby="query-topic"><svgviewBox="0 0 16 16"aria-hidden="true"><usexlink:href="#svg-link"></use></svg></a> Query topic </h3><p>On successful registration the server MUST be listening to the topic derived from:</p><divclass="language-plaintext highlighter-rouge"><divclass="highlight"><preclass="highlight"><code> 0XHexEncode(Shake256(CompressedClientPublicKey))
</code></pre></div></div><p>Using the topic derivation algorithm described <ahref="../stable/10-waku-usage.md#public-chats">here</a> and listen for client queries.</p><h3id="server-grant"><ahref="#server-grant"class="anchor-heading"aria-labelledby="server-grant"><svgviewBox="0 0 16 16"aria-hidden="true"><usexlink:href="#svg-link"></use></svg></a> Server grant </h3><p>A push notification server needs to demonstrate to a client that it was authorized by the client to send them push notifications. This is done by building a grant which is specific to a given client-server pair. The grant is built as follow:</p><divclass="language-plaintext highlighter-rouge"><divclass="highlight"><preclass="highlight"><code> Signature(Keccak256(CompressedPublicKeyOfClient . CompressedPublicKeyOfServer . AccessToken), PrivateKeyOfClient)
</code></pre></div></div><p>When receiving a grant the server MUST be validate that the signature matches the registering client.</p><h2id="re-registering-with-the-push-notification-server"><ahref="#re-registering-with-the-push-notification-server"class="anchor-heading"aria-labelledby="re-registering-with-the-push-notification-server"><svgviewBox="0 0 16 16"aria-hidden="true"><usexlink:href="#svg-link"></use></svg></a> Re-registering with the push notification server </h2><p>A client SHOULD re-register with the node if the APN or FIREBASE token changes.</p><p>When re-registering a client SHOULD ensure that it has the most up-to-date <codeclass="language-plaintext highlighter-rouge">PushNotificationRegistration</code> and increment <codeclass="language-plaintext highlighter-rouge">version</code> if necessary.</p><p>Once re-registered, a client SHOULD advertise the changes.</p><h2id="changing-options"><ahref="#changing-options"class="anchor-heading"aria-labelledby="changing-options"><svgviewBox="0 0 16 16"aria-hidden="true"><usexlink:href="#svg-link"></use></svg></a> Changing options </h2><p>This is handled in exactly the same way as re-registering above.</p><h2id="unregistering-from-push-notifications"><ahref="#unregistering-from-push-notifications"class="anchor-heading"aria-labelledby="unregistering-from-push-notifications"><svgviewBox="0 0 16 16"aria-hidden="true"><usexlink:href="#svg-link"></use></svg></a> Unregistering from push notifications </h2><p>To unregister a client MUST send a <codeclass="language-plaintext highlighter-rouge">PushNotificationRegistration</code> request as described above with <codeclass="language-plaintext highlighter-rouge">unregister</code> set to <codeclass="language-plaintext highlighter-rouge">true</code>, or removing their device information.</p><p>The server MUST remove all data about this user if <codeclass="language-plaintext highlighter-rouge">unregistering</code> is <codeclass="language-plaintext highlighter-rouge">true</code>, apart from the <codeclass="language-plaintext highlighter-rouge">hash</code> of the public key and the <codeclass="language-plaintext highlighter-rouge">version</code> of the last options, in order to make sure that old messages are not processed.</p><p>A client MAY unregister from a server on explicit logout if multiple chat keys are used on a single device.</p><h2id="advertising-a-push-notification-server"><ahref="#advertising-a-push-notification-server"class="anchor-heading"aria-labelledby="advertising-a-push-notification-server"><svgviewBox="0 0 16 16"aria-hidden="true"><usexlink:href="#svg-link"></use></svg></a> Advertising a push notification server </h2><p>Each user registered with one or more push notification servers SHOULD advertise periodically the push notification services that they have registered with for each device they own.</p><divclass="language-protobuf highlighter-rouge"><divclass="highlight"><preclass="highlight"><code><spanclass="kd">message</span><spanclass="nc">PushNotificationQueryInfo</span><spanclass="p">{</span>
</code></pre></div></div><p>The message MUST be wrapped in a <ahref="../stable/6-payloads.6#payload-wrapper"><codeclass="language-plaintext highlighter-rouge">ApplicationMetadataMessage</code></a> with type set to <codeclass="language-plaintext highlighter-rouge">PUSH_NOTIFICATION_QUERY_INFO</code>.</p><p>If no filtering is done based on public keys, the access token SHOULD be included in the advertisement. Otherwise it SHOULD be left empty.</p><p>This SHOULD be advertised on the <ahref="./10-waku-usage.md#contact-code-topic">contact code topic</a> and SHOULD be coupled with normal contact-code advertisement.</p><p>Every time a user register or re-register with a push notification service, their contact-code SHOULD be re-advertised.</p><p>Multiple servers MAY be advertised for the same <codeclass="language-plaintext highlighter-rouge">installation_id</code> for redundancy reasons.</p><h2id="discovering-a-push-notification-server"><ahref="#discovering-a-push-notification-server"class="anchor-heading"aria-labelledby="discovering-a-push-notification-server"><svgviewBox="0 0 16 16"aria-hidden="true"><usexlink:href="#svg-link"></use></svg></a> Discovering a push notification server </h2><p>To discover a push notification service for a given user, their <ahref="./10-waku-usage.md#contact-code-topic">contact code topic</a> SHOULD be listened to. A mailserver can be queried for the specific topic to retrieve the most up-to-date contact code.</p><h2id="querying-the-push-notification-server"><ahref="#querying-the-push-notification-server"class="anchor-heading"aria-labelledby="querying-the-push-notification-server"><svgviewBox="0 0 16 16"aria-hidden="true"><usexlink:href="#svg-link"></use></svg></a> Querying the push notification server </h2><p>If a token is not present in the latest advertisement for a user, the server SHOULD be queried directly.</p><p>To query a server a message:</p><divclass="language-protobuf highlighter-rouge"><divclass="highlight"><preclass="highlight"><code><spanclass="kd">message</span><spanclass="nc">PushNotificationQuery</span><spanclass="p">{</span>
</code></pre></div></div><p>The message MUST be wrapped in a <ahref="../stable/6-payloads.6#payload-wrapper"><codeclass="language-plaintext highlighter-rouge">ApplicationMetadataMessage</code></a> with type set to <codeclass="language-plaintext highlighter-rouge">PUSH_NOTIFICATION_QUERY</code>.</p><p>MUST be sent to the server on the topic derived from the hashed public key of the key we are querying, as <ahref="#query-topic">described above</a>.</p><p>An ephemeral key SHOULD be used and SHOULD NOT be encrypted using the <ahref="../docs/stable/5-secure-transport.md">secure transport</a>.</p><p>If the server has information about the client a response MUST be sent:</p><divclass="language-protobuf highlighter-rouge"><divclass="highlight"><preclass="highlight"><code><spanclass="kd">message</span><spanclass="nc">PushNotificationQueryInfo</span><spanclass="p">{</span>
</code></pre></div></div><p>A <codeclass="language-plaintext highlighter-rouge">PushNotificationQueryResponse</code> message MUST be wrapped in a <ahref="../stable/6-payloads.6#payload-wrapper"><codeclass="language-plaintext highlighter-rouge">ApplicationMetadataMessage</code></a> with type set to <codeclass="language-plaintext highlighter-rouge">PUSH_NOTIFICATION_QUERY_RESPONSE</code>.</p><p>Otherwise a response MUST NOT be sent.</p><p>If <codeclass="language-plaintext highlighter-rouge">allowed_key_list</code> is not set <codeclass="language-plaintext highlighter-rouge">access_token</code> MUST be set and <codeclass="language-plaintext highlighter-rouge">allowed_key_list</code> MUST NOT be set.</p><p>If <codeclass="language-plaintext highlighter-rouge">allowed_key_list</code> is set <codeclass="language-plaintext highlighter-rouge">allowed_key_list</code> MUST be set and <codeclass="language-plaintext highlighter-rouge">access_token</code> MUST NOT be set.</p><p>If <codeclass="language-plaintext highlighter-rouge">access_token</code> is returned, the <codeclass="language-plaintext highlighter-rouge">access_token</code> SHOULD be used to send push notifications.</p><p>If <codeclass="language-plaintext highlighter-rouge">allowed_key_list</code> are returned, the client SHOULD decrypt each token by generating an <codeclass="language-plaintext highlighter-rouge">AES-GCM</code> symmetric key from the Diffie–Hellman between the target client and itself If AES decryption succeeds it will return a valid <ahref="https://tools.ietf.org/html/rfc4122"><codeclass="language-plaintext highlighter-rouge">uuid</code></a> which is what is used for access_token. The token SHOULD be used to send push notifications.</p><p>The response MUST be sent on the [partitioned topic][./10-waku-usage.md#partitioned-topic] of the sender and MUST not be encrypted using the <ahref="../docs/stable/5-secure-transport.md">secure transport</a> to facilitate the usage of ephemeral keys.</p><p>On receiving a response a client MUST verify <codeclass="language-plaintext highlighter-rouge">grant</code> to ensure that the server has been authorized to send push notification to a given client.</p><h2id="sending-a-push-notification"><ahref="#sending-a-push-notification"class="anchor-heading"aria-labelledby="sending-a-push-notification"><svgviewBox="0 0 16 16"aria-hidden="true"><usexlink:href="#svg-link"></use></svg></a> Sending a push notification </h2><p>When sending a push notification, only the <codeclass="language-plaintext highlighter-rouge">installation_id</code> for the devices targeted by the message SHOULD be used.</p><p>If a message is for all the user devices, all the <codeclass="language-plaintext highlighter-rouge">installation_id</code> known to the client MAY be used.</p><p>The number of devices MAY be capped in order to reduce resource consumption.</p><p>At least 3 devices SHOULD be targeted, ordered by last activity.</p><p>For any device that a token is available, or that a token is successfully queried, a push notification message SHOULD be sent to the corresponding push notification server.</p><divclass="language-protobuf highlighter-rouge"><divclass="highlight"><preclass="highlight"><code><spanclass="kd">message</span><spanclass="nc">PushNotification</span><spanclass="p">{</span>
</code></pre></div></div><p>A <codeclass="language-plaintext highlighter-rouge">PushNotificationRequest</code> message MUST be wrapped in a <ahref="../stable/6-payloads.6#payload-wrapper"><codeclass="language-plaintext highlighter-rouge">ApplicationMetadataMessage</code></a> with type set to <codeclass="language-plaintext highlighter-rouge">PUSH_NOTIFICATION_REQUEST</code>.</p><p>Where <codeclass="language-plaintext highlighter-rouge">message</code> is the encrypted payload of the message and <codeclass="language-plaintext highlighter-rouge">chat_id</code> is the <codeclass="language-plaintext highlighter-rouge">SHAKE-256</code> of the <codeclass="language-plaintext highlighter-rouge">chat_id</code>. <codeclass="language-plaintext highlighter-rouge">message_id</code> is the id of the message <codeclass="language-plaintext highlighter-rouge">author</code> is the <codeclass="language-plaintext highlighter-rouge">SHAKE-256</code> of the public key of the sender.</p><p>If multiple server are available for a given push notification, only one notification MUST be sent.</p><p>If no response is received a client SHOULD wait at least 3 seconds, after which the request MAY be retried against a different server</p><p>This message SHOULD be sent using an ephemeral key.</p><p>On receiving the message, the push notification server MUST validate the access token. If the access token is valid, a notification MUST be sent to the gorush instance with the following data:</p><divclass="language-plaintext highlighter-rouge"><divclass="highlight"><preclass="highlight"><code>{
</code></pre></div></div><p>Where platform is <codeclass="language-plaintext highlighter-rouge">1</code> for IOS and <codeclass="language-plaintext highlighter-rouge">2</code> for Firebase, according to the <ahref="https://github.com/appleboy/gorush">gorush documentation</a></p><p>A server MUST return a response message:</p><divclass="language-protobuf highlighter-rouge"><divclass="highlight"><preclass="highlight"><code><spanclass="kd">message</span><spanclass="nc">PushNotificationReport</span><spanclass="p">{</span>
</code></pre></div></div><p>A <codeclass="language-plaintext highlighter-rouge">PushNotificationResponse</code> message MUST be wrapped in a <ahref="../stable/6-payloads.6#payload-wrapper"><codeclass="language-plaintext highlighter-rouge">ApplicationMetadataMessage</code></a> with type set to <codeclass="language-plaintext highlighter-rouge">PUSH_NOTIFICATION_RESPONSE</code>.</p><p>Where <codeclass="language-plaintext highlighter-rouge">message_id</code> is the <codeclass="language-plaintext highlighter-rouge">message_id</code> sent by the client.</p><p>The response MUST be sent on the [partitioned topic][./10-waku-usage.md#partitioned-topic] of the sender and MUST not be encrypted using the <ahref="../docs/stable/5-secure-transport.md">secure transport</a> to facilitate the usage of ephemeral keys.</p><p>If the request is accepted <codeclass="language-plaintext highlighter-rouge">success</code> MUST be set to <codeclass="language-plaintext highlighter-rouge">true</code>. Otherwise <codeclass="language-plaintext highlighter-rouge">success</code> MUST be set to <codeclass="language-plaintext highlighter-rouge">false</code>.</p><p>If <codeclass="language-plaintext highlighter-rouge">error</code> is <codeclass="language-plaintext highlighter-rouge">BAD_TOKEN</code> the client MAY query again the server for the token and retry the request.</p><p>If <codeclass="language-plaintext highlighter-rouge">error</code> is <codeclass="language-plaintext highlighter-rouge">INTERNAL_ERROR</code> the client MAY retry the request.</p><h2id="flow"><ahref="#flow"class="anchor-heading"aria-labelledby="flow"><svgviewBox="0 0 16 16"aria-hidden="true"><usexlink:href="#svg-link"></use></svg></a> Flow </h2><h3id="registration-process"><ahref="#registration-process"class="anchor-heading"aria-labelledby="registration-process"><svgviewBox="0 0 16 16"aria-hidden="true"><usexlink:href="#svg-link"></use></svg></a> Registration process </h3><ul><li>A client will generate a notification token through <codeclass="language-plaintext highlighter-rouge">APN</code> or <codeclass="language-plaintext highlighter-rouge">Firebase</code>.</li><li>The client will <ahref="#registering-with-the-push-notification-service">register</a> with one or more push notification server of their choosing.</li><li>The server should process the response and respond according to the success of the operation</li><li>If the request is not successful it might be retried, and adjusted according to the response. A different server can be also used.</li><li>Once the request is successful the client should <ahref="#advertising-a-push-notification-server">advertise</a> the new coordinates</li></ul><h3id="sending-a-notification"><ahref="#sending-a-notification"class="anchor-heading"aria-labelledby="sending-a-notification"><svgviewBox="0 0 16 16"aria-hidden="true"><usexlink:href="#svg-link"></use></svg></a> Sending a notification </h3><ul><li>A client should prepare a message and extract the targeted installation-ids</li><li>It should retrieve the most up to date information for a given user, either by querying a push notification server, a mailserver if not listening already to the given topic, or checking the database locally</li><li>It should then <ahref="#sending-a-push-notification">send</a> a push notification according to the rules described</li><li>The server should then send a request to the gorush server including all the required information</li></ul><h3id="receiving-a-push-notification"><ahref="#receiving-a-push-notification"class="anchor-heading"aria-labelledby="receiving-a-push-notification"><svgviewBox="0 0 16 16"aria-hidden="true"><usexlink:href="#svg-link"></use></svg></a> Receiving a push notification </h3><ul><li>On receiving the notification, a client can open the right account by checking the <codeclass="language-plaintext highlighter-rouge">installation_id</code> included. The <codeclass="language-plaintext highlighter-rouge">chat_id</code> MAY be used to open the chat if present.</li><li><