Skip to content
Open
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
24473e5
Add MSC for webpush push kind
p1gp1g Aug 1, 2024
2eae5a3
Apply suggestions
p1gp1g Aug 16, 2024
0346332
Add capability and VAPID
MatMaul Dec 1, 2024
201ddc9
Wording + typo
MatMaul Dec 2, 2024
2f71bf3
Small clarification
MatMaul Dec 2, 2024
1f5b73c
Merge pull request #1 from MatMaul/webpush-pushkind
p1gp1g Dec 2, 2024
e413f9e
Add links for apple and google push standard
p1gp1g Jan 21, 2025
faa3101
Fix md links
p1gp1g Jan 23, 2025
216cf01
Add acknowledgement token
p1gp1g May 6, 2025
6bc8165
Merge pull request #2 from p1gp1g/webpush-pushkind-ack
p1gp1g May 26, 2025
a9f7e48
Apply suggestions from spaetz code review
p1gp1g Nov 15, 2025
c9d0b79
Apply suggestion to highlight what this MSC introduces or extend
p1gp1g Nov 15, 2025
5ebd199
Rename 4174
p1gp1g Nov 15, 2025
706e440
Typo
p1gp1g Nov 15, 2025
92ae561
Do not use RFC title for drafts
p1gp1g Nov 18, 2025
1452aa8
Typos
p1gp1g Nov 18, 2025
8473849
Link current specs to set pusher
p1gp1g Nov 18, 2025
55aeaf5
Use inline links
p1gp1g Nov 18, 2025
9b8ca9d
Add overview with webpush
p1gp1g Nov 18, 2025
75b73c4
Typo, and change app_id
p1gp1g Nov 18, 2025
3874fe0
Update proposals/4174-webpush-pushkind.md
p1gp1g Jan 6, 2026
4b7642b
Add ref to RFC8291 for auth
p1gp1g Jan 6, 2026
1d7dd7a
Fix list formatting
p1gp1g Jan 6, 2026
373dbc0
Apply suggestions from code review
p1gp1g Jan 7, 2026
df81404
Apply suggestions
p1gp1g Jan 7, 2026
aee6877
Reuse URL for web push endpoint
p1gp1g Feb 10, 2026
9b41d6d
Clarify implementation of draft web push
p1gp1g Feb 10, 2026
52ba762
Typo
p1gp1g Feb 10, 2026
9ee94eb
Add note to delete registration on push
p1gp1g Feb 10, 2026
39e3687
Add link to current overview
p1gp1g Feb 10, 2026
3a37cec
Use different response code for registration ack
p1gp1g Feb 10, 2026
8b376de
Define `ack_token` using opaque identifier grammar
p1gp1g Feb 17, 2026
d2485a2
Update proposals/4174-webpush-pushkind.md
p1gp1g Feb 17, 2026
2561254
Fix b64
p1gp1g Feb 18, 2026
7f1849e
Require VAPID
p1gp1g Feb 18, 2026
f3f96be
Add summary of webpush
p1gp1g Feb 18, 2026
5bab3c4
Add Matrix errcode
p1gp1g Feb 19, 2026
83dc378
Move activated field above, and add more information regarding the ac…
p1gp1g Feb 19, 2026
6b70438
Typo
p1gp1g Feb 23, 2026
f59cb97
Use new error code for activation
p1gp1g Feb 24, 2026
bd30e72
Apply suggestion from @anoadragon453
p1gp1g Mar 12, 2026
246db23
Lint
p1gp1g Mar 12, 2026
8a67d56
Apply suggestion from @p1gp1g
p1gp1g Mar 18, 2026
becdd6e
Update proposals/4174-webpush-pushkind.md
p1gp1g Mar 18, 2026
5217c69
Update proposals/4174-webpush-pushkind.md
p1gp1g Mar 18, 2026
a3850f7
Apply suggestions from code review
p1gp1g Mar 18, 2026
6d59575
Apply suggestions from code review
p1gp1g Mar 18, 2026
7a00947
Use Web Push
p1gp1g Mar 18, 2026
a31ae62
Use homeserver
p1gp1g Mar 18, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
174 changes: 174 additions & 0 deletions proposals/4174-webpush-pushkind.md
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implementation requirements:

  • Client
  • Server

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@turt2live These requirements are now met

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Client (Hydrogen) and Server (Synapse) implementations noted at #4174 (comment)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This MSC has changed since the implementations were made. I'm not sure what all the changes were. If they are primarily minor changes such as naming changes (e.g. renaming properties), or changes of that sort, then the implementations should still be fine as a demonstration that the MSC works. If there are any substantial changes, then the implementations may need to be updated.

Copy link
Copy Markdown
Member

@anoadragon453 anoadragon453 Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The homeserver implementation for Synapse is actively being updated in element-hq/synapse#17987. It has yet to be merged.

The client implementation for Hydrogen has not been updated since Dec 2nd, 2024: element-hq/hydrogen-web#1201. The changes to the MSC since Dec 2nd, 2024 are https://github.com/matrix-org/matrix-spec-proposals/pull/4174/files/0346332a1f802425b579d2b4cdf98fe8a462d48b..f59cb97551216812d1ca492fafba05d057cc1298.

I've left a review on the Hydrogen PR with what appears to be missing from a cursory glance. As such, I think we're still missing an up-to-date client implementation.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mscbot concern The client implementation has grown stale with respect to the MSC. See this PR review for the outstanding changes.

Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
# MSC4174: Web push
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[non-blocking] This MSC refers to the feature inconsistently as "WebPush" or "Web Push" (with or without the space), but it's hard to fault this MSC for that, since the RFCs do the same thing. However, when the spec gets written, we should pick one and stick with it.


This MSC supersedes and replaces [MSC3013](https://github.com/matrix-org/matrix-spec-proposals/pull/3013), which introduced push notification encryption first.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tulir claims this MSC does not adequately replace MSC3013.

@mscbot concern decide if we are replacing MSC3013 or not

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that we only want to use the encryption defined in here for WebPush (and only because it isn't easy to replace the encryption in WebPush with something else). If we want to encrypt push notifictions over other systems, we should use a different mechanism, which will probably be something different from what 3013 defines. So 3013 will be probably replaced, but not by this MSC.

Basically, this first sentence should be removed.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


Push notifications typically go through third-party push providers in order to be delivered: 1) a push gateway (sygnal) and
2) e.g. FCM (Google) or APNs (Apple). In order to prevent these push providers and
push gateways from being able to read any sensitive information, the `event_id_only` format was introduced, where the push content only contains the `event_id` and `room_id` of an event. After receiving the push message the client uses
`GET /_matrix/client/r0/rooms/{roomId}/event/{eventId}` to fetch the full event itself, and creates the notification based
on that.

Leaking the room and event id to third parties is problematic and can be avoided.


Web Push is a standard for (E2EE) push notifications, defined by 3 RFCs:
- [RFC8030](https://www.rfc-editor.org/rfc/rfc8030) defines the application server to push server communications
- [RFC8291](https://www.rfc-editor.org/rfc/rfc8291) defines the encryption:
- the subscribing client (user agent in RFC8030) generates a P-256 key pair and the *auth* secret, and sends the P-256 public key and the *auth* secret to the homeserver during registration
- the homeserver encrypts outgoing push notifications with the client keys
- the notifications are then decrypted by the subscribing client
- [RFC8292](https://www.rfc-editor.org/rfc/rfc8292), VAPID: defines the authorization:
- it is supposed to be *Voluntary* (optional) but many big push servers require it
- the homeserver generates a single P-256 key pair, and any client can request the public key
- the client passes the VAPID public key while requesting a new registration to their push server
- the push server is then able to allow only requests which have an authorization header signed with the VAPID key
- the homeserver adds the authorization header to all push notifications

Many libraries are already available and robust: they are reviewed, and acknowledged by experts.

Adding a `kind` of `webpush` to [`POST /_matrix/client/v3/pushers/set`](https://spec.matrix.org/v1.16/client-server-api/#post_matrixclientv3pushersset) would allow the following types of client to to receive encrypted push notifications without the need for an external gateway:
- Web apps and desktop apps
- Android apps using [UnifiedPush](https://codeberg.org/UnifiedPush/specifications/src/branch/main/specifications/android.md#resources). This MSC would make [MSC2970](https://github.com/matrix-org/matrix-spec-proposals/pull/2970) redundant.
- Android apps using FCM ([It is possible to push to FCM with Web Push standard](https://unifiedpush.org/news/20250131_push_for_decentralized/))
- Maybe others? For example, Apple are starting to move starting to move towards web push [support](https://developer.apple.com/documentation/usernotifications/sending-web-push-notifications-in-web-apps-and-browsers).
- iOS apps would still require some sort "gateway" (to transform Web Push push notifications to a format that the Apple Push Notification Service (APNS) will accept). But such a gateway is trivial compared to what matrix push gateways implement today. Mastodon's [webpush-apn-relay](https://github.com/mastodon/webpush-apn-relay) is one such example. Notification content remains encrypted between the homeserver all the way to the client.

## Proposal

The MSC introduces a new pusher `kind`, for use with [`/pushers/set`](https://spec.matrix.org/v1.17/client-server-api/#post_matrixclientv3pushersset): `webpush`.

[`PusherData`](https://spec.matrix.org/v1.17/client-server-api/#post_matrixclientv3pushersset_request_pusherdata) is extended as follows:
- `url`: is updated to be required if `kind` is `http`, or if `kind` is `webpush`. If `kind` is `webpush`, this is the URL defined as a `push resource` by RFC8030, and MUST be an HTTPS URL.
- `auth`: is introduced, required if `kind` is `webpush`, not used otherwise. This holds the authentication secret as
specified by [RFC8291 section 3.2](https://www.rfc-editor.org/rfc/rfc8291#section-3.2) - 16 random bytes encoded in URL-safe Base64 without padding.

`PusherData` also gets new field, `activated`, a boolean, that must not be set by the client, and the server must add to the responses that include the data. During a first registration, or during a re-registration where the Pusher `pushkey`, `PusherData.url` or `PusherData.auth` is updated, `activated` is set to false until the pusher is activated with the request to
`/_matrix/client/v3/pushers/ack` (cf. below). Re-subscribing an existing pusher, with the same `pushkey`, `PusherData.url` and `PusherData.auth` doesn't change its value.

The homeserver doesn't send any notification - except the one to validate the pusher - to the pusher, until the Pusher is activated.

The POST request to the endpoint dedicated to the creation, modification and deletion of pushers,
`POST /_matrix/client/v3/pushers/set` now supports a new `kind`: `webpush`.
- `kind`: is updated to introduce `webpush` which makes a
pusher that sends Web Push encrypted messages.
- `pushkey`: is updated, if the `kind` is `webpush`, this is the user agent public key.
Per [RFC8291 section 3.1](https://www.rfc-editor.org/rfc/rfc8291#section-3.1), this is the public part of a
P-256 keypair, converted to bytes using the uncompressed form ([SEC 1](https://www.secg.org/sec1-v2.pdf),
section 2.3.3, replicated from X9.62), and encoded in URL-safe Base64 without padding.

If the request creates a new pusher or modifies values under `pushkey` , `PusherData.url`, or `PusherData.auth`, then
the server MUST respond with 201, "The pusher is set but needs to be activated". The server MUST then send a push notification to the
url, encrypted with `pushKey` and `PusherData.auth`, authenticated with the VAPID key, with a message containing
`app_id` and `ack_token`. `ack_token` MUST be a unique identifier conforming to [the opaque identifier grammar](https://spec.matrix.org/v1.17/appendices/#opaque-identifiers).
To ensure sufficient entropy is used, it is recommended to use a UUIDv4 token in hyphen form.

The server must expire the `ack_token` after 5 minutes. In this case, the registration remains inactivated until the client tries to register again, then the homeserver sends a new `ack_token`.
Example of a push notification containing a validation token:

```
{
"app_id": "im.vector.app.android",
"ack_token": "6fc76b70-5fad-4eb7-93ea-a1af7a03258b"
}
```

A new endpoint is introduced, dedicated to pusher validation. This is called by the matrix client to validate the pusher once it has received the `ack_token` from the validation push message:
- POST `/_matrix/client/v3/pushers/ack`
- Rate limited: No, Requires authentication: Yes
- The request body contains the `app_id` and `ack_token` parameters, received with the push notification.
- The response contains the following HTTP code and error code:
- 404, M_NOT_FOUND: if no pusher with this app_id exists
- 410, M_EXPIRED_ACTIVATION_TOKEN: if this token for this app_id is expired
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not convinced 410 is appropriate here. I'd just use 400 if there is no particular reason for 410.

- 400, M_UNKNOWN_ACTIVATION_TOKEN: if a pusher with this app_id exists, but the token is not known. An expired token may send this status too
- 200: if the pusher has been activated. The response body does not contain any parameters.

M_EXPIRED_ACTIVATION_TOKEN and M_UNKNOWN_ACTIVATION_TOKEN are new error codes.

Note: As specified by RFC8030, the homeserver deletes the registration if it receives a 404, 410 or 403 from the push server on push.

As many popular push servers require VAPID (Voluntary Application Server Identification, cf RFC8292), a server supporting this MSC MUST add a `m.webpush` capability to the `/capabilities` endpoint with this format:

The VAPID public key is in the uncompressed form, in URL-safe Base64 without padding.

```
"m.webpush": {
"enabled": true,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is enabled necessary here, or is the presence of an m.webpush entry sufficient for clients to determine that their server supports webpush?

"vapid": "BNbXV88MfMI0fSxB7cDngopoviZRTbxIS0qSS-O7BZCtG04khMOn-PP2ueb_X7Aeci42n02kJ0-JJJ0uQ4ELRTs"
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I slightly feel that it might be more appropriate to expose this on a separate endpoint, but it's not a strong opinion.

}
```

A client that supports this kind of pusher should use it if the server supports it too, and
not register another `http` pusher to avoid duplicate pushes.

## Overview with webpush

The current overview is here: <https://spec.matrix.org/v1.17/push-gateway-api/#overview>

It becomes:

```
+-------------------+
Matrix HTTP | |
Notification Protocol | Device Vendor |
| |
+-------------------+ | +---------------+ |
| | | | | |
| Matrix homeserver +--> Web Push +----> Push Server | |
| | | | | |
+-^-----------------+ | +----+----------+ |
| | | |
Matrix | | | |
Client/Server API + | | |
| | +-------------------+
| +--+-+ |
| | <--------------------------------+
+---+ |
| | Provider Push Protocol
+----+

Mobile Device or Client
```

## Potential issues

Many libraries only implement [I-D.ietf-webpush-encryption-04](https://datatracker.ietf.org/doc/html/draft-ietf-webpush-encryption-04) from October 2016, rather than the final version of [RFC8291](https://datatracker.ietf.org/doc/html/rfc8291) from November 2017. Thus, some care needs to be taken during implementation. Checking the
Content-Encoding header is a good way to check for the correct version. If the value is `aes128gcm`, then it uses
the right specifications, in case of `aesgcm` it uses the draft version.

The legacy version of web push (draft RFC8291 and draft RFC8292) MUST NOT be implemented.

## Alternatives

`pushkey` could be a random ID, and we can add `p256dh` in the `PusherData`. But it would require client to store it,
while the public key already identifies that pusher. And the client already uses the PusherData that way.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And the client already uses the PusherData that way.

I don't understand what this means.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hydrogen already uses pushkey for the p-256 pubkey


`vapid` parameter could be made optional considering it is officially not a requirement, however it seems
existing push servers from big players need it anyway to be able to subscribe, so it was decided to make it mandatory
to avoid issues with those.

## Security considerations

Security considerations are listed by [RFC8030](https://www.rfc-editor.org/rfc/rfc8030#section-8), they are mainly resolved with [RFC8291](https://datatracker.ietf.org/doc/html/rfc8291) (Encryption) and
[RFC8292](https://datatracker.ietf.org/doc/html/rfc8292) (VAPID).

Like federation requests, there is a risk of SSRF. This risk is limited since the post data isn't
arbitrary (the content is encrypted), and a potential malicious actor doesn't have access to the response.
Nevertheless, it is recommended to not post to arbitrary private addresses but offer the option to
safelist a private IP. (Synapse already implements [`ip_range_whitelist`](https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#ip_range_whitelist))
It is also recommended to not follow redirection, to avoid implementation issue where the destination is checked
before sending the request but not for redirection.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does the sample implementation follow these recommendations? Do they work ok in practice (especially not following redirects)?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MatMaul may answer that question

Like any other federation request, there is a risk of DOS amplification. One malicious actor can register many users
to a valid endpoint, then change the DNS record and target another server, then notify all these users. This
amplification is very limited since HTTPS is required and the TLS certificate of the target will be rejected. The
request won't reach any functionality of the targeted application. The homeserver can reject pusher if the response
code is not one intended.

## Unstable prefix

- Until this proposal is considered stable, implementations must use
`org.matrix.msc4174.webpush` instead of `m.webpush`.
Comment on lines +169 to +170
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for the capabilities? There doesn't seem much point specifying an unstable prefix for that without also doing so for Pusher.kind and the new API endpoint.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MatMaul any opinion ?


## Dependencies

-
Loading