In a previous post about read receipts & notifications in Matrix I briefly mentioned that push rules generate notifications, but with little detail. After completing a rather large project to improve notifications in Matrix I want to fill in some of those blanks. 
These notes are true as of the v1.6 of the Matrix spec and also cover some Matrix spec changes which may or may not have been merged since.
Matrix includes a push notifications module which defines when Matrix events are considered an unread notification or highlight notification  and how those events are sent to third-party push notification services.
Push rules are a set of ordered rules which clients upload to the homeserver. These are shared by all device and are evaluated per event by the homeserver (and also by clients). Default push rules are defined in the Matrix spec. Push rules power the unread (and highlight) counts for each room, push notifications, and the notifications API.
Each rule defines conditions which must be met for the rule to match and actions to take if the rule matches.
Processing of push rules occur until a rule matches or all rules have been evaluated.
As some background, clients receive notifications in one of two ways, via polling /sync and/or via push notifications.
Web-based clients often receive events via polling:
The sync response (both initial and incremental) include the count of unread notifications and unread highlight notifications per room.
Push notifications include the event information (or just the event ID) and whether the event was a highlight notification. (The event being pushed implies it increased the notification count.)
The deployment of the push gateway must be paired with the application (the push keys must be paired). I.e. if you make your own application (or even your own build of Element iOS / Android) you cannot re-use the deployment at matrix.org and must have your own deployment.
There’s an API to retrieve a list of events which the user has been notified about. This powers the “notification panel” on Element Web and is meant to help users catch-up on missed notifications.
It is fairly underspecified and the Synapse implementation has limitations:
Additionally it works poorly for encrypted rooms.
There’s a set of APIs to fetch or modify push rules, they let you:
An initial sync includes all of a user’s push rules under the user’s account data.
Any changes to push rules are included in incremental syncs. Except for newly added rules to the specification (this is likely a homeserver bug).
A push rule is a JSON object with the following fields:
All conditions must match for a push rule to match. If there are no conditions, then the push rule always matches. Possible conditions include:
Push rule actions define what to do once a push rule matches an event.
There are other undefined or no-op actions (dont_notify, coalesce) which will be removed in the next version of the spec. 
Push rules have a type associated with them, these are executed in order:
The previously discussed shape of push rules is not the full story! There are special cases which do not accept conditions, but can be mapped to them.
Encryption ruins everything! Some of the push rules require the decrypted event content to be properly processed. The enable this, the default rules declare all encrypted events as notifications. Clients are expected to re-run push rules on the decrypted content. 
This can result in one of the following: 
Due to gappy syncs clients frequently can only make a best estimate of the true unread / highlight count of events in encrypted rooms.
Element iOS / Android get encrypted events pushed to them, but do not properly implement mentions & keywords.
The default rules are in the Matrix spec and include:
Default rules can be disabled or have their actions modified on a per-user basis. Some of the above features are handled by multiple push rules.
Element creates custom push rules based on a known form. 
Matrix also allows defining arbitrary rules (e.g. to change behavior for particular rooms, senders, message types, etc.)
The unread (“bold”) rooms logic in Element Web is completely custom and outside of the Matrix specification.
Note that if you enable hidden events (or tweak other options to show events) then the behavior changes!
…it gets complicated trying to figure out whether a message will generate a notification or not.
|||Improving unintentional mentions (MSC3952) is the main feature we were working on, but this was powered by MSC3758 (from Beeper), MSC3873 (from a coworker), and MSC3966. MSC3980 was also a follow-up for consistency.|
Notification count (the grey badge with count in Element Web) is the number of unread messages in a room. Highlight count (the red badge with count in Element Web) is the number of unread mentions in a room.
The unread (“bold”) rooms feature in Element Web, which represents a room with unread messages (but no notification count) is not powered by push rules (and is not specced).
See the Element Web docs on the room list.
|||This post generally defines “push notifications” as a notification which is sent via a push provider to an application. Push providers include Apple, Google, Microsoft, or Mozilla.|
|||MSC4010 aims to make this explicit.|
|||It was not clear how clients should handle encrypted events until recently|
|||Adapted from a Gist from Half-Shot.|
|||These don’t seem to be specced, I’m unsure if other clients create similar rules or understand these rules.|