1657 views
# Push 2.0 --- This is a draft proposal, it only has experimental implementations currently (Prosody [mod_push2](https://modules.prosody.im/mod_push2)). It was discussed at [XMPP Summit 25](https://pad.nixnet.services/oy6MKVbESSycLeMJIOh6zw). Some feedback (not yet incorporated into the document) can be found at the end. --- Requirements: - Support for SASL2 inlining - Extensible stanza matching rules and notification payload rules - Simpler syntax and concept model than original specification ## Client registers to receive push notifications ```xml <enable xmlns='urn:xmpp:push2:0'> <service>5891b5b522@push.example.net</service> <client>bmVvdmdlbmVsIHB5dnJhZyBxbmduIHRicmYgdXJlcg==</client> <match profile="urn:xmpp:push2:match:archived-with-body" /> <send profile="urn:xmpp:push2:send:notify-only"/> <limit>1</limit> </enable> ``` The `<service/>` element contains a JID which push notifications for this client will be sent to. It may be a host, bare or full JID. The `<client/>` element contains an opaque string that will be included in all communication with the push service. It may be used to convey client identifiers used by the push notification service to route notifications. The `<match/>` and `<send/>` elements define what profiles to use for matching stanzas and sending notifications. These are described later in this document. Clients can specify a limit (`<limit/>`) on the number of notifications to send during any period of unavailability. If the client connects to the server, the notification count is reset to zero. ## Match and send profiles Different clients and push services have different requirements for push notifications, often due to the differing capabilities of target platforms. A "profile" in the context of this specification is a set of rules for matching the kinds of stanzas that should be pushed, and how to transform them before sending the notification to the push service. ### Match profiles Match profiles define which incoming stanzas will trigger a push notification. Some match profiles are defined in this XEP. Other XEPs may define additional profiles with the reserved `urn:xmpp:push2:match:` prefix, following the registrar considerations explained later in this document. Custom profiles not defined in a XEP should use their own appropriate URI. #### `urn:xmpp:push2:match:all` Using this profile, all stanzas will trigger a push notification to be sent to the push service when the client is unavailable. #### `urn:xmpp:push2:match:important` Stanzas that are considered to be "important" are pushed. At the time of writing, there is no standard definition of "important", however most servers already contain such logic for traffic optimization when combined with [XEP-0352: Client State Indication](https://xmpp.org/extensions/xep-0352.html). #### `urn:xmpp:push2:match:archived` Push notifications will be sent for any stanza that is stored in the user's archive. This is a good indication that the stanza is important, and is desired on all of a user's devices. #### `urn:xmpp:push2:match:archived-with-body` Matches only archived messages that contain a body. This can be used to exclude certain message types, such as typing notifications, receipts and error replies. **TODO: encrypted stanzas?** ### Send profiles When a server has determined that a stanza should trigger a push notification (according to the client's selected 'match' profile), it proceeds to create a notification stanza following the rules of the client's selected 'send' profile. After constructing the notification stanza, it will then be sent to the push service JID selected by the client. Some send profiles are defined in this XEP. Other XEPs may define additional profiles with the `urn:xmpp:push2:send:` prefix, following the registrar considerations explained later in this document. Custom profiles not defined in a XEP should use their own appropriate URI. #### `urn:xmpp:push2:send:notify-only` Send an empty notification to the push service. Such notifications are useful if a push notification can trigger the client to "wake up" and connect to the server to receive the message over XMPP. Example: ```xml <message to="5891b5b522@push.example.net"> <notification xmlns="urn:xmpp:push2:0"> <client>bmVvdmdlbmVsIHB5dnJhZyBxbmduIHRicmYgdXJlcg==</client> </notification> </message> ``` #### `urn:xmpp:push2:send:forward` Forward the entire pushed stanza to the push service. Example: ```xml <message to="5891b5b522@push.example.net"> <notification xmlns="urn:xmpp:push2:0"> <client>bmVvdmdlbmVsIHB5dnJhZyBxbmduIHRicmYgdXJlcg==</client> <forwarded xmlns='urn:xmpp:forward:0'> <delay xmlns='urn:xmpp:delay' stamp='2010-07-10T23:08:25Z'/> <message from='juliet@capulet.lit/orchard' id='0202197' to='romeo@montague.lit' type='chat' xmlns='jabber:client'> <body>Yet I should kill thee with much cherishing.</body> </message> </forwarded> </notification> </message> ``` **Note:** Security and privacy implications must be considered when using this profile. As it exposes metadata and message contents to the push service, it should only be used where necessary and when the user fully trusts the push service with access to their communications. Servers MAY require additional verification before allowing this profile to be configured by a client. #### `urn:xmpp:push2-send:encrypted-json:0` This is likely to live in a separate specification for easier evolution. https://xeps.tigase.net/docs/push-notifications/encrypt/ ```xml <send profile="urn:xmpp:push2-send:encrypted-json:0"> <key alg='aes-128-gcm'> BASE64_ENCODED_AES128_KEY.... </key> <message-len>235</message-len> </send> ``` The `<key/>` element contains the base64-encoded key that should be used to encrypt the JSON payload before sending it to the push service. The `alg` attribute of this element indicates the encryption algorithm and key type to use. Supported algorithms are: `aes-128-gcm` : AES-128 GCM mode: 128-bit key, 96-bit IV, 128-bit GCM tag appended to the ciphertext. A new cryptographically-random IV should be generated by the server for every payload. Other algorithms MUST be rejected by the server with an error. ##### JSON payload The server should construct and serialize to JSON an object with the following properties: - "id" (string) - the archive id (defined in XEP-0313) of the stanza being notified about, if the stanza is in the user's archive - "sender" (string) - containing a JID of a stanza sender causing this notification (should be bare JID, with exception for JingleMessageInitiation as in this case full JID is required) - "type" (string): type of the push notification, must be one of: - "chat" - for 1-1 messages - "groupchat" - for MUC rooms and groupchat messages - "subscribe" - for presence subscribe requests - "call" - for jingle calls - "message" (string) - body of the received message, if available. Implementations MUST truncate long messages to the number of characters provided in the `<message-len/>` element in the profile configuration (if provided), ensuring the result remains a valid UTF-8 string. - "nickname" (string) - in case of a message of type "groupchat", this field should be set to the nickname of the message sender (optional) - "sid" - in case of a Jingle session initiation, session id of the Jingle call initiated by message (optional) - "media" - in case of Jingle session initiation (JMI <propose/>), list of proposed values of media attribute from each proposed <description/> element (optional but required for Jingle session initiation/JMI propose) Example payload for a chat message: ```json { "id": "376b332c-75d5-41af-b515-d9317c5e89ab", "sender": "juliet@capulet.example", "type": "chat", "message": "Wherefore art thou, Romeo?" } ``` Example payload for a call: ```json { "id": "8a663995-8feb-4341-be63-64f00f58d90f", "sender": "juliet@capulet.example/balcony", "type": "call", "sid": "84f6b56d-03e9-4724-86cd-a2ae4b82001b", "media": ["audio"] } ``` ##### Encrypting the payload Encrypt the payload according to the selected algorithm. The result should be base64-encoded, and wrapped in an `<encrypted/>` element. If the selected algorithm requires an IV, this should be base64-encoded and added in the 'iv' attribute of the element. ```xml <encrypted iv='dG90ZXMgcmFuZG9t'> 1AJFY6MT+jjCE2WC+HiovOFqChqjAmnftrrbT/opkw6S/yyjfymJyi6pnpHJJok5PMOnWfeLpZL5cF55kFi9Q7TjUDYZZ5g9aroAW2A+JrHP0VEqi8PXuAUtARGR00NVe7fyYi9aVdUYEp6N+Czgmk/MJw== </encrypted> ``` If the notification is being generated for a received Jingle proposal, add a `type="voip"` attribute to the `<encrypted/>` element, and add `priority="high"` attribute on the outer `<notification/>` element. A final example of a message would look like this: ```xml <message to="5891b5b522@push.example.net"> <notification xmlns="urn:xmpp:push2:0"> <client>bmVvdmdlbmVsIHB5dnJhZyBxbmduIHRicmYgdXJlcg==</client> <encrypted iv="dG90ZXMgcmFuZG9t"> 1AJFY6MT+jjCE2WC+HiovOFqChqjAmnftrrbT/opkw6S/yyjfymJyi6pnpHJJok5PMOnWfeLpZL5cF55kFi9Q7TjUDYZZ5g9aroAW2A+JrHP0VEqi8PXuAUtARGR00NVe7fyYi9aVdUYEp6N+Czgmk/MJw== </encrypted> </notification> </message> ``` And an encrypted call notification would look like this: ```xml <message to="5891b5b522@push.example.net"> <notification xmlns="urn:xmpp:push2:0" priority="high"> <client>bmVvdmdlbmVsIHB5dnJhZyBxbmduIHRicmYgdXJlcg==</client> <encrypted iv="dG90ZXMgcmFuZG9t" type="voip"> 1AJcf6AFuWWFUj7MwzLh7OlpAQGnADL24PvaQ/wznXPssjKvdDSRxDToltXOctt/K8K4Hrrf4cDpflEizlqtTPnzQjZDYcV9eLATUzd+baHV41cqn5SB7F0hFwye2lZLPK+th9rynzDP80Sq6xjHbGp7PQXulxW1eup9uNloGEasNEabhzcDGMTF+KUL3l3FUJ/C </encrypted> </notification> </message> ``` ## Discovering support A server that supports this protocol MUST advertise the `urn:xmpp:push2:0` feature in an account's service discovery information, along with the supported match and send profiles. ```xml <iq from='juliet@capulet.lit' to='juliet@capulet.lit/balcony' id='disco1' type='result'> <query xmlns='http://jabber.org/protocol/disco#info'> <identity category='account' type='registered'/> <feature var='urn:xmpp:push2:0'/> <feature var='urn:xmpp:push2:send:'/> </query> </iq> ``` ## Push service interactions ### Push service announces disabled notification route If the push service knows that it will no longer be able to relay notifications for a particular client, it can notify the client's server. This may happen in response to the user's server sending a notification, or at any other time (e.g. if the push service learns out-of-band that the client app has been uninstalled). Such a notification should consist of a `<message/>` stanza of type 'error', containing a standard XMPP stanza error with a condition of `<gone/>`. As a sibling to the condition element, the push service should include the application-specific error element `<disabled/>` qualified by the 'urn:xmpp:push2:0' namespace. ```xml <message to="user@server" from="5891b5b522@push.example.net" type="error" id="ddaab6e8"> <error by='push.example.net' type='cancel'> <gone xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <disabled xmlns="urn:xmpp:push2:0"> <client>bmVvdmdlbmVsIHB5dnJhZyBxbmduIHRicmYgdXJlcg==</client> </disabled> </error> </message> ``` On receipt of such an error at any time, the user's server MUST verify that the stanza 'from' JID matches the client's configured push service, and that the text content of the `<client/>` element matches the configured client identifier for that service. If they match, the user's server MUST automatically remove the push configuration and no longer send push notifications on behalf of this client. The client SHOULD NOT be prevented from reconfiguring the route in the future if it chooses to. ### Transient delivery errors The user's server might receive delivery errors while sending notifications to the user's push service. The error 'type' attribute SHOULD be honoured - errors of type 'wait' SHOULD be retried in an appropriate manner (e.g. using exponential back-off algorithm, up to a limit), discarding the notification after an appropriate length of time or number of attempts. Other error types MUST NOT be automatically retried. A user's server MAY automatically disable a push configuration for a service that has consistently failed to relay notifications for an extended period of time. This period is a matter of deployment configuration, but a default no less than 72 hours is recommended. --- # Feedback - Feedback from Andrzej (paraphrased): - Is `<limit/>` necessary? What's the need? - Move `<send/>` inside `<match/>`, allow multiple `<match/>` - Replace encryption algorithm selector with namespace (e.g. `urn:xmpp:push2-send:encrypted-json:0:aes-128-gcm`) - Better definitions of JSON payload types (+define more types?) - Store error bounces from push service in MAM, to alert client? - Pick some match/send rules as baseline functionality. Can this apply to 'forward'? (it allows full flexibility and fallback via the push service) - Clarify rules about when push notifications are generated. E.g. only when client is offline? What about hibernating XEP-0198 sessions? What about interaction with other online devices? Will a client still get pushes if the message was successfully delivered to another device? (clarify 'yes') - Feedback from Friedrich (paraphrased): - Allow client discovery of last successful push and push error count - Allow discovery of registrations to a specific server - Use-case: want to allow migrating to a new push server, and turning off old registrations. May not be necessary in a per-device world, but that has a dependency on SASL2. - Other: - Switch to iq for pushes? That way we can be sure it was received (or is message+198 good enough?). I suspect iq has better semantics: we don't want to necessarily track successful delivery, but also successful push to the third-party API/device. - +1 from Holger, MattJ - Should pushes come from the user or server JID? For privacy, we might want to push from the server JID (so push servers don't need to see user JIDs, ever) - Possibly detach the push 2.0 client<->server protocol from the server<->server protocol, so both can be rolled out independently - Delay pushes, and don't send if there is activity from another device within the grace period