mirror of https://github.com/status-im/specs.git
119 lines
73 KiB
HTML
119 lines
73 KiB
HTML
<!DOCTYPE html> <html lang="en-US"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=Edge"> <title>16/PUSH-NOTIFICATION-SERVER - Status Specification</title> <link rel="shortcut icon" href="/favicon.ico" type="image/x-icon"> <link rel="stylesheet" href="/assets/css/just-the-docs-default.css"> <script type="text/javascript" src="/assets/js/vendor/lunr.min.js"></script> <script type="text/javascript" src="/assets/js/just-the-docs.js"></script> <meta name="viewport" content="width=device-width, initial-scale=1"> <!-- Begin Jekyll SEO tag v2.7.1 --> <title>16/PUSH-NOTIFICATION-SERVER | Status Specification</title> <meta name="generator" content="Jekyll v4.2.1" /> <meta property="og:title" content="16/PUSH-NOTIFICATION-SERVER" /> <meta property="og:locale" content="en_US" /> <link rel="canonical" href="https://specs.status.im/raw/16" /> <meta property="og:url" content="https://specs.status.im/raw/16" /> <meta property="og:site_name" content="Status Specification" /> <meta name="twitter:card" content="summary" /> <meta property="twitter:title" content="16/PUSH-NOTIFICATION-SERVER" /> <script type="application/ld+json"> {"@type":"WebPage","url":"https://specs.status.im/raw/16","headline":"16/PUSH-NOTIFICATION-SERVER","@context":"https://schema.org"}</script> <!-- End Jekyll SEO tag --> </head> <body> <svg xmlns="http://www.w3.org/2000/svg" style="display: none;"> <symbol id="svg-link" viewBox="0 0 24 24"> <title>Link</title> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-link"> <path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path> </svg> </symbol> <symbol id="svg-search" viewBox="0 0 24 24"> <title>Search</title> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-search"> <circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line> </svg> </symbol> <symbol id="svg-menu" viewBox="0 0 24 24"> <title>Menu</title> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-menu"> <line x1="3" y1="12" x2="21" y2="12"></line><line x1="3" y1="6" x2="21" y2="6"></line><line x1="3" y1="18" x2="21" y2="18"></line> </svg> </symbol> <symbol id="svg-arrow-right" viewBox="0 0 24 24"> <title>Expand</title> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-chevron-right"> <polyline points="9 18 15 12 9 6"></polyline> </svg> </symbol> <symbol id="svg-doc" viewBox="0 0 24 24"> <title>Document</title> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-file"> <path d="M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z"></path><polyline points="13 2 13 9 20 9"></polyline> </svg> </symbol> </svg> <div class="side-bar"> <div class="site-header"> <a href="https://specs.status.im/" class="site-title lh-tight"> Status Specification </a> <a href="#" id="menu-button" class="site-button"> <svg viewBox="0 0 24 24" class="icon"><use xlink:href="#svg-menu"></use></svg> </a> </div> <nav role="navigation" aria-label="Main" id="site-nav" class="site-nav"> <ul class="nav-list"><li class="nav-list-item"><a href="#" class="nav-list-expander"><svg viewBox="0 0 24 24"><use xlink:href="#svg-arrow-right"></use></svg></a><a href="https://specs.status.im/stable/" class="nav-list-link">Stable specs</a><ul class="nav-list "><li class="nav-list-item "><a href="https://specs.status.im/stable/1" class="nav-list-link">1/CLIENT</a></li><li class="nav-list-item "><a href="https://specs.status.im/stable/10" class="nav-list-link">10/WAKU-USAGE</a></li><li class="nav-list-item "><a href="https://specs.status.im/stable/11" class="nav-list-link">11/WAKU-MAILSERVER</a></li><li class="nav-list-item "><a href="https://specs.status.im/stable/15" class="nav-list-link">15/NOTIFICATIONS</a></li><li class="nav-list-item "><a href="https://specs.status.im/stable/2" class="nav-list-link">2/ACCOUNT</a></li><li class="nav-list-item "><a href="https://specs.status.im/stable/3" class="nav-list-link">3/WHISPER-USAGE</a></li><li class="nav-list-item "><a href="https://specs.status.im/stable/4" class="nav-list-link">4/WHISPER-MAILSERVER</a></li><li class="nav-list-item "><a href="https://specs.status.im/stable/5" class="nav-list-link">5/SECURE-TRANSPORT</a></li><li class="nav-list-item "><a href="https://specs.status.im/stable/6" class="nav-list-link">6/PAYLOADS</a></li><li class="nav-list-item "><a href="https://specs.status.im/stable/8" class="nav-list-link">8/EIPS</a></li><li class="nav-list-item "><a href="https://specs.status.im/stable/9" class="nav-list-link">9/ETHEREUM-USAGE</a></li></ul></li><li class="nav-list-item"><a href="#" class="nav-list-expander"><svg viewBox="0 0 24 24"><use xlink:href="#svg-arrow-right"></use></svg></a><a href="https://specs.status.im/draft/" class="nav-list-link">Draft specs</a><ul class="nav-list "><li class="nav-list-item "><a href="https://specs.status.im/draft/12" class="nav-list-link">12/IPFS gateway for Sticker Pack</a></li><li class="nav-list-item "><a href="https://specs.status.im/draft/13" class="nav-list-link">13/3RD-PARTY-USAGE</a></li><li class="nav-list-item "><a href="https://specs.status.im/draft/14" class="nav-list-link">14/Dapp browser API usage</a></li><li class="nav-list-item "><a href="https://specs.status.im/draft/16" class="nav-list-link">16/Keycard Usage for Wallet and Chat Keys</a></li><li class="nav-list-item "><a href="https://specs.status.im/draft/3" class="nav-list-link">3/WHISPER-USAGE</a></li><li class="nav-list-item "><a href="https://specs.status.im/draft/6" class="nav-list-link">6/PAYLOADS</a></li><li class="nav-list-item "><a href="https://specs.status.im/draft/7" class="nav-list-link">7/GROUP-CHAT</a></li></ul></li><li class="nav-list-item active"><a href="#" class="nav-list-expander"><svg viewBox="0 0 24 24"><use xlink:href="#svg-arrow-right"></use></svg></a><a href="https://specs.status.im/raw/" class="nav-list-link">Raw specs</a><ul class="nav-list "><li class="nav-list-item active"><a href="https://specs.status.im/raw/16" class="nav-list-link active">16/PUSH-NOTIFICATION-SERVER</a></li></ul></li><li class="nav-list-item"><a href="https://specs.status.im/development" class="nav-list-link">DEVELOPMENT</a></li><li class="nav-list-item"><a href="https://specs.status.im/style-guideline" class="nav-list-link">STYLE-GUIDELINE</a></li></ul> </nav> <footer class="site-footer"> This site uses <a href="https://github.com/pmarsceill/just-the-docs">Just the Docs</a>, a documentation theme for Jekyll. </footer> </div> <div class="main" id="top"> <div id="main-header" class="main-header"> <div class="search"> <div class="search-input-wrap"> <input type="text" id="search-input" class="search-input" tabindex="0" placeholder="Search Status Specification" aria-label="Search Status Specification" autocomplete="off"> <label for="search-input" class="search-label"><svg viewBox="0 0 24 24" class="search-icon"><use xlink:href="#svg-search"></use></svg></label> </div> <div id="search-results" class="search-results"></div> </div> </div> <div id="main-content-wrap" class="main-content-wrap"> <nav aria-label="Breadcrumb" class="breadcrumb-nav"> <ol class="breadcrumb-nav-list"> <li class="breadcrumb-nav-list-item"><a href="https://specs.status.im/raw/">Raw specs</a></li> <li class="breadcrumb-nav-list-item"><span>16/PUSH-NOTIFICATION-SERVER</span></li> </ol> </nav> <div id="main-content" class="main-content" role="main"> <h1 id="16push-notification-server"> <a href="#16push-notification-server" class="anchor-heading" aria-labelledby="16push-notification-server"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink:href="#svg-link"></use></svg></a> 16/PUSH-NOTIFICATION-SERVER </h1> <blockquote> <p>Version: 0.1</p> <p>Status: Raw</p> <p>Authors: Andrea Maria Piana <a href="mailto:andreap@status.im">andreap@status.im</a></p> </blockquote> <ul> <li><a href="#16push-notification-server">Push Notification Server</a> <ul> <li><a href="#reason">Reason</a></li> <li><a href="#requirements">Requirements</a></li> <li><a href="#components">Components</a></li> <li><a href="#registering-with-the-push-notification-service">Registering with the push notification service</a></li> <li><a href="#re-registering-with-the-push-notification-server">Re-registering with the push notification service</a></li> <li><a href="#changing-options">Changing options</a></li> <li><a href="#unregistering-from-push-notifications">Unregistering from push notifications</a></li> <li><a href="#advertising-a-push-notification-server">Advertising a push notification server</a></li> <li><a href="#discovering-a-push-notification-server">Discovering a push notification server</a></li> <li><a href="#querying-the-push-notification-server">Querying the push notification service</a></li> <li><a href="#sending-a-push-notification">Sending a push notification</a></li> <li><a href="#flow">Flow</a> <ul> <li><a href="#registration-process">Registration process</a></li> <li><a href="#sending-a-notification">Sending a notification</a></li> <li><a href="#receiving-a-push-notification">Receiving a push notification</a></li> </ul> </li> <li><a href="#protobuf-description">Protobuf description</a> <ul> <li><a href="#pushnotificationregistration">PushNotificationRegistration</a></li> <li><a href="#pushnotificationregistrationresponse">PushNotificationRegistrationResponse</a></li> <li><a href="#contactcodeadvertisement">ContactCodeAdvertisement</a></li> <li><a href="#pushnotificationquery">PushNotificationQuery</a></li> <li><a href="#pushnotificationqueryinfo">PushNotificationQueryInfo</a></li> <li><a href="#pushnotificationqueryresponse">PushNotificationQueryResponse</a></li> <li><a href="#pushnotification">PushNotification</a></li> <li><a href="#pushnotificationrequest">PushNotificationRequest</a></li> <li><a href="#pushnotificationacknowledgement">PushNotificationResponse</a></li> <li><a href="#pushnotificationreport">PushNotificationReport</a></li> </ul> </li> <li><a href="#anonymous-mode-of-operations">Anonymous mode of operations</a></li> <li><a href="#security-considerations">Security considerations</a></li> <li><a href="#faq">FAQ</a></li> <li><a href="#changelog">Changelog</a> <ul> <li><a href="#version-01">Version 0.1</a></li> </ul> </li> <li><a href="#copyright">Copyright</a></li> </ul> </li> </ul> <h2 id="reason"> <a href="#reason" class="anchor-heading" aria-labelledby="reason"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink:href="#svg-link"></use></svg></a> Reason </h2> <p>Push notifications for iOS devices and some Android devices can only be implemented by relying on <a href="https://developer.apple.com/library/archive/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/APNSOverview.html#//apple_ref/doc/uid/TP40008194-CH8-SW1">APN service</a> for iOS or <a href="https://firebase.google.com/">Firebase</a>.</p> <p>This is useful for Android devices that do not support foreground services or that often kill the foreground service.</p> <p>iOS only allows certain kind of applications to keep a connection open when in the background, VoIP for example, which current status client does not qualify for.</p> <p>Applications on iOS can also request execution time when they are in the <a href="https://developer.apple.com/documentation/uikit/app_and_environment/scenes/preparing_your_ui_to_run_in_the_background/updating_your_app_with_background_app_refresh">background</a> but it has a limited set of use cases, for example it won’t schedule any time if the application was force quit, and generally is not responsive enough to implement a push notification system.</p> <p>Therefore Status provides a set of Push notification services that can be used to achieve this functionality.</p> <p>Because this can’t be safely implemented in a privacy preserving manner, clients MUST be given an option to opt-in to receiving and sending push notifications. They are disabled by default.</p> <h2 id="requirements"> <a href="#requirements" class="anchor-heading" aria-labelledby="requirements"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink:href="#svg-link"></use></svg></a> Requirements </h2> <p>The party releasing the app MUST possess a certificate for the Apple Push Notification service and its has to run a <a href="https://github.com/appleboy/gorush">gorush</a> publicly accessible server for sending the actual notification. The party releasing the app, Status in this case, needs to run its own <a href="https://github.com/appleboy/gorush">gorush</a></p> <h2 id="components"> <a href="#components" class="anchor-heading" aria-labelledby="components"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink:href="#svg-link"></use></svg></a> Components </h2> <h3 id="gorush-instance"> <a href="#gorush-instance" class="anchor-heading" aria-labelledby="gorush-instance"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink:href="#svg-link"></use></svg></a> Gorush instance </h3> <p>A <a href="https://github.com/appleboy/gorush">gorush</a> instance MUST be publicly available, this will be used only by push notification servers.</p> <h3 id="push-notification-server"> <a href="#push-notification-server" class="anchor-heading" aria-labelledby="push-notification-server"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink:href="#svg-link"></use></svg></a> Push notification server </h3> <p>A push notification server used by clients to register for receiving and sending push notifications.</p> <h3 id="registering-client"> <a href="#registering-client" class="anchor-heading" aria-labelledby="registering-client"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink:href="#svg-link"></use></svg></a> Registering client </h3> <p>A Status client that wants to receive push notifications</p> <h3 id="sending-client"> <a href="#sending-client" class="anchor-heading" aria-labelledby="sending-client"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink:href="#svg-link"></use></svg></a> Sending client </h3> <p>A Status client that wants to send push notifications</p> <h2 id="registering-with-the-push-notification-service"> <a href="#registering-with-the-push-notification-service" class="anchor-heading" aria-labelledby="registering-with-the-push-notification-service"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink:href="#svg-link"></use></svg></a> Registering with the push notification service </h2> <p>A client MAY register with one or more Push Notification services of their choice.</p> <p>A <code class="language-plaintext highlighter-rouge">PNR message</code> (Push Notification Registration) MUST be sent to the <a href="../stable/10-waku-usage.md#partitioned-topic">partitioned topic</a> for the public key of the node, encrypted with this key.</p> <p>The message MUST be wrapped in a <a href="../stable/6-payloads.6#payload-wrapper"><code class="language-plaintext highlighter-rouge">ApplicationMetadataMessage</code></a> with type set to <code class="language-plaintext highlighter-rouge">PUSH_NOTIFICATION_REGISTRATION</code>.</p> <p>The marshaled protobuf payload MUST also be encrypted with AES-GCM using the Diffie–Hellman key generated from the client and server identity.</p> <p>This is done in order to ensure that the extracted key from the signature will be considered invalid if it can’t decrypt the payload.</p> <p>The content of the message MUST contain the following <a href="https://developers.google.com/protocol-buffers/">protobuf record</a>:</p> <div class="language-protobuf highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">message</span> <span class="nc">PushNotificationRegistration</span> <span class="p">{</span>
|
||
<span class="kd">enum</span> <span class="n">TokenType</span> <span class="p">{</span>
|
||
<span class="na">UNKNOWN_TOKEN_TYPE</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
|
||
<span class="na">APN_TOKEN</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
|
||
<span class="na">FIREBASE_TOKEN</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>
|
||
<span class="p">}</span>
|
||
<span class="n">TokenType</span> <span class="na">token_type</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
|
||
<span class="kt">string</span> <span class="na">device_token</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>
|
||
<span class="kt">string</span> <span class="na">installation_id</span> <span class="o">=</span> <span class="mi">3</span><span class="p">;</span>
|
||
<span class="kt">string</span> <span class="na">access_token</span> <span class="o">=</span> <span class="mi">4</span><span class="p">;</span>
|
||
<span class="kt">bool</span> <span class="na">enabled</span> <span class="o">=</span> <span class="mi">5</span><span class="p">;</span>
|
||
<span class="kt">uint64</span> <span class="na">version</span> <span class="o">=</span> <span class="mi">6</span><span class="p">;</span>
|
||
<span class="k">repeated</span> <span class="kt">bytes</span> <span class="na">allowed_key_list</span> <span class="o">=</span> <span class="mi">7</span><span class="p">;</span>
|
||
<span class="k">repeated</span> <span class="kt">bytes</span> <span class="na">blocked_chat_list</span> <span class="o">=</span> <span class="mi">8</span><span class="p">;</span>
|
||
<span class="kt">bool</span> <span class="na">unregister</span> <span class="o">=</span> <span class="mi">9</span><span class="p">;</span>
|
||
<span class="kt">bytes</span> <span class="na">grant</span> <span class="o">=</span> <span class="mi">10</span><span class="p">;</span>
|
||
<span class="kt">bool</span> <span class="na">allow_from_contacts_only</span> <span class="o">=</span> <span class="mi">11</span><span class="p">;</span>
|
||
<span class="kt">string</span> <span class="na">apn_topic</span> <span class="o">=</span> <span class="mi">12</span><span class="p">;</span>
|
||
<span class="kt">bool</span> <span class="na">block_mentions</span> <span class="o">=</span> <span class="mi">13</span><span class="p">;</span>
|
||
<span class="k">repeated</span> <span class="kt">bytes</span> <span class="na">allowed_mentions_chat_list</span> <span class="o">=</span> <span class="mi">14</span><span class="p">;</span>
|
||
<span class="p">}</span>
|
||
</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 <code class="language-plaintext highlighter-rouge">token_type</code> is supported</li> <li>it MUST verify that <code class="language-plaintext highlighter-rouge">device_token</code> is non empty</li> <li>it MUST verify that <code class="language-plaintext highlighter-rouge">installation_id</code> is non empty</li> <li>it MUST verify that <code class="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 <code class="language-plaintext highlighter-rouge">grant</code> is non empty and according to the <a href="#server-grant">specs</a></li> <li>it MUST verify that <code class="language-plaintext highlighter-rouge">access_token</code> is a valid <a href="https://tools.ietf.org/html/rfc4122"><code class="language-plaintext highlighter-rouge">uuid</code></a></li> <li>it MUST verify that <code class="language-plaintext highlighter-rouge">apn_topic</code> is set if <code class="language-plaintext highlighter-rouge">token_type</code> is <code class="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 <code class="language-plaintext highlighter-rouge">token_type</code> is not supported, a response MUST be sent with <code class="language-plaintext highlighter-rouge">error</code> set to <code class="language-plaintext highlighter-rouge">UNSUPPORTED_TOKEN_TYPE</code>.</p> <p>If <code class="language-plaintext highlighter-rouge">token</code>,<code class="language-plaintext highlighter-rouge">installation_id</code>,<code class="language-plaintext highlighter-rouge">device_tokens</code>,<code class="language-plaintext highlighter-rouge">version</code> are empty, a response MUST be sent with <code class="language-plaintext highlighter-rouge">error</code> set to <code class="language-plaintext highlighter-rouge">MALFORMED_MESSAGE</code>.</p> <p>If the <code class="language-plaintext highlighter-rouge">version</code> is equal or less than the currently stored version, a response MUST be sent with <code class="language-plaintext highlighter-rouge">error</code> set to <code class="language-plaintext highlighter-rouge">VERSION_MISMATCH</code>.</p> <p>If any other error occurs the <code class="language-plaintext highlighter-rouge">error</code> should be set to <code class="language-plaintext highlighter-rouge">INTERNAL_ERROR</code>.</p> <p>If the response is successful <code class="language-plaintext highlighter-rouge">success</code> MUST be set to <code class="language-plaintext highlighter-rouge">true</code> otherwise a response MUST be sent with <code class="language-plaintext highlighter-rouge">success</code> set to <code class="language-plaintext highlighter-rouge">false</code>.</p> <p><code class="language-plaintext highlighter-rouge">request_id</code> should be set to the <code class="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 <a href="../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> <div class="language-protobuf highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">message</span> <span class="nc">PushNotificationRegistrationResponse</span> <span class="p">{</span>
|
||
<span class="kt">bool</span> <span class="na">success</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
|
||
<span class="n">ErrorType</span> <span class="na">error</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>
|
||
<span class="kt">bytes</span> <span class="na">request_id</span> <span class="o">=</span> <span class="mi">3</span><span class="p">;</span>
|
||
|
||
<span class="kd">enum</span> <span class="n">ErrorType</span> <span class="p">{</span>
|
||
<span class="na">UNKNOWN_ERROR_TYPE</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
|
||
<span class="na">MALFORMED_MESSAGE</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
|
||
<span class="na">VERSION_MISMATCH</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>
|
||
<span class="na">UNSUPPORTED_TOKEN_TYPE</span> <span class="o">=</span> <span class="mi">3</span><span class="p">;</span>
|
||
<span class="na">INTERNAL_ERROR</span> <span class="o">=</span> <span class="mi">4</span><span class="p">;</span>
|
||
<span class="p">}</span>
|
||
<span class="p">}</span>
|
||
</code></pre></div></div> <p>The message MUST be wrapped in a <a href="../stable/6-payloads.6#payload-wrapper"><code class="language-plaintext highlighter-rouge">ApplicationMetadataMessage</code></a> with type set to <code class="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 <code class="language-plaintext highlighter-rouge">success</code> is <code class="language-plaintext highlighter-rouge">true</code> the client has registered successfully.</p> <p>If <code class="language-plaintext highlighter-rouge">success</code> is <code class="language-plaintext highlighter-rouge">false</code>:</p> <ul> <li>If <code class="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 <code class="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 <a href="#advertising-a-push-notification-server">advertised</a> as described below</p> <h3 id="query-topic"> <a href="#query-topic" class="anchor-heading" aria-labelledby="query-topic"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink:href="#svg-link"></use></svg></a> Query topic </h3> <p>On successful registration the server MUST be listening to the topic derived from:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> 0XHexEncode(Shake256(CompressedClientPublicKey))
|
||
</code></pre></div></div> <p>Using the topic derivation algorithm described <a href="../stable/10-waku-usage.md#public-chats">here</a> and listen for client queries.</p> <h3 id="server-grant"> <a href="#server-grant" class="anchor-heading" aria-labelledby="server-grant"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink: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> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="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> <h2 id="re-registering-with-the-push-notification-server"> <a href="#re-registering-with-the-push-notification-server" class="anchor-heading" aria-labelledby="re-registering-with-the-push-notification-server"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink: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 <code class="language-plaintext highlighter-rouge">PushNotificationRegistration</code> and increment <code class="language-plaintext highlighter-rouge">version</code> if necessary.</p> <p>Once re-registered, a client SHOULD advertise the changes.</p> <h2 id="changing-options"> <a href="#changing-options" class="anchor-heading" aria-labelledby="changing-options"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink:href="#svg-link"></use></svg></a> Changing options </h2> <p>This is handled in exactly the same way as re-registering above.</p> <h2 id="unregistering-from-push-notifications"> <a href="#unregistering-from-push-notifications" class="anchor-heading" aria-labelledby="unregistering-from-push-notifications"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink:href="#svg-link"></use></svg></a> Unregistering from push notifications </h2> <p>To unregister a client MUST send a <code class="language-plaintext highlighter-rouge">PushNotificationRegistration</code> request as described above with <code class="language-plaintext highlighter-rouge">unregister</code> set to <code class="language-plaintext highlighter-rouge">true</code>, or removing their device information.</p> <p>The server MUST remove all data about this user if <code class="language-plaintext highlighter-rouge">unregistering</code> is <code class="language-plaintext highlighter-rouge">true</code>, apart from the <code class="language-plaintext highlighter-rouge">hash</code> of the public key and the <code class="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> <h2 id="advertising-a-push-notification-server"> <a href="#advertising-a-push-notification-server" class="anchor-heading" aria-labelledby="advertising-a-push-notification-server"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink: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> <div class="language-protobuf highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">message</span> <span class="nc">PushNotificationQueryInfo</span> <span class="p">{</span>
|
||
<span class="kt">string</span> <span class="na">access_token</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
|
||
<span class="kt">string</span> <span class="na">installation_id</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>
|
||
<span class="kt">bytes</span> <span class="na">public_key</span> <span class="o">=</span> <span class="mi">3</span><span class="p">;</span>
|
||
<span class="k">repeated</span> <span class="kt">bytes</span> <span class="na">allowed_user_list</span> <span class="o">=</span> <span class="mi">4</span><span class="p">;</span>
|
||
<span class="kt">bytes</span> <span class="na">grant</span> <span class="o">=</span> <span class="mi">5</span><span class="p">;</span>
|
||
<span class="kt">uint64</span> <span class="na">version</span> <span class="o">=</span> <span class="mi">6</span><span class="p">;</span>
|
||
<span class="kt">bytes</span> <span class="na">server_public_key</span> <span class="o">=</span> <span class="mi">7</span><span class="p">;</span>
|
||
<span class="p">}</span>
|
||
|
||
<span class="kd">message</span> <span class="nc">ContactCodeAdvertisement</span> <span class="p">{</span>
|
||
<span class="k">repeated</span> <span class="n">PushNotificationQueryInfo</span> <span class="na">push_notification_info</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
|
||
<span class="p">}</span>
|
||
</code></pre></div></div> <p>The message MUST be wrapped in a <a href="../stable/6-payloads.6#payload-wrapper"><code class="language-plaintext highlighter-rouge">ApplicationMetadataMessage</code></a> with type set to <code class="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 <a href="./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 <code class="language-plaintext highlighter-rouge">installation_id</code> for redundancy reasons.</p> <h2 id="discovering-a-push-notification-server"> <a href="#discovering-a-push-notification-server" class="anchor-heading" aria-labelledby="discovering-a-push-notification-server"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink:href="#svg-link"></use></svg></a> Discovering a push notification server </h2> <p>To discover a push notification service for a given user, their <a href="./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> <h2 id="querying-the-push-notification-server"> <a href="#querying-the-push-notification-server" class="anchor-heading" aria-labelledby="querying-the-push-notification-server"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink: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> <div class="language-protobuf highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">message</span> <span class="nc">PushNotificationQuery</span> <span class="p">{</span>
|
||
<span class="k">repeated</span> <span class="kt">bytes</span> <span class="na">public_keys</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
|
||
<span class="p">}</span>
|
||
</code></pre></div></div> <p>The message MUST be wrapped in a <a href="../stable/6-payloads.6#payload-wrapper"><code class="language-plaintext highlighter-rouge">ApplicationMetadataMessage</code></a> with type set to <code class="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 <a href="#query-topic">described above</a>.</p> <p>An ephemeral key SHOULD be used and SHOULD NOT be encrypted using the <a href="../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> <div class="language-protobuf highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">message</span> <span class="nc">PushNotificationQueryInfo</span> <span class="p">{</span>
|
||
<span class="kt">string</span> <span class="na">access_token</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
|
||
<span class="kt">string</span> <span class="na">installation_id</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>
|
||
<span class="kt">bytes</span> <span class="na">public_key</span> <span class="o">=</span> <span class="mi">3</span><span class="p">;</span>
|
||
<span class="k">repeated</span> <span class="kt">bytes</span> <span class="na">allowed_user_list</span> <span class="o">=</span> <span class="mi">4</span><span class="p">;</span>
|
||
<span class="kt">bytes</span> <span class="na">grant</span> <span class="o">=</span> <span class="mi">5</span><span class="p">;</span>
|
||
<span class="kt">uint64</span> <span class="na">version</span> <span class="o">=</span> <span class="mi">6</span><span class="p">;</span>
|
||
<span class="kt">bytes</span> <span class="na">server_public_key</span> <span class="o">=</span> <span class="mi">7</span><span class="p">;</span>
|
||
<span class="p">}</span>
|
||
|
||
<span class="kd">message</span> <span class="nc">PushNotificationQueryResponse</span> <span class="p">{</span>
|
||
<span class="k">repeated</span> <span class="n">PushNotificationQueryInfo</span> <span class="na">info</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
|
||
<span class="kt">bytes</span> <span class="na">message_id</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>
|
||
<span class="kt">bool</span> <span class="na">success</span> <span class="o">=</span> <span class="mi">3</span><span class="p">;</span>
|
||
<span class="p">}</span>
|
||
</code></pre></div></div> <p>A <code class="language-plaintext highlighter-rouge">PushNotificationQueryResponse</code> message MUST be wrapped in a <a href="../stable/6-payloads.6#payload-wrapper"><code class="language-plaintext highlighter-rouge">ApplicationMetadataMessage</code></a> with type set to <code class="language-plaintext highlighter-rouge">PUSH_NOTIFICATION_QUERY_RESPONSE</code>.</p> <p>Otherwise a response MUST NOT be sent.</p> <p>If <code class="language-plaintext highlighter-rouge">allowed_key_list</code> is not set <code class="language-plaintext highlighter-rouge">access_token</code> MUST be set and <code class="language-plaintext highlighter-rouge">allowed_key_list</code> MUST NOT be set.</p> <p>If <code class="language-plaintext highlighter-rouge">allowed_key_list</code> is set <code class="language-plaintext highlighter-rouge">allowed_key_list</code> MUST be set and <code class="language-plaintext highlighter-rouge">access_token</code> MUST NOT be set.</p> <p>If <code class="language-plaintext highlighter-rouge">access_token</code> is returned, the <code class="language-plaintext highlighter-rouge">access_token</code> SHOULD be used to send push notifications.</p> <p>If <code class="language-plaintext highlighter-rouge">allowed_key_list</code> are returned, the client SHOULD decrypt each token by generating an <code class="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 <a href="https://tools.ietf.org/html/rfc4122"><code class="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 <a href="../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 <code class="language-plaintext highlighter-rouge">grant</code> to ensure that the server has been authorized to send push notification to a given client.</p> <h2 id="sending-a-push-notification"> <a href="#sending-a-push-notification" class="anchor-heading" aria-labelledby="sending-a-push-notification"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink:href="#svg-link"></use></svg></a> Sending a push notification </h2> <p>When sending a push notification, only the <code class="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 <code class="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> <div class="language-protobuf highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">message</span> <span class="nc">PushNotification</span> <span class="p">{</span>
|
||
<span class="kt">string</span> <span class="na">access_token</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
|
||
<span class="kt">string</span> <span class="na">chat_id</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>
|
||
<span class="kt">bytes</span> <span class="na">public_key</span> <span class="o">=</span> <span class="mi">3</span><span class="p">;</span>
|
||
<span class="kt">string</span> <span class="na">installation_id</span> <span class="o">=</span> <span class="mi">4</span><span class="p">;</span>
|
||
<span class="kt">bytes</span> <span class="kd">message</span> <span class="o">=</span> <span class="mi">5</span><span class="p">;</span>
|
||
<span class="n">PushNotificationType</span> <span class="na">type</span> <span class="o">=</span> <span class="mi">6</span><span class="p">;</span>
|
||
<span class="kd">enum</span> <span class="n">PushNotificationType</span> <span class="p">{</span>
|
||
<span class="na">UNKNOWN_PUSH_NOTIFICATION_TYPE</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
|
||
<span class="na">MESSAGE</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
|
||
<span class="na">MENTION</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>
|
||
<span class="p">}</span>
|
||
<span class="kt">bytes</span> <span class="na">author</span> <span class="o">=</span> <span class="mi">7</span><span class="p">;</span>
|
||
<span class="p">}</span>
|
||
|
||
<span class="kd">message</span> <span class="nc">PushNotificationRequest</span> <span class="p">{</span>
|
||
<span class="k">repeated</span> <span class="n">PushNotification</span> <span class="na">requests</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
|
||
<span class="kt">bytes</span> <span class="na">message_id</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>
|
||
<span class="p">}</span>
|
||
</code></pre></div></div> <p>A <code class="language-plaintext highlighter-rouge">PushNotificationRequest</code> message MUST be wrapped in a <a href="../stable/6-payloads.6#payload-wrapper"><code class="language-plaintext highlighter-rouge">ApplicationMetadataMessage</code></a> with type set to <code class="language-plaintext highlighter-rouge">PUSH_NOTIFICATION_REQUEST</code>.</p> <p>Where <code class="language-plaintext highlighter-rouge">message</code> is the encrypted payload of the message and <code class="language-plaintext highlighter-rouge">chat_id</code> is the <code class="language-plaintext highlighter-rouge">SHAKE-256</code> of the <code class="language-plaintext highlighter-rouge">chat_id</code>. <code class="language-plaintext highlighter-rouge">message_id</code> is the id of the message <code class="language-plaintext highlighter-rouge">author</code> is the <code class="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> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>{
|
||
"notifications": [
|
||
{
|
||
"tokens": ["token_a", "token_b"],
|
||
"platform": 1,
|
||
"message": "You have a new message",
|
||
"data": {
|
||
"chat_id": chat_id,
|
||
"message": message,
|
||
"installation_ids": [installation_id_1, installation_id_2]
|
||
}
|
||
}
|
||
]
|
||
}
|
||
</code></pre></div></div> <p>Where platform is <code class="language-plaintext highlighter-rouge">1</code> for IOS and <code class="language-plaintext highlighter-rouge">2</code> for Firebase, according to the <a href="https://github.com/appleboy/gorush">gorush documentation</a></p> <p>A server MUST return a response message:</p> <div class="language-protobuf highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">message</span> <span class="nc">PushNotificationReport</span> <span class="p">{</span>
|
||
<span class="kt">bool</span> <span class="na">success</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
|
||
<span class="n">ErrorType</span> <span class="na">error</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>
|
||
<span class="kd">enum</span> <span class="n">ErrorType</span> <span class="p">{</span>
|
||
<span class="na">UNKNOWN_ERROR_TYPE</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
|
||
<span class="na">WRONG_TOKEN</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
|
||
<span class="na">INTERNAL_ERROR</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>
|
||
<span class="na">NOT_REGISTERED</span> <span class="o">=</span> <span class="mi">3</span><span class="p">;</span>
|
||
<span class="p">}</span>
|
||
<span class="kt">bytes</span> <span class="na">public_key</span> <span class="o">=</span> <span class="mi">3</span><span class="p">;</span>
|
||
<span class="kt">string</span> <span class="na">installation_id</span> <span class="o">=</span> <span class="mi">4</span><span class="p">;</span>
|
||
<span class="p">}</span>
|
||
|
||
<span class="kd">message</span> <span class="nc">PushNotificationResponse</span> <span class="p">{</span>
|
||
<span class="kt">bytes</span> <span class="na">message_id</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
|
||
<span class="k">repeated</span> <span class="n">PushNotificationReport</span> <span class="na">reports</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>
|
||
<span class="p">}</span>
|
||
</code></pre></div></div> <p>A <code class="language-plaintext highlighter-rouge">PushNotificationResponse</code> message MUST be wrapped in a <a href="../stable/6-payloads.6#payload-wrapper"><code class="language-plaintext highlighter-rouge">ApplicationMetadataMessage</code></a> with type set to <code class="language-plaintext highlighter-rouge">PUSH_NOTIFICATION_RESPONSE</code>.</p> <p>Where <code class="language-plaintext highlighter-rouge">message_id</code> is the <code class="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 <a href="../docs/stable/5-secure-transport.md">secure transport</a> to facilitate the usage of ephemeral keys.</p> <p>If the request is accepted <code class="language-plaintext highlighter-rouge">success</code> MUST be set to <code class="language-plaintext highlighter-rouge">true</code>. Otherwise <code class="language-plaintext highlighter-rouge">success</code> MUST be set to <code class="language-plaintext highlighter-rouge">false</code>.</p> <p>If <code class="language-plaintext highlighter-rouge">error</code> is <code class="language-plaintext highlighter-rouge">BAD_TOKEN</code> the client MAY query again the server for the token and retry the request.</p> <p>If <code class="language-plaintext highlighter-rouge">error</code> is <code class="language-plaintext highlighter-rouge">INTERNAL_ERROR</code> the client MAY retry the request.</p> <h2 id="flow"> <a href="#flow" class="anchor-heading" aria-labelledby="flow"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink:href="#svg-link"></use></svg></a> Flow </h2> <h3 id="registration-process"> <a href="#registration-process" class="anchor-heading" aria-labelledby="registration-process"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink:href="#svg-link"></use></svg></a> Registration process </h3> <ul> <li>A client will generate a notification token through <code class="language-plaintext highlighter-rouge">APN</code> or <code class="language-plaintext highlighter-rouge">Firebase</code>.</li> <li>The client will <a href="#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 <a href="#advertising-a-push-notification-server">advertise</a> the new coordinates</li> </ul> <h3 id="sending-a-notification"> <a href="#sending-a-notification" class="anchor-heading" aria-labelledby="sending-a-notification"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink: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 <a href="#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> <h3 id="receiving-a-push-notification"> <a href="#receiving-a-push-notification" class="anchor-heading" aria-labelledby="receiving-a-push-notification"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink: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 <code class="language-plaintext highlighter-rouge">installation_id</code> included. The <code class="language-plaintext highlighter-rouge">chat_id</code> MAY be used to open the chat if present.</li> <li><code class="language-plaintext highlighter-rouge">message</code> can be decrypted and presented to the user. Otherwise messages can be pulled from the mailserver if the <code class="language-plaintext highlighter-rouge">message_id</code> is no already present.</li> </ul> <h2 id="protobuf-description"> <a href="#protobuf-description" class="anchor-heading" aria-labelledby="protobuf-description"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink:href="#svg-link"></use></svg></a> Protobuf description </h2> <h3 id="pushnotificationregistration"> <a href="#pushnotificationregistration" class="anchor-heading" aria-labelledby="pushnotificationregistration"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink:href="#svg-link"></use></svg></a> PushNotificationRegistration </h3> <p><code class="language-plaintext highlighter-rouge">token_type</code>: the type of token. Currently supported is <code class="language-plaintext highlighter-rouge">APN_TOKEN</code> for Apple Push <code class="language-plaintext highlighter-rouge">device_token</code>: the actual push notification token sent by <code class="language-plaintext highlighter-rouge">Firebase</code> or <code class="language-plaintext highlighter-rouge">APN</code> and <code class="language-plaintext highlighter-rouge">FIREBASE_TOKEN</code> for firebase. <code class="language-plaintext highlighter-rouge">installation_id</code>: the <a href="./2-account.md"><code class="language-plaintext highlighter-rouge">installation_id</code></a> of the device <code class="language-plaintext highlighter-rouge">access_token</code>: the access token that will be given to clients to send push notifications <code class="language-plaintext highlighter-rouge">enabled</code>: whether the device wants to be sent push notifications <code class="language-plaintext highlighter-rouge">version</code>: a monotonically increasing number identifying the current <code class="language-plaintext highlighter-rouge">PushNotificationRegistration</code>. Any time anything is changed in the record it MUST be increased by the client, otherwise the request will not be accepted. <code class="language-plaintext highlighter-rouge">allowed_key_list</code>: a list of <code class="language-plaintext highlighter-rouge">access_token</code> encrypted with the AES key generated by Diffie–Hellman between the publisher and the allowed contact. <code class="language-plaintext highlighter-rouge">blocked_chat_list</code>: a list of <code class="language-plaintext highlighter-rouge">SHA2-256</code> hashes of chat ids. Any chat id in this list will not trigger a notification. <code class="language-plaintext highlighter-rouge">unregister</code>: whether the account should be unregistered <code class="language-plaintext highlighter-rouge">grant</code>: the grant for this specific server <code class="language-plaintext highlighter-rouge">allow_from_contacts_only</code>: whether the client only wants push notifications from contacts <code class="language-plaintext highlighter-rouge">apn_topic</code>: the APN topic for the push notification <code class="language-plaintext highlighter-rouge">block_mentions</code>: whether the client does not want to be notified on mentions <code class="language-plaintext highlighter-rouge">allowed_mentions_chat_list</code>: a list of <code class="language-plaintext highlighter-rouge">SHA2-256</code> hashes of chat ids where we want to receive mentions</p> <h4 id="data-disclosed"> <a href="#data-disclosed" class="anchor-heading" aria-labelledby="data-disclosed"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink:href="#svg-link"></use></svg></a> Data disclosed </h4> <ul> <li>Type of device owned by a given user</li> <li>The <code class="language-plaintext highlighter-rouge">FIREBASE</code> or <code class="language-plaintext highlighter-rouge">APN</code> push notification token</li> <li>Hash of the chat_id a user is not interested in for notifications</li> <li>The times a push notification record has been modified by the user</li> <li>The number of contacts a client has, in case <code class="language-plaintext highlighter-rouge">allowed_key_list</code> is set</li> </ul> <h3 id="pushnotificationregistrationresponse"> <a href="#pushnotificationregistrationresponse" class="anchor-heading" aria-labelledby="pushnotificationregistrationresponse"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink:href="#svg-link"></use></svg></a> PushNotificationRegistrationResponse </h3> <p><code class="language-plaintext highlighter-rouge">success</code>: whether the registration was successful <code class="language-plaintext highlighter-rouge">error</code>: the error type, if any <code class="language-plaintext highlighter-rouge">request_id</code>: the <code class="language-plaintext highlighter-rouge">SHAKE-256</code> hash of the <code class="language-plaintext highlighter-rouge">signature</code> of the request <code class="language-plaintext highlighter-rouge">preferences</code>: the server stored preferences in case of an error</p> <h3 id="contactcodeadvertisement"> <a href="#contactcodeadvertisement" class="anchor-heading" aria-labelledby="contactcodeadvertisement"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink:href="#svg-link"></use></svg></a> ContactCodeAdvertisement </h3> <p><code class="language-plaintext highlighter-rouge">push_notification_info</code>: the information for each device advertised</p> <h4 id="data-disclosed-1"> <a href="#data-disclosed-1" class="anchor-heading" aria-labelledby="data-disclosed-1"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink:href="#svg-link"></use></svg></a> Data disclosed </h4> <ul> <li>The chat key of the sender</li> </ul> <h3 id="pushnotificationquery"> <a href="#pushnotificationquery" class="anchor-heading" aria-labelledby="pushnotificationquery"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink:href="#svg-link"></use></svg></a> PushNotificationQuery </h3> <p><code class="language-plaintext highlighter-rouge">public_keys</code>: the <code class="language-plaintext highlighter-rouge">SHAKE-256</code> of the public keys the client is interested in</p> <h4 id="data-disclosed-2"> <a href="#data-disclosed-2" class="anchor-heading" aria-labelledby="data-disclosed-2"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink:href="#svg-link"></use></svg></a> Data disclosed </h4> <ul> <li>The hash of the public keys the client is interested in</li> </ul> <h3 id="pushnotificationqueryinfo"> <a href="#pushnotificationqueryinfo" class="anchor-heading" aria-labelledby="pushnotificationqueryinfo"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink:href="#svg-link"></use></svg></a> PushNotificationQueryInfo </h3> <p><code class="language-plaintext highlighter-rouge">access_token</code>: the access token used to send a push notification <code class="language-plaintext highlighter-rouge">installation_id</code>: the <code class="language-plaintext highlighter-rouge">installation_id</code> of the device associated with the <code class="language-plaintext highlighter-rouge">access_token</code> <code class="language-plaintext highlighter-rouge">public_key</code>: the <code class="language-plaintext highlighter-rouge">SHAKE-256</code> of the public key associated with this <code class="language-plaintext highlighter-rouge">access_token</code> and <code class="language-plaintext highlighter-rouge">installation_id</code> <code class="language-plaintext highlighter-rouge">allowed_key_list</code>: a list of encrypted access tokens to be returned to the client in case there’s any filtering on public keys in place. <code class="language-plaintext highlighter-rouge">grant</code>: the grant used to register with this server. <code class="language-plaintext highlighter-rouge">version</code>: the version of the registration on the server. <code class="language-plaintext highlighter-rouge">server_public_key</code>: the compressed public key of the server.</p> <h3 id="pushnotificationqueryresponse"> <a href="#pushnotificationqueryresponse" class="anchor-heading" aria-labelledby="pushnotificationqueryresponse"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink:href="#svg-link"></use></svg></a> PushNotificationQueryResponse </h3> <p><code class="language-plaintext highlighter-rouge">info</code>: a list of <code class="language-plaintext highlighter-rouge">PushNotificationQueryInfo</code>. <code class="language-plaintext highlighter-rouge">message_id</code>: the message id of the <code class="language-plaintext highlighter-rouge">PushNotificationQueryInfo</code> the server is replying to. <code class="language-plaintext highlighter-rouge">success</code>: whether the query was successful.</p> <h3 id="pushnotification"> <a href="#pushnotification" class="anchor-heading" aria-labelledby="pushnotification"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink:href="#svg-link"></use></svg></a> PushNotification </h3> <p><code class="language-plaintext highlighter-rouge">access_token</code>: the access token used to send a push notification. <code class="language-plaintext highlighter-rouge">chat_id</code>: the <code class="language-plaintext highlighter-rouge">SHAKE-256</code> of the <code class="language-plaintext highlighter-rouge">chat_id</code>. <code class="language-plaintext highlighter-rouge">public_key</code>: the <code class="language-plaintext highlighter-rouge">SHAKE-256</code> of the compressed public key of the receiving client. <code class="language-plaintext highlighter-rouge">installation_id</code>: the installation id of the receiving client. <code class="language-plaintext highlighter-rouge">message</code>: the encrypted message that is being notified on. <code class="language-plaintext highlighter-rouge">type</code>: the type of the push notification, either <code class="language-plaintext highlighter-rouge">MESSAGE</code> or <code class="language-plaintext highlighter-rouge">MENTION</code> <code class="language-plaintext highlighter-rouge">author</code>: the <code class="language-plaintext highlighter-rouge">SHAKE-256</code> of the public key of the sender</p> <h3 id="data-disclosed-3"> <a href="#data-disclosed-3" class="anchor-heading" aria-labelledby="data-disclosed-3"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink:href="#svg-link"></use></svg></a> Data disclosed </h3> <ul> <li>The <code class="language-plaintext highlighter-rouge">SHAKE-256</code> of the <code class="language-plaintext highlighter-rouge">chat_id</code> the notification is to be sent for</li> <li>The cypher text of the message</li> <li>The <code class="language-plaintext highlighter-rouge">SHAKE-256</code> of the public key of the sender</li> <li>The type of notification</li> </ul> <h3 id="pushnotificationrequest"> <a href="#pushnotificationrequest" class="anchor-heading" aria-labelledby="pushnotificationrequest"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink:href="#svg-link"></use></svg></a> PushNotificationRequest </h3> <p><code class="language-plaintext highlighter-rouge">requests</code>: a list of <code class="language-plaintext highlighter-rouge">PushNotification</code> <code class="language-plaintext highlighter-rouge">message_id</code>: the <a href="./6-payloads.md">status message id</a></p> <h3 id="data-disclosed-4"> <a href="#data-disclosed-4" class="anchor-heading" aria-labelledby="data-disclosed-4"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink:href="#svg-link"></use></svg></a> Data disclosed </h3> <ul> <li>The status message id for which the notification is for</li> </ul> <h3 id="pushnotificationresponse"> <a href="#pushnotificationresponse" class="anchor-heading" aria-labelledby="pushnotificationresponse"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink:href="#svg-link"></use></svg></a> PushNotificationResponse </h3> <p><code class="language-plaintext highlighter-rouge">message_id</code>: the <code class="language-plaintext highlighter-rouge">message_id</code> being notified on. <code class="language-plaintext highlighter-rouge">reports</code>: a list of <code class="language-plaintext highlighter-rouge">PushNotificationReport</code></p> <h3 id="pushnotificationreport"> <a href="#pushnotificationreport" class="anchor-heading" aria-labelledby="pushnotificationreport"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink:href="#svg-link"></use></svg></a> PushNotificationReport </h3> <p><code class="language-plaintext highlighter-rouge">success</code>: whether the push notification was successful. <code class="language-plaintext highlighter-rouge">error</code>: the type of the error in case of failure. <code class="language-plaintext highlighter-rouge">public_key</code>: the public key of the user being notified. <code class="language-plaintext highlighter-rouge">installation_id</code>: the installation id of the user being notified.</p> <h2 id="anonymous-mode-of-operations"> <a href="#anonymous-mode-of-operations" class="anchor-heading" aria-labelledby="anonymous-mode-of-operations"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink:href="#svg-link"></use></svg></a> Anonymous mode of operations </h2> <p>An anonymous mode of operations MAY be provided by the client, where the responsibility of propagating information about the user is left to the client, in order to preserve privacy.</p> <p>A client in anonymous mode can register with the server using a key different from their chat key. This will hide their real chat key.</p> <p>This public key is effectively a secret and SHOULD only be disclosed to clients that you the user wants to be notified by.</p> <p>A client MAY advertise the access token on the contact-code topic of the key generated. A client MAY share their public key through <a href="./6-payloads.md#contact-update">contact updates</a></p> <p>A client receiving a push notification public key SHOULD listen to the contact code topic of the push notification public key for updates.</p> <p>The method described above effectively does not share the identity of the sender nor the receiver to the server, but MAY result in missing push notifications as the propagation of the secret is left to the client.</p> <p>This can be mitigated by <a href="./6-payloads.md">device syncing</a>, but not completely addressed.</p> <h2 id="security-considerations"> <a href="#security-considerations" class="anchor-heading" aria-labelledby="security-considerations"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink:href="#svg-link"></use></svg></a> Security considerations </h2> <p>If no anonymous mode is used, when registering with a push notification service a client discloses:</p> <ul> <li>The chat key</li> <li>The devices that will receive notifications</li> </ul> <p>A client MAY disclose:</p> <ul> <li>The hash of the chat_ids they want to filter out</li> </ul> <p>When running in anonymous mode, the client’s chat key is not disclosed.</p> <p>When querying a push notification server a client will disclose:</p> <ul> <li>That it is interested in sending push notification to another client, but the querying client’s chat key is not disclosed</li> </ul> <p>When sending a push notification a client discloses:</p> <ul> <li>The <code class="language-plaintext highlighter-rouge">SHAKE-256</code> of the chat id</li> </ul> <p>review process. Point can be integrated, suggestion welcome.</p> <h2 id="faq"> <a href="#faq" class="anchor-heading" aria-labelledby="faq"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink:href="#svg-link"></use></svg></a> FAQ </h2> <h3 id="why-having-acl-done-at-the-server-side-and-not-the-client"> <a href="#why-having-acl-done-at-the-server-side-and-not-the-client" class="anchor-heading" aria-labelledby="why-having-acl-done-at-the-server-side-and-not-the-client"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink:href="#svg-link"></use></svg></a> Why having ACL done at the server side and not the client? </h3> <p>We looked into silent notification for <a href="https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/pushing_background_updates_to_your_app">IOS</a> (android has no equivalent) but can’t be used as it’s expected to receive maximum 2/3 per hour, so not our use case. There are also issue when the user force quit the app.</p> <h3 id="why-using-an-access-token"> <a href="#why-using-an-access-token" class="anchor-heading" aria-labelledby="why-using-an-access-token"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink:href="#svg-link"></use></svg></a> Why using an access token? </h3> <p>The access token is used to decouple the requesting information from the user from actually sending the push notification.</p> <p>Some ACL is necessary otherwise it would be too easy to spam users (it’s still fairly trivial, but with this method you could allow only contacts to send you push notifications).</p> <p>Therefore your identity must be revealed to the server either when sending or querying.</p> <p>By using an access token we increase deniability, as the server would know who requested the token but not necessarily who sent a push notification. Correlation between the two can be trivial in some cases.</p> <p>This also allows a mode of use as we had before, where the server does not propagate info at all, and it’s left to the user to propagate the token, through contact requests for example.</p> <h3 id="why-advertise-with-the-bundle"> <a href="#why-advertise-with-the-bundle" class="anchor-heading" aria-labelledby="why-advertise-with-the-bundle"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink:href="#svg-link"></use></svg></a> Why advertise with the bundle? </h3> <p>Advertising with the bundle allows us to piggy-back on an already implemented behavior and save some bandwidth in cases where is not filtering by public keys</p> <h3 id="whats-the--bandwidth-impact-for-this"> <a href="#whats-the--bandwidth-impact-for-this" class="anchor-heading" aria-labelledby="whats-the--bandwidth-impact-for-this"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink:href="#svg-link"></use></svg></a> What’s the bandwidth impact for this? </h3> <p>Generally speaking, for each 1-to-1 message and group chat message you will sending 1 and <code class="language-plaintext highlighter-rouge">number of participants</code> push notifications. This can be optimized if multiple users are using the same push notification server. Queries have also a bandwidth impact but they are made only when actually needed</p> <h3 id="whats-the-information-disclosed"> <a href="#whats-the-information-disclosed" class="anchor-heading" aria-labelledby="whats-the-information-disclosed"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink:href="#svg-link"></use></svg></a> What’s the information disclosed? </h3> <p>The data disclosed with each message sent by the client is above, but for a summary:</p> <p>When you register with a push notification service you may disclose:</p> <p>1) Your chat key 2) Which devices you have 3) The hash of the chat_ids you want to filter out 4) The hash of the public keys you are interested/not interested in</p> <p>When you query a notification service you may disclose:</p> <p>1) Your chat key 2) The fact that you are interested in sending push notification to a given user</p> <p>Effectively this is fairly revealing if the user has a whitelist implemented. Therefore sending notification should be optional.</p> <h3 id="what-prevents-a-user-from-generating-a-random-key-and-getting-an-access-token-and-spamming"> <a href="#what-prevents-a-user-from-generating-a-random-key-and-getting-an-access-token-and-spamming" class="anchor-heading" aria-labelledby="what-prevents-a-user-from-generating-a-random-key-and-getting-an-access-token-and-spamming"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink:href="#svg-link"></use></svg></a> What prevents a user from generating a random key and getting an access token and spamming? </h3> <p>Nothing really, that’s the same as the status app as a whole. the only mechanism that prevents this is using a white-list as described above, but that implies disclosing your true identity to the push notification server.</p> <h3 id="why-not-0-knowledge-proofsquantum-computing"> <a href="#why-not-0-knowledge-proofsquantum-computing" class="anchor-heading" aria-labelledby="why-not-0-knowledge-proofsquantum-computing"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink:href="#svg-link"></use></svg></a> Why not 0-knowledge proofs/quantum computing </h3> <p>We start simple, we can iterate</p> <h3 id="how-to-handle-backwardforward-compatibility"> <a href="#how-to-handle-backwardforward-compatibility" class="anchor-heading" aria-labelledby="how-to-handle-backwardforward-compatibility"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink:href="#svg-link"></use></svg></a> How to handle backward/forward compatibility </h3> <p>Most of the request have a target, so protocol negotiation can happen. We cannot negotiated the advertisement as that’s effectively a broadcast, but those info should not change and we can always accrete the message.</p> <h3 id="why-ack_key"> <a href="#why-ack_key" class="anchor-heading" aria-labelledby="why-ack_key"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink:href="#svg-link"></use></svg></a> Why ack_key? </h3> <p>That’s necessary to avoid duplicated push notifications and allow for the retry in case the notification is not successful.</p> <p>Deduplication of the push notification is done on the client side, to reduce a bit of centralization and also in order not to have to modify gorush.</p> <h3 id="can-i-run-my-own-node"> <a href="#can-i-run-my-own-node" class="anchor-heading" aria-labelledby="can-i-run-my-own-node"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink:href="#svg-link"></use></svg></a> Can I run my own node? </h3> <p>Sure, the methods allow that</p> <h3 id="can-i-register-with-multiple-nodes-for-redundancy"> <a href="#can-i-register-with-multiple-nodes-for-redundancy" class="anchor-heading" aria-labelledby="can-i-register-with-multiple-nodes-for-redundancy"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink:href="#svg-link"></use></svg></a> Can I register with multiple nodes for redundancy </h3> <p>Yep</p> <h3 id="what-does-my-node-disclose"> <a href="#what-does-my-node-disclose" class="anchor-heading" aria-labelledby="what-does-my-node-disclose"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink:href="#svg-link"></use></svg></a> What does my node disclose? </h3> <p>Your node will disclose the IP address is running from, as it makes an HTTP post to gorush. A waku adapter could be used, but please not now.</p> <h3 id="does-this-have-high-reliability-requirements"> <a href="#does-this-have-high-reliability-requirements" class="anchor-heading" aria-labelledby="does-this-have-high-reliability-requirements"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink:href="#svg-link"></use></svg></a> Does this have high-reliability requirements? </h3> <p>The gorush server yes, no way around it.</p> <p>The rest, kind of, at least one node having your token needs to be up for you to receive notifications. But you can register with multiple servers (desktop, status, etc) if that’s a concern.</p> <h3 id="can-someone-else-ie-not-status-run-this"> <a href="#can-someone-else-ie-not-status-run-this" class="anchor-heading" aria-labelledby="can-someone-else-ie-not-status-run-this"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink:href="#svg-link"></use></svg></a> Can someone else (i.e not status) run this? </h3> <p>Push notification servers can be run by anyone. Gorush can be run by anyone I take, but we are in charge of the certificate, so they would not be able to notify status-clients.</p> <h2 id="changelog"> <a href="#changelog" class="anchor-heading" aria-labelledby="changelog"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink:href="#svg-link"></use></svg></a> Changelog </h2> <h3 id="version-01"> <a href="#version-01" class="anchor-heading" aria-labelledby="version-01"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink:href="#svg-link"></use></svg></a> Version 0.1 </h3> <p>Released <a href="https://github.com/status-im/specs/commit/"></a></p> <ul> <li>Initial version</li> </ul> <h2 id="copyright"> <a href="#copyright" class="anchor-heading" aria-labelledby="copyright"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink:href="#svg-link"></use></svg></a> Copyright </h2> <p>Copyright and related rights waived via <a href="https://creativecommons.org/publicdomain/zero/1.0/">CC0</a>.</p> </div> </div> <div class="search-overlay"></div> </div> </body> </html>
|