-
Notifications
You must be signed in to change notification settings - Fork 401
MSC1763: Proposal for specifying configurable message retention periods #1763
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: old_master
Are you sure you want to change the base?
Changes from 29 commits
687b650
f770440
b25367e
2aafa02
64695ed
c493dbd
0afc3af
7597e03
7a8d204
4646fcd
c55158d
6e33c2f
28ea4e1
cca99dd
a4974b6
c27394c
f0553c0
bdce6f1
a30a853
c281420
ef215dd
0b6a209
5c29779
032e63b
1a4101e
90b17d6
32f21ac
a1b8726
ee0a7ee
cabef48
f5c3729
f8ceb97
8b1a0c3
9357ec6
ac2f87e
116c5b9
f809087
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,318 @@ | ||||||
# Proposal for specifying configurable per-room message retention periods. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I’m sensing an innate conflict within this MSCs interests, one where it both wants to reduce server history in rooms, but where it also simultaneously expects to be able to fetch that history from thin air at any convenient time. I have a feeling it’s written with the underlying idea that large servers will carry all the events in the federation, with some servers being able to fetch from those at any time. …however, this is mentioned nowhere in the MSC, where it skirts around these problems by putting these assumptions between the lines, while not thinking critically about what this means for the larger federation; more dependency on large servers. With this, it does not bring a lucid solution to the problem of dealing with history retention, one where any server eventually has to face that it cannot fetch events it knows exist(ed), but are now expected to respond with them to a client’s query. The semantic equivalent of HTTP Error 410 (“gone”) has to exist somewhere here, to be able to tell clients it’s unable to fetch a historical event due to history retention, and all sad and happy paths that spring from that. The current stance against this is “you’re SOL, have a 404 with no context”. I don’t see this MSC deal with the reality that it is deleting events, I don’t see a coherent solution to allow some servers to “archive” history, and make that explicit (also in the rooms, for privacy concerns, for people who wanna know which servers are ignoring retention rules and archiving anyways) Servers ignoring retention rules does have a basis, namely one of actually archiving historic conversations, in a similar philosophy as The Internet Archive. If this MSC were to go through as-is, then we’d have a similar situation as the general internet, namely one where all history is lost to time due to individual retention strategies. While reliance on large servers isn’t what a federation would want, an explicit form of mentioning where at least people are aware which servers are backing up, and which ones aren’t, would help this MSC greatly in the long run. |
||||||
|
||||||
A major shortcoming of Matrix has been the inability to specify how long | ||||||
events should stored by the servers and clients which participate in a given | ||||||
room. | ||||||
|
||||||
This proposal aims to specify a simple yet flexible set of rules which allow | ||||||
users, room admins and server admins to determine how long data should be | ||||||
stored for a room, from the perspective of respecting the privacy requirements | ||||||
of that room (which may range from a "burn after reading" ephemeral conversation, | ||||||
through to FOIA-style public record keeping requirements). | ||||||
|
||||||
As well as enforcing privacy requirements, these rules provide a way for server | ||||||
administrators to better manage disk space (e.g. to enforce rules such as "don't | ||||||
store remote events for public rooms for more than a month"). | ||||||
|
||||||
This proposal originally tried to also define semantics for per-message | ||||||
retention as well as per-room; this has been split out into | ||||||
[MSC2228](https://github.com/matrix-org/matrix-doc/pull/2228) in order to get | ||||||
the easier per-room semantics landed. | ||||||
|
||||||
## Problem: | ||||||
|
||||||
Matrix is inherently a protocol for storing and synchronising conversation | ||||||
history, and various parties may wish to control how long that history is stored | ||||||
for. | ||||||
|
||||||
* Users may wish to specify a maximum age for their messages for privacy | ||||||
purposes, for instance: | ||||||
* to avoid their messages (or message metadata) being profiled by | ||||||
unscrupulous or compromised homeservers | ||||||
* to avoid their messages in public rooms staying indefinitely on the public | ||||||
record | ||||||
* because of legal/corporate requirements to store message history for a | ||||||
limited period of time | ||||||
* because of legal/corporate requirements to store messages forever | ||||||
(e.g. FOIA) | ||||||
* to provide "ephemeral messaging" semantics where messages are best-effort | ||||||
deleted after being read. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I question the feasibility of this - on what I essentially see as a matrix-specced version of Synapse's History Purge functionality. What would qualify exactly as "after read"? Shouldn't this be removed and left alone for MSC2228 to specify or address? |
||||||
* Room admins may wish to specify a retention policy for all messages in a | ||||||
room. | ||||||
* A room admin may wish to enforce a lower or upper bound on message | ||||||
retention on behalf of its users, overriding their preferences. | ||||||
ara4n marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
* A bridged room should be able to enforce the data retention policies of the | ||||||
remote rooms. | ||||||
* Server admins may wish to specify a retention policy for their copy of given | ||||||
rooms, in order to manage disk space. | ||||||
|
||||||
Additionally, we would like to provide this behaviour whilst also ensuring that | ||||||
users generally see a consistent view of message history, without lots of gaps | ||||||
and one-sided conversations where messages have been automatically removed. | ||||||
|
||||||
At the least, it should be possible for people participating in a conversation | ||||||
to know the expected lifetime of the other messages in the conversation **at | ||||||
the time they are sent** in order to know how best to interact with them (i.e. | ||||||
whether they are knowingly participating in a ephemeral conversation or not). | ||||||
|
||||||
We would also like to set the expectation that rooms typically have a long | ||||||
message retention - allowing those who wish to use Matrix to act as an archive | ||||||
of their conversations to do so. If everyone starts defaulting their rooms to | ||||||
finite retention periods, then the value of Matrix as a knowledge repository is | ||||||
broken. | ||||||
|
||||||
This proposal does not try to solve the problems of: | ||||||
* GDPR erasure (as this involves retrospectively changing the lifetime of | ||||||
messages) | ||||||
ara4n marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
* Bulk redaction (e.g. to remove all messages from an abusive user in a room, | ||||||
as again this is retrospectively changing message lifetime) | ||||||
* Specifying history retention based on the number of messages (as opposed to | ||||||
their age) in a room. This is descoped because it is effectively a disk space | ||||||
management problem for a given server or client, rather than a policy | ||||||
problem of the room. It can be solved as an implementation specific manner, or | ||||||
a new MSC can be proposed to standardise letting clients specify disk quotas | ||||||
per room. | ||||||
* Per-message retention (as having a mix of message lifetime within a room | ||||||
complicates implementation considerably - for instance, you cannot just | ||||||
purge arbitrary events from the DB without fracturing the DAG of the room, | ||||||
and so a different approach is required) | ||||||
|
||||||
## Proposal | ||||||
|
||||||
### Room Admin-specified per-room retention | ||||||
|
||||||
We introduce a `m.room.retention` state event, which room admins can set to | ||||||
richvdh marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
mandate the history retention behaviour for a given room. It follows the | ||||||
default PL semantics for a state event (requiring PL of 50 by default to be | ||||||
set). | ||||||
|
||||||
The following fields are defined in the `m.room.retention` contents: | ||||||
|
||||||
`max_lifetime`: | ||||||
ara4n marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
the maximum duration in milliseconds for which a server must store this event. | ||||||
ara4n marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
Must be null or an integer in range [0, 2<sup>53</sup>-1]. If absent, or | ||||||
null, should be interpreted as 'forever'. | ||||||
|
||||||
`min_lifetime`: | ||||||
ara4n marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
the minimum duration in milliseconds for which a server should store this event. | ||||||
ara4n marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
Must be null or an integer in range [0, 2<sup>53</sup>-1]. If absent, or | ||||||
null, should be interpreted as 'forever'. | ||||||
|
||||||
`expire_on_clients`: | ||||||
a boolean for whether clients must expire messages clientside to match the | ||||||
min/max lifetime fields. If absent, or null, should be interpreted as false. | ||||||
The intention of this is to distinguish between rules intended to impose a | ||||||
data retention policy on the server - versus rules intended to provide a | ||||||
degree of privacy by requesting all data is purged from all clients after a | ||||||
given time. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If this is (Reading below, it seems that this is the case, but it seems unclear to me here.) |
||||||
|
||||||
Retention is only considered for non-state events. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Stupid question as general audience: what does this imply for the room topic, membership, etc. state data? Is the full history of e.g. who was a member of a room and when retained or purged? If retained, should the summary on the top mention this limitation? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Those are state events and according to the current MSC they must be retained. The issue here is that some state events are used to authorise new events, e.g. so you can't send messages into a room that you haven't joined (i.e. in the state of which there's no join event from you), so purging state events could potentially break the room. We could theoretically avoid that by carefully selecting which state events should not be purged and which ones can (and I'm not even sure about that) but then it becomes a ticking time bomb because one day we're bound to forget about that and make some changes in state events without updating the retention policies spec and break everything. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I think this limitation is mentioned at the right place here, but ymmv. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The summary contains a statement "... set of rules which allow users, room admins and server admins to determine how long data should be stored for a room, from the perspective of respecting the privacy requirements of that room" which seems to incorrectly imply that the retention rules apply to all data. This was my initial understanding also when reading the configuration file in the current synapse implementation. Just a suggestion from user perspective, but I think it would be important to be clear what it does and doesn't do, so that people can make an informed decision. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh right, that makes sense, fair point. |
||||||
|
||||||
If set, these fields SHOULD replace other retention behaviour configured by | ||||||
the user or server admin - even if it means forcing laxer privacy requirements | ||||||
on that user. This is a conscious privacy tradeoff to allow admins to specify | ||||||
explicit privacy requirements for a room. For instance, a room may explicitly | ||||||
require all messages in the room be stored forever with `min_lifetime: null`. | ||||||
|
||||||
In the instance of `min_lifetime` or `max_lifetime` being overridden, the | ||||||
invariant that `max_lifetime >= min_lifetime` must be maintained by clamping | ||||||
max_lifetime to be equal to `min_lifetime`. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IMHO, it makes more sense to clamp There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. agreed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could this also be added as a fallback when the |
||||||
|
||||||
If the user's retention settings conflicts with those in the room, then the | ||||||
ara4n marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
user's clients are expected to warn the user when participating in the room. | ||||||
A conflict exists if the user has configured their client to create rooms with | ||||||
retention settings which differing from the values on the `m.room.retention` | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
state event. This is particularly important to warn the user if the room's | ||||||
ara4n marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
retention is longer than their default requested retention period. | ||||||
|
||||||
For instance: | ||||||
|
||||||
```json | ||||||
{ | ||||||
"max_lifetime": 86400000, | ||||||
} | ||||||
``` | ||||||
|
||||||
The above example means that servers receiving messages in this room should | ||||||
store the event for only 86400 seconds (1 day), as measured from that | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||||
event's `origin_server_ts`, after which they MUST purge all references to that | ||||||
event (e.g. from their db and any in-memory queues). | ||||||
|
||||||
We consciously do not redact the event, as we are trying to eliminate metadata | ||||||
and save disk space at the cost of deliberately discarding older messages from | ||||||
the DAG. | ||||||
|
||||||
```json | ||||||
{ | ||||||
"min_lifetime": 2419200000, | ||||||
} | ||||||
``` | ||||||
|
||||||
The above example means that servers receiving this message SHOULD store the | ||||||
event forever, but can choose to purge their copy after 28 days (or longer) in | ||||||
order to reclaim diskspace. | ||||||
|
||||||
### Server Admin-specified per-room retention | ||||||
|
||||||
Server admins have two ways of influencing message retention on their server: | ||||||
|
||||||
1) Specifying a default `m.room.retention` for rooms created on the server, as | ||||||
ara4n marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
defined as a per-server implementation configuration option which inserts the | ||||||
state events after creating the room, and before `initial_state` is applied on | ||||||
`/createRoom` (effectively augmenting the presets used when creating a room). | ||||||
If a server admin is trying to conserve diskspace, they may do so by | ||||||
specifying and enforcing a relatively low min_lifetime (e.g. 1 month), but not | ||||||
specify a max_lifetime, in the hope that other servers will retain the data | ||||||
for longer. | ||||||
|
||||||
XXX: is this the correct approach to take? It's how we force E2E encryption | ||||||
ara4n marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
on, but it feels very fragmentory to have magical presets which do different | ||||||
things depending on which server you're on. The alternative would be some | ||||||
kind of federation-aware negotiation where a server refuses to participate in | ||||||
a room unless it gets its way on retention settings, however this feels | ||||||
unnecessarily draconian. | ||||||
|
||||||
2) By adjusting how aggressively their server enforces the the `min_lifetime` | ||||||
ara4n marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
value for message retention within a room. For instance, a server admin could | ||||||
configure their server to attempt to automatically purge remote messages in | ||||||
public rooms which are older than three months (unless min_lifetime for those | ||||||
messages was set higher). | ||||||
|
||||||
A possible implementation-specific server configuration here could be | ||||||
something like: | ||||||
* target_lifetime_public_remote_events: 3 months | ||||||
* target_lifetime_public_local_events: null # forever | ||||||
* target_lifetime_private_remote_events: null # forever | ||||||
* target_lifetime_private_local_events: null # forever | ||||||
|
||||||
...which would try to automatically purge remote events from public rooms after | ||||||
3 months (assuming their individual min_lifetime is not higher), but leave | ||||||
others alone. | ||||||
|
||||||
These config values would interact with the min_lifetime and max_lifetime | ||||||
values in the different classes of room by decreasing the effective | ||||||
max_lifetime to the proposed value (whilst preserving the `max_lifetime >= | ||||||
min_lifetime` invariant). However, the precise behaviour would be up to the | ||||||
server implementation. | ||||||
|
||||||
Server admins could also override the requested retention limits (e.g. if | ||||||
resource constrained), but this isn't recommended given it may result in | ||||||
history being irrevocably lost against the senders' wishes. | ||||||
|
||||||
## Pruning algorithm | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do need something here to encourage clients to delete/discard the megolm keys for pruned e2e convos? |
||||||
|
||||||
To summarise, servers and clients must implement the pruning algorithm as | ||||||
follows: | ||||||
|
||||||
If we're a client (including bots and bridges), apply the algorithm: | ||||||
* if specified, the `expire_on_clients` field in the `m.room.retention` event for the room is true. | ||||||
ara4n marked this conversation as resolved.
Show resolved
Hide resolved
ara4n marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
* otherwise, don't apply the algorithm. | ||||||
|
||||||
The maximum lifetime of an event is calculated as: | ||||||
* if specified, the `max_lifetime` field in the `m.room.retention` event for the room. | ||||||
* otherwise, the message's maximum lifetime is considered 'forever'. | ||||||
|
||||||
The minimum lifetime of an event is calculated as: | ||||||
* if specified, the `min_lifetime` field in the `m.room.retention` event for the room. | ||||||
* otherwise, the message's minimum lifetime is considered 'forever'. | ||||||
* for clients, `min_lifetime` should be considered to be 0 (as there is no | ||||||
requirement for clients to persist events). | ||||||
|
||||||
If the calculated `max_lifetime` is less than the `min_lifetime` then the `max_lifetime` | ||||||
is set to be equal to the `min_lifetime`. | ||||||
|
||||||
The server/client then selects a lifetime of the event to lie between the | ||||||
calculated values of minimum and maximum lifetime, based on their implementation | ||||||
and configuration requirements. The selected lifetime MUST NOT exceed the | ||||||
calculated maximum lifetime. The selected lifetime SHOULD NOT be less than the | ||||||
calculated minimum lifetime, but may be less in case of constrained resources, | ||||||
in which case the server should prioritise retaining locally generated events | ||||||
over remote generated events. | ||||||
|
||||||
Server/clients then set a maintenance task to remove ("purge") old events and | ||||||
references to their IDs from their DB and in-memory queues after the lifetime | ||||||
has expired (starting timing from the absolute origin_server_ts on the event). | ||||||
ara4n marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
It's worth noting that this means events may sometimes disappear from event | ||||||
streams; calling the same `/sync` or `/messages` API twice may give different | ||||||
results if some of the events have disappeared in the interim. | ||||||
|
||||||
In order to retain the integrity of the DAG for the room on the server, events | ||||||
which form forward extremities for a room should not be purged but redacted. | ||||||
|
||||||
XXX: is this sufficient? Should we keep a heuristic of the number of | ||||||
ara4n marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
redacted events which hang around, just in case some lost server reappears | ||||||
ara4n marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
from a netsplit and tries referencing older events? Perhaps we can check | ||||||
the other servers in the room to ensure that we don't purge events their | ||||||
forward extremities refer to (except this won't work if the other servers | ||||||
have netsplit) | ||||||
|
||||||
If possible, servers/clients should remove downstream notifications of a message | ||||||
once it has expired (e.g. by cancelling push notifications). | ||||||
|
||||||
If a user tries to re-backfill in history which has already been purged, it's | ||||||
up to the server implementation's configuration on whether to allow it or not, | ||||||
ara4n marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
and if allowed, configure how long the backfill should persist before being | ||||||
purged again. | ||||||
|
||||||
Media uploads must also be expired in line with the retention policy of the | ||||||
richvdh marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
room. For unencrypted rooms this is easy; when the event that references a | ||||||
piece of content is expired, the content must be expired too - assuming the | ||||||
content was first uploaded in that room. (This allows for content reuse in | ||||||
ara4n marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
retention-limited rooms for things like stickers). | ||||||
|
||||||
For encrypted rooms, there is (currently) no alternative than have the client | ||||||
manually delete media content from the server as it expires its own local | ||||||
copies of messages. (This requires us to have actually implemented a media | ||||||
deletion API at last.) | ||||||
ara4n marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
|
||||||
Clients and Servers should not default to setting a `max_lifetime` when | ||||||
creating rooms; instead users should only specify a `max_lifetime` when they | ||||||
need it for a specific conversation. This avoids unintentionally stopping | ||||||
users from using Matrix as a way to archive their conversations if they want. | ||||||
|
||||||
## Tradeoffs | ||||||
|
||||||
This proposal tries to keep it simple by letting the room admin mandate the | ||||||
retention behaviour for a room. However, we could alternatively have a negotiation | ||||||
between the client and its server to determine the viable retention for a room. | ||||||
Or we could have the servers negotiate together to decide the retention for a room. | ||||||
Both seem overengineered, however. | ||||||
|
||||||
This proposal deliberately doesn't address GDPR erasure or mega-redaction scenarios, | ||||||
ara4n marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
as it attempts to build a coherent UX around the use case of users knowing their | ||||||
privacy requirements *at the point they send messages*. Meanwhile GDPR erasure is | ||||||
handled elsewhere (and involves hiding rather than purging messages, in order to | ||||||
avoid annhilating conversation history), and mega-redaction is yet to be defined. | ||||||
|
||||||
It also doesn't solve specifying storage quotas per room (i.e. "store the last | ||||||
500 messages in this room"), to avoid scope creep. This can be handled by an | ||||||
MSC for configuring resource quotas per room (or per user) in general. | ||||||
|
||||||
It also doesn't solve per-message retention behaviour - this has been split out | ||||||
into a seperate MSC. | ||||||
|
||||||
## Issues | ||||||
|
||||||
Should room retention be announced in a room per-server? The advantage is full | ||||||
ara4n marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
flexibility in terms of servers announcing their different policies for a room | ||||||
(and possibly letting users know how likely history is to be retained, or conversely | ||||||
letting servers know if they need to step up to retain history). The disadvantage | ||||||
is that it could make for very complex UX for end-users: "Warning, some servers in | ||||||
this room have overridden history retention to conflict with your preferences" etc. | ||||||
ara4n marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
|
||||||
## Security considerations | ||||||
|
||||||
It's always a gentlemen's agreement for servers and clients alike to actually | ||||||
uphold the requested retention behaviour; users should never rely on deletion | ||||||
actually having happened. | ||||||
|
||||||
## Conclusion | ||||||
|
||||||
Previous attempts to solve this have got stuck by trying to combine together too many | ||||||
disparate problems (e.g. reclaiming diskspace; aiding user data privacy; self-destructing | ||||||
messages; mega-redaction; clearing history on specific devices; etc) - see | ||||||
https://github.com/matrix-org/matrix-doc/issues/440 and https://github.com/matrix-org/matrix-doc/issues/447 | ||||||
for the history. | ||||||
|
||||||
This proposal attempts to simplify things to strictly considering the question of | ||||||
how long servers (and clients) should persist events for. |
Uh oh!
There was an error while loading. Please reload this page.