12 KiB
MSC3531: Letting moderators hide messages pending review
Matrix supports redacting messages as a mechanism to remove unwanted content. Redacting events, as defined in the Matrix spec, is a mechanism that entirely removes the content of an event. Users can redact their own events, and room moderators can redact unwanted events, including illegal content. At present, there is no manner of undoing these redactions.
Historically, redacting messages has been useful for two use cases:
- a user accidentally posting a password, credit card number or other confidential information, in which case the information must be scrubbed as fast as possible from all places;
- a moderator removing spam, bullying, etc. from a malicious user / spam bot.
Experience shows that redacting messages for case 2. is not always the best solution:
- moderators make mistakes and there is currently no way for them to fix these;
- in many cases, it may be desirable for a moderator to quickly hide a message before having a conversation with other moderators to determine whether the message should be let through (e.g. discussing whether the local room's CoC should allow a possibly inflamatory political message - or a newbie moderator waiting for experienced moderators to come online to ask them for clarifications on borderline content);
- some bots automatically remove messages based on heuristics (e.g. users sending too many messages or too many images) but may get it wrong, in which case the moderator currently cannot fix the errors of these bots.
In addition, proposals such as MSC3215, which aims to decentralize moderation, will very likely increase the number of moderators - and in particular, the number of moderators who may not be familiar with moderation tools, hence will make mistakes.
For all these reasons, it would be very useful to have a mechanism that would let moderators undo their own redactions. Unfortunately, reversing a redaction is tricky, as we cover in the Alternatives section.
Rather, we propose a spec to let moderators hide messages pending review. This mechanism is entirely client-based and will not prevent hidden messages from being distributed, only from being seen by non-moderator users. This spec can then be used by clients or bots such as Mjölnir to implement two phase redaction:
- a first phase during which messages are flagged for moderation (either by a bot or manually) and hidden from general consumption;
- a second phase during which moderators may either restore the message or
redact
it entirely.
Proposal
We introduce a new type of event: m.visibility
.
Events with type m.visibility
are ignored by clients if they are invalid or sent by users with
a powerlevel insufficient to send a state event m.visibility
. This relation controls whether clients should
display an event or hide it.
An event of m.visibility
MUST with the following content fields:
Content Key | Type | Description |
---|---|---|
m.relates_to |
Visibility Relation | Required The payload for this event |
visible |
boolean |
Required If true , clients should show the affected event normally. If false, clients should mark the affected event as hidden pending review. |
reason |
string |
Optional. If visible is false , a reason that clients MAY display to indicate why the affected event is hidden pending review. |
Visibility relation
Content Key | Type | Description |
---|---|---|
rel_type |
string |
Required Must be "m.reference" |
event_id |
eventId |
Required eventId of the event affected by this visibility change. Must be a past event in this room. |
Server behavior
No changes in server behavior.
Client behavior
- When a client receives an event
event
with typem.visibility
relating to an existing eventoriginal_event
in roomroom
:- If the
event
is well-formed and powerlevel ofevent.sender
inroom
is greater or equal to the powerlevel needed to sent state eventm.visibility
- If
event
specifies a visibility of "hidden", markoriginal_event
as hidden- In every display of
original_event
, either by itself or in a reaction- If the current user is the sender of
original_event
- Label the display of
original_event
with a label such as(pending moderation)
- If
event.content
contains a string fieldreason
, this field may be used to display a reason for moderation.
- Label the display of
- Otherwise, if the current user has a powerlevel greater or
equal to
m.visibility
- Display
original_event
as a spoiler. - Label the display of
original_event
with a label such as(pending moderation)
- If
event.content
contains a string fieldreason
, this field may be used to display a reason for moderation.
- Display
- Otherwise
- Instead of displaying
original_event
, display a message such asMessage is pending moderation
- If
event.content
contains a string fieldreason
, this field may be used to display a reason for moderation.
- Instead of displaying
- If the current user is the sender of
- In every display of
- Otherwise, if
event
specifies a visibility of "visible", markoriginal_event
as visible- Display
original_event
exactly as it would be displayed without this MSC
- Display
- Otherwise, ignore
- If
- Otherwise, ignore
- If the
- When a client prepares to display a message
original_event
with visibility "hidden", whether by itself or in a reaction- (see 1.1.1.1.1. for details on how to display
original_event
)
- (see 1.1.1.1.1. for details on how to display
- If an event
event
withrel_type
m.visibility
and relating to an existing eventoriginal_event
is redacted, update the display ororiginal_event
as per the latest event withrel_type
m.visibility
in this room relating to the sameoriginal_event
.
If several reactions race against each other to mark a message as visible or
hidden, we consider the most recent one (by order of origin_server_ts
) the
source of truth.
For simplicity, if a user gains or loses the powerlevel m.visibility
, this
does not affect any of the m.visibility
relations already sent by that user.
This may, however, affect how hidden events are displayed to this specific user.
Example use
A moderation bot such as Mjölnir might implement two-phase redaction as follows:
- When a room protection rule or a moderator requires Mjölnir to redact a
message
original_message
inroom
- Copy
original_message
to a "moderation pending" room as messagebackup_message
, with some UX to decide whetherbackup_message
should be PASS or REJECT. - Mark
original_message
inroom
as hidden, using the current MSC.
- Copy
- When a moderator marks
backup_message
as PASS- Mark
original_message
inroom
as visible, using the current MSC. - Remove
backup_message
from the "moderation pending" room.
- Mark
- When a moderator marks clone
backup_message
as REJECT- Send a message
m.room.redaction
toroom
to fully redact messageoriginal_message
. - Remove
backup_message
from the "moderation pending" room.
- Send a message
- If, after <some retention duration, e.g. one week>, a clone
backup_message
has been marked neither PASS nor REJECT- Behave as if
backup_message
had been marked REJECT
- Behave as if
Potential issues
Abuse by moderators
This proposal does not give substantial new powers to moderators, so we don't think that there is cause for concern here.
Race conditions
There may be race conditions between e.g. an edition (https://github.com/matrix-org/matrix-doc/pull/2676) and marking a message visible/hidden. We do not think that this can cause any real issue.
Hidden channel
As messages are hidden but still distributed to all clients in the room, it is entirely possible to write a client/bot that ignores hiding and one could imagine using hidden messages to semi-covertly exchange messages in a room.
As there are already countless ways to implement this, we don't foresee this to cause any problem.
Liabilities
It is possible that, in some countries, if moderators decide to mark content as hidden but fail to redact it, this could make the homeserver owner legally responsible for illegal content being exchanged through this covert channel.
We believe that using a bot that automatically redacts hidden messages after a retention period would help administrators avoid such liabilities.
Alternatives
Server behavior changes
We could amend this proposal to have the server reject messages with type
m.visibility
if these messages are sent by a user with a powerlevel below
m.visibility
. However, this would require changes to the flow of encryption
to let the server read the relation between events, something that is less than
ideal.
We prefer requiring that clients ignore messages sent by users without a sufficient powerlevel.
A message to undo a redaction
As the original objective of this proposal is to undo redactions, one could
imagine a message m.room.undo_redaction
with the following behavior:
- The ability to send a
m.room.undo_redaction
is controlled by a powerlevel, just asm.room.redaction
. - When a server receives a
m.room.undo_redaction
for event E, event E loses its "redacted" status, in particular in any futuresync
or/room/.../event/...
or other, the original event E is returned, rather than its redacted status. - When a client receives a
m.room.undo_redaction
for an event E, they need to refetch event E from the homeserver.
This proposal would have the benefit of removing the hidden channel.
However, servers are intended to redact events immediately and permanently, though regulations for some areas of operation require the contents to be preserved for a short amount of time. In any case, it is not possible to determine how long a server is willing or able to keep event contents, so we can only assume it has not kept them at all. Any attempt to undo redaction would, at best, race against this retention duration, which may differ across homeservers in the same room, and might end up causing divergence between the room views.
Thus, undoing is not possible, in practice.
Injecting content in redacted messages
An alternative mechanism to undo redactions would be to let moderators un-redact a message by injecting new content in it. This would let clients or moderation bots such as Mjölnir implement undoing redactions by first backing up redacted messages (in a manner similar to what we discuss in "Example use"), then if a redaction is canceled, reinjecting content. We decided not to pursue this mechanism as it is more complicated and it opens abuse vectors by malicious moderators de facto modifying the content of other user's messages (even if this could be mitigated by clients displaying who has modified a user's messages).
Letting users hide their own messages
There would be use cases for users hiding their own messages, e.g. marking a task as complete. We believe that this complicates the present MSC, as it introduces edge cases that deserve their own discussion, e.g.:
- can a moderator make a message hidden by a user visible?
- how do we reinterpret a sequence of visibility change messages interleaving self-hide, self-unhide, moderator-hide, moderator-unhide when one or more of the messages in the sequence gets redacted?
For these reasons, we prefer postponing such feature to a further MSC.
Security considerations
Old clients
Old clients that do not implement this MSC will continue displaying messages that should be hidden. We believe that it's an acceptable risk, as it does not expose data that is meant to be kept private.
Unstable prefix
During the prototyping phase:
- message type
m.visibility
should be prefixed intoorg.matrix.msc3531.visibility
.