diff options
| author | ojacobson <ojacobson@noreply.codeberg.org> | 2025-11-07 23:17:15 +0100 |
|---|---|---|
| committer | ojacobson <ojacobson@noreply.codeberg.org> | 2025-11-07 23:17:15 +0100 |
| commit | 9e6f19f0f188eaa7f8b6be21c8405786cfb0dddd (patch) | |
| tree | b2999341645dec61e8143d7bb1b8a9d0056e0db1 /docs/api/push.md | |
| parent | 3c588861ef5814de329743147398dbae22c1aeeb (diff) | |
| parent | 78d901328261d2306cf59c8e83fc217a63aa4a64 (diff) | |
Set up infrastructure for push message subscriptions.
A subscription allows an application server (here, the Pilcrow server) to send web push messages to a user agent.
On the server, Pilcrow records subscriptions verbatim, in the clear. Each subscription has an associated key, which will be used to encrypt messages for the corresponding client, but we store them in the clear, for the same broad reason that we store the VAPID key in the clear. They allow anyone who obtains them to impersonate the server and send push messages to clients, but they're rotated regularly - clients must rotate them whenever the server's VAPID key changes.
On the client, we monitor VAPID key change events to drive automatic subscription management, once the user sets up an initial subscription manually (which we must do as it can involve a user-interaction-only prompt for permission to send notifications). This isn't the final UI, but rather a bare-minimum version to let us move on with testing push notifications.
Merges push-subscribe into push-notify.
Diffstat (limited to 'docs/api/push.md')
| -rw-r--r-- | docs/api/push.md | 124 |
1 files changed, 124 insertions, 0 deletions
diff --git a/docs/api/push.md b/docs/api/push.md new file mode 100644 index 0000000..363bff3 --- /dev/null +++ b/docs/api/push.md @@ -0,0 +1,124 @@ +# Push Notifications + +```mermaid +--- +Push lifecycle +--- +sequenceDiagram + actor Andrea + participant API + participant Broker + + Note over Andrea, Broker : Creating a push subscription + + Andrea ->>+ API : Client Boot + API ->>- Andrea : Boot message with VAPID keys + + Andrea ->>+ API : Subscribe to events + + Andrea ->>+ Broker : New Push Subscription + Broker ->>- Andrea : Subscription URL + + Andrea ->>+ API : Subscribe + API ->>- Andrea : Created + + API ->>- Andrea : Disconnect + + Note over Andrea, Broker : Asynchronous notification + + API ->> Broker : Push Publication + Broker ->> Andrea : Push Delivery + + Andrea ->>+ API : Resume event subscription + API ->>- Andrea : Disconnect + + Note over Andrea, Broker : VAPID key rotation + + Andrea ->>+ API : Subscribe to events + API -) Andrea : VAPID key changed + + Andrea ->>+ Broker : Unsubscribe + Broker ->>- Andrea : Unsubscribed + Andrea ->>+ Broker : New Push Subscription + Broker ->>- Andrea : Subscription URL + + Andrea ->>+ API : Subscribe + API ->>- Andrea : Created + + API ->>- Andrea : Disconnect +``` + +Pilcrow uses [Web Push] to notify clients asynchronously of interesting events, to allow clients to disconnect from [the event stream](events.md) while keeping their users informed of updates to conversations. Clients are _not required_ to implement push subscriptions. Pilcrow's primarily channel for communicating real-time communications data to clients is the [event stream](events.md). + +[Web Push]: https://developer.mozilla.org/en-US/docs/Web/API/Push_API + +## VAPID keys + +Pilcrow uses a generated Voluntary Application Server Identification (VAPID) key to authenticate to push brokers. Clients do not generally need to handle VAPID authentication themselves, but, due to the design of the Web Push protocol, clients are responsible for providing Pilcrow's VAPID key to push brokers when subscribing to notifications. To make this possible, Pilcrow delivers its VAPID key to clients via the event stream. See the [VAPID key events](events.md#vapid-key-events) section for details of these events. + +Pilcrow rotates the VAPID key periodically, and sends all clients an event when this occurs. This immediately invalidates all existing subscriptions, as the previous VAPID key is destroyed. Clients that wish to continue receiving messages must re-subscribe using the new VAPID key when this happens, and will miss any Web Push messages sent during this interval. + +## Receiving Web Push messages + +Pilcrow sends web push messages to subscriptions. A Pilcrow user may have many subscriptions - in principle, generally one per client, but Pilcrow does not enforce any such limit. Clients create subscriptions as needed, and inform the Pilcrow server of those subscriptions to begin the flow of push messages. + +The specific taxonomy and structure of push messages is still under development. This API is even more experimental than the rest of Pilcrow. + +Pilcrow keeps track of the token used to create a web push subscription, and abandons subscriptions when that token is invalidated. This prevents the server from inadvertently sending information to a client after the user for which that information is intended has logged out, or after another user has taken over the client. Clients are responsible for re-establishing subscriptions on login if they wish to resume push messaging. + +## `POST /api/push/subscribe` + +Inform the Pilcrow server of a push subscription. + +### Request + +```json +{ + "subscription": { + "endpoint": "https://example.push.com/P1234", + "keys": { + "p256dh": "base64-encoded key", + "auth": "base64-encoded authentication secret" + } + } + "vapid": "BKILjh0SzTiHOZ5P_sfduv-iqtOg_S18nR7ePcjnDivJaOY6nOG1L3OmvjXjNnthlRDdVnawl1_owfdPCvmDt5U=" +} +``` + +The request must have the following fields: + +| Field | Type | Description | +| :------------- | :----- | :------------------------------------------------------ | +| `subscription` | object | The push subscription object created by the user agent. | +| `vapid` | string | The VAPID key used to create the subscription. | + +The `subscription` field should be the result of calling `toJSON()` on a `PushSubscription` object in the DOM API. For details, see [the WebPush specification](https://w3c.github.io/push-api/#dom-pushsubscription-tojson) or [MDN](https://developer.mozilla.org/en-US/docs/Web/API/PushSubscription/toJSON). + +The `vapid` field should be set to the key used when creating the subscription, which should in turn be obtained from the event stream. + +[wp-example]: https://developer.mozilla.org/en-US/docs/Web/API/PushSubscription#sending_coding_information_to_the_server + +This request can be retried on any non-400 response, including any network-related errors. A duplicate subscription with the same endpoint, the same public key and the same authentication secret will be accepted by the server even if the subscription has already been created. + +### Success + +This endpoint will respond with a status of `201 Created` when successful. The response body is empty. + +### Duplicate endpoint + +If the push subscription's endpoint URL is already associated with a subscription with different keys, then Pilcrow will return a `409 Conflict` response. + +### Stale VAPID key + +If the provided `vapid` key is not the server's current VAPID key, then the client has created a subscription which is not usable. This may happen if Pilcrow rotates its key while a client is negotiating with a Web Push broker to set up a subscription using the old key. Pilcrow will respond a `400 Bad Request` response. + +**This response includes a JSON body**, unlike other errors, and will have a `content-type: application/json` header. + +The response will include the following fields: + +| Field | Type | Description | +| :-------- | :----- | :------------------------------ | +| `message` | string | A human-readable error message. | +| `key` | string | Pilcrow's new VAPID key. | + +Clients should immediately destroy the push subscription they were attempting to create, and may try again using the new VAPID key (either immediately, using the key from the response, or asynchronously, using the key when it is delivered via the event stream). |
