Skip to content
Open
Changes from 40 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
176 changes: 176 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,176 @@
# 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.

Today, web clients supporting push notifications (eg. hydrogen) needs to use a matrix to webpush gateway. This requires
going over the specifications, because they use `endpoint`, and `auth` in the `PusherData` ([hydrogen](https://github.com/element-hq/hydrogen-web/blob/9b68f30aad329c003ead70ff43f289e293efb8e0/src/platform/web/dom/NotificationService.js#L32), [sygnal](https://github.com/matrix-org/sygnal/blob/main/sygnal/webpushpushkin.py#L152)),
while [the current specifications let understand that only `url` and `format` are allowed](https://spec.matrix.org/v1.9/client-server-api/#_matrixclientv3pushers_pusherdata).
The specifications already need to be adapted to follow what the web clients do.

Web Push is a standard for (E2EE) push notifications, defined with 3 RFC:
- [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 the *auth* secret, and sends the P-256 public key and the *auth* secret to the home server during registration
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.

Are the auth secret and P-256 key pair different things?

(Also, why is auth in italics?)

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.

line 19 mentions "application server", and line 21 mentions "home server". Are the two the same thing? It would be good to stick with the same terminology in this section.

- the home server 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): defines the authorization:
- it is supposed to be *Voluntary* (optional) but many big push servers require it
- the home server generates a single P-256 key pair, and any client get 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, with an authorization header signed with the VAPID key
- the home server adds the authorization header to all push notifications

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

Extending the push kind to [`POST /_matrix/client/v3/pushers/set`](https://spec.matrix.org/v1.16/client-server-api/#post_matrixclientv3pushersset) to a `*webpush*` would provide encrypted push notifications without the need for an external gateway to
- Web app and desktop app
- 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 webpush standard](https://unifiedpush.org/news/20250131_push_for_decentralized/))
- Maybe other ? We have seen [apple moving a lot into web push support](https://developer.apple.com/documentation/usernotifications/sending-web-push-notifications-in-web-apps-and-browsers)

## Proposal

The MSC introduces a new push kind: webpush.

`PusherData` is extended as follows:
- `format`: is updated to be required if `kind` is `http` or `webpush`
- `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. 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 - 16 random bytes encoded in URL-safe Base64 without padding.
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.

We should make it explicit that the contents of PusherData are not sent to the push gateway, unlike with kind: http.

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.

Now that PusherData is able to contain other data that the ones specified, it may be used by a client to store something

The auth and the pubkey are exposed, because you need to have them to decrypt the content of the push notif


`PusherData` also get 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
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.

the server must add to the responses that include the data

Meaning GET /_matrix/client/v3/pushers, or something else?

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.

a re-registration where the Pusher pushkey ... is updated

Doesn't changing the pushkey mean that it's a new registration?

`/_matrix/client/v3/pushers/ack`i (cf. bellow). Re-subscribing an existing pusher, with the same `pushkey`, `PusherData.url` and `PusherData.auth` doesn't change its value.
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.

Suggested change
`/_matrix/client/v3/pushers/ack`i (cf. bellow). Re-subscribing an existing pusher, with the same `pushkey`, `PusherData.url` and `PusherData.auth` doesn't change its value.
`/_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 home server 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 in the uncompressed form ([SEC 1](https://www.secg.org/sec1-v2.pdf), section 2.3.3, replicated from X9.62), encoded in URL-safe Base64 without padding. The public key comes from a ECDH
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 the intention here that the pushkey is still a unique identifier for the pusher (as it is currently defined in the spec), and is also the public key? In other words, the client needs to use a unique P-256 key per pusher? If so, I think this should be stated explicity.

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.

The spec isn't very clear for FCM/APNS push key too

keypair using the P-256 (prime256v1, cf. FIPS186) curve.

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 send a push notification to the
url, encrypted with `pushKey` and `PusherData.auth`, authenticated with the VAPID key with a message containing
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.

Again, what is a VAPID key?

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.

RFC8292

`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.

`ack_token` valid for 5 minutes:

```
{
"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

M_EXPIRED_ACTIVATION_TOKEN and M_UNKNOWN_ACTIVATION_TOKEN are new error codes.

Note: 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.

}
```

It is also useful to decide if the client should register a pusher using `http` kind and and old style
Sygnal WebPush semantic. 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 any other federation request, 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 home server 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

-