Phase 8: push and notifications
Implements PLAN.md Phase 8. internal/pushrules is the rule language: the spec's default ruleset, all six condition kinds, glob and word-boundary matching, intentional mentions switching off the legacy body rules, and rule ordering as Synapse has it. internal/pushserver keeps the rules, pushers and notifications and talks to push gateways. Push rules are the m.push_rules account data, written only by the push server; reads keep the user's own rules and the enabled/actions of each default and take the rest from the current defaults, so new default rules reach every user without a migration. PUT account_data/m.push_rules is refused with 405. The push server listens on the roomserver's emit stage and evaluates each event on its own worker for every local member and an invite's target. Notifications and per-room unread counts are stored in migration 0010; counts are a seventh sync token stream, so /sync reports unread_notifications and picks up count changes made after the event or by a receipt from another device. m.read and m.read.private receipts clear the counts; leaving a room clears it. Pushers (http kind only) push through a bounded sender pool with retries, honour event_id_only, drop push keys the gateway rejects, and go away with their device. GET /notifications pages the history. New config section push: enabled, include_content, gateway_timeout, notification_retention. Account data writes now wake the user's blocked syncs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
74f7bc8805
commit
b507753172
47 files changed
+5136
-50
No files matched your search
+17
-7
@@ -42,7 +42,7 @@ second place for them to disagree.
|
||||
|
||||
## Stream tokens
|
||||
|
||||
A sync response is assembled from five streams that move independently:
|
||||
A sync response is assembled from seven streams that move independently:
|
||||
|
||||
| Stream | Moves when | Position from |
|
||||
|---|---|---|
|
||||
@@ -51,17 +51,21 @@ A sync response is assembled from five streams that move independently:
|
||||
| to-device | a device is sent a message | `sync_sequences` |
|
||||
| account data | a user changes a setting | `sync_sequences` |
|
||||
| presence | somebody's presence actually changes | `sync_sequences` |
|
||||
| device list | somebody's end-to-end keys change ([e2ee.md](e2ee.md)) | `keyserver_sequences` |
|
||||
| notification | a user's unread counts in a room move ([push.md](push.md)) | `push_sequences` |
|
||||
|
||||
A client that has seen receipt 7 but only event 4 has to be able to say so
|
||||
in the one opaque string the API gives it, so a token names a position in
|
||||
each:
|
||||
|
||||
```
|
||||
s{pdu}_{receipt}_{to_device}_{account_data}_{presence}
|
||||
s{pdu}_{receipt}_{to_device}_{account_data}_{presence}_{device_list}_{notification}
|
||||
```
|
||||
|
||||
Trailing zeroes are left off, so a client that has only ever seen room
|
||||
events carries `s12` rather than `s12_0_0_0_0`. A token with fewer
|
||||
events carries `s12` rather than `s12_0_0_0_0_0_0`. The device list and
|
||||
notification positions came later (Phases 7 and 8) and were added at the
|
||||
end. A token with fewer
|
||||
positions than this build writes is accepted and the missing ones read as
|
||||
zero — which is what lets a token stored before a stream existed go on
|
||||
working: the client is sent that stream in full once and is in step
|
||||
@@ -400,7 +404,7 @@ no timestamp and have to decide what that meant.
|
||||
|
||||
| Thing | Needs | Phase |
|
||||
|---|---|---|
|
||||
| `unread_notifications`, `unread_thread_notifications` | push rules | 8 |
|
||||
| `unread_thread_notifications` | threads | 10 |
|
||||
| Remote presence | presence over federation | — |
|
||||
| Threads, and threaded receipts beyond storing `thread_id` | threads | 10 |
|
||||
| `contains_url`, `event_fields` | nothing; they are parsed and ignored | — |
|
||||
@@ -410,9 +414,15 @@ no timestamp and have to decide what that meant.
|
||||
`device_lists` is sent as two empty lists rather than left out: a client
|
||||
reads a missing one as a malformed response, where two empty lists say
|
||||
"nothing changed". How the two lists are worked out, and the key counts
|
||||
that ride along with them, are in [e2ee.md](e2ee.md). The notification
|
||||
counts are left out entirely instead, because a zero there is a claim —
|
||||
"you have nothing unread" — and an absent field is not.
|
||||
that ride along with them, are in [e2ee.md](e2ee.md).
|
||||
|
||||
Each joined room carries `unread_notifications` from the push server,
|
||||
whose counts are the seventh stream, and the global account data carries
|
||||
`m.push_rules`, rendered over the server defaults for a user who never
|
||||
changed a rule. Both are described in [push.md](push.md). A build with no
|
||||
push server leaves the counts out entirely rather than sending zeroes,
|
||||
because a zero is a claim — "you have nothing unread" — and an absent field
|
||||
is not.
|
||||
|
||||
## Endpoints
|
||||
|
||||
|
||||
Reference in new issue
Block a user