matrix-doc/proposals/3952-intentional-mentions.md

26 KiB

MSC3952: Intentional Mentions

Mentioning other users on Matrix is difficult -- it is not possible to know if mentioning a user by display name or Matrix ID will count as a mention, but is also too easy to mistakenly mention a user.

(Note that throughout this proposal "mention" is considered equivalent to a "ping" or highlight notification.)

Some situations that result in unintentional mentions include:

  • Replying to a message will re-issue pings from the initial message due to fallback replies.
    • A user without the power level to send @room can abuse this by including @room in a message and getting a user with the appropriate power levels to reply to them.
  • Each time a message is edited the new version will be re-evaluated for mentions.
  • Mentions occurring in spoiler contents or code blocks are evaluated.
  • If the localpart of your Matrix ID is a common word then the push rule matching usernames (.m.rule.contains_user_name) matches too often (e.g. Travis CI matching if your Matrix ID is @travis:example.org).
  • If the localpart or display name of your Matrix ID matches the hostname (e.g. @example:example.org receives notifications whenever @foo:example.org is replied to).

As a sender you do not know if including the user's display name or Matrix ID would even be interpreted as a mention (see issue 353). This results in some unexpected behavior and bugs:

  • Matrix users use "leetspeak" when sending messages to avoid mentions (e.g. referring to M4tthew instead of Matthew).
  • It is impossible to ping one out of multiple people with the same localpart (or display name).
  • Since the relation between body and formatted_body is ill-defined and "pills" are converted to display names, this can result in missed messages. 1

There are also some other related bugs:

  • Matrix users will append emoji or other unique text in their display names to avoid unintentional pings.
  • Bridging mentions is suboptimal since they use display names as a workaround, e.g.:
    • It breaks the contract that bridges will not mutate the content of messages.
    • For some protocols, bridges need try to figure out if every message contains any of the possible nicknames of room members.
  • If a user changes their display name in a room, they might not be mentioned unless the historical display name is used while processing push rules.

Background

Mentions are powered by two of the default push rules that search an event's content.body property for the current user's display name (.m.rule.contains_display_name) or the localpart of their Matrix ID (.m.rule.contains_user_name).

There's also a section about "user and room mentions" which defines that messages which mention the current user in the formatted_body of the message should be colored differently:

If the current user is mentioned in a message (either by a mention as defined in this module or by a push rule), the client should show that mention differently from other mentions, such as by using a red background color to signify to the user that they were mentioned.

Proposal

The existing push rules for user and room mentions are deprecated and new rules, which use a property specific for mentions2, are added to make mentions simpler and more reliable for users.

New event property

A new m.mentions property is added to the event content; it is an object with two optional properties:

  • user_ids: an array of strings consisting of Matrix IDs to mention.
  • room: a boolean, true indicates an "@room" mention. Any other value or the property missing is interpreted as not an "@room" mention.

It is valid to include both the user_ids and room properties.

It is recommended that homeservers reject locally created events with an invalid m.mentions property with an error with a status code of 400 and an errcode of M_INVALID_PARAM.

Clients add a Matrix ID to the user_ids array whenever composing a message which includes an intentional mention, such as a "pill". Clients set the room value to true when making a room-wide announcement. Clients should also set these values at other times when it is obvious the user intends to explicitly mention a user.3

The m.mentions property is part of the plaintext event body and should be encrypted into the ciphertext for encrypted events.

New push rules

Two new default push rule are added.

The .m.rule.is_user_mention override push rule would appear directly before the .m.rule.contains_display_name push rule:

{
    "rule_id": ".m.rule.is_user_mention",
    "default": true,
    "enabled": true,
    "conditions": [
        {
            "kind": "event_property_contains",
            "key": "content.m\\.mentions.user_ids",
            "value": "[the user's Matrix ID]"
        }
    ],
    "actions": [
        "notify",
        {
            "set_tweak": "sound",
            "value": "default"
        },
        {
            "set_tweak": "highlight"
        }
    ]
}

(Note: \\. would become a single logical backslash followed by a dot since the above is in JSON-representation. See MSC3873.)

The .m.rule.is_room_mention override push rule would appear directly before the .m.rule.roomnotif push rule:

{
    "rule_id": ".m.rule.is_room_mention",
    "default": true,
    "enabled": true,
    "conditions": [
        {
            "kind": "event_property_is",
            "key": "content.m\\.mentions.room",
            "value": true
        },
        {
            "kind": "sender_notification_permission",
            "key": "room"
        }
    ],
    "actions": [
        "notify",
        {
            "set_tweak": "highlight"
        }
    ]
}

An example event matching both .m.rule.is_user_mention (for @alice:example.org) and .m.rule.is_room_mention is provided below:

{
    "content": {
        "body": "This is an example mention @alice:example.org",
        "format": "org.matrix.custom.html",
        "formatted_body": "<b>This is an example mention</b> <a href='https://matrix.to/#/@alice:example.org'>Alice</a>",
        "msgtype": "m.text",
        "m.mentions": {
            "user_ids": ["@alice:example.org"],
            "room": true
        }
    },
    "event_id": "$143273582443PhrSn:example.org",
    "origin_server_ts": 1432735824653,
    "room_id": "!somewhere:over.the.rainbow",
    "sender": "@example:example.org",
    "type": "m.room.message",
    "unsigned": {
        "age": 1234
    }
}

Client behavior

The overall user experience is not modified, beyond improving explicitness and reducing unintended mentions.

For example, it is common that a client will show an event with a mention in a different color (and denote the current user's "pill", as a way of showing the user why they were mentioned). This behavior is unchanged.

There are two variations that clients should take into account when decorating messages for mentions, however:

  • The presence of a user's "pill" in a message no longer implies it is a mention.
  • This makes it easier to mention users without including their "pill" in a message (see Abuse Potential for ideas to combat this).

Backwards compatibility

The .m.rule.contains_display_name, .m.rule.contains_user_name, and .m.rule.roomnotif push rules are to be deprecated.

To avoid unintentional mentions these rules are modified to only apply when the m.mentions property is missing; clients should provide at least an empty m.mentions property on every message to avoid the unintentional mentions discussed in the introduction.

A future room version may wish to disable the legacy push rules: clients would no longer be required to include the m.mentions property on every event. It maybe convenient to do this when extensible events are adopted (see MSC3932).

After acceptance, it is likely for there to be disagreement about which push rules are implemented: legacy clients and homeservers may not yet have deprecated the .m.rule.contains_display_name, .m.rule.contains_user_name, and .m.rule.roomnotif push rules, while up-to-date clients and homeservers will support the .m.rule.is_user_mention and .m.rule.is_room_mention push rules. It is expected that both sets of push rules will need to be supported for a period of time, but at worst case should simply result in the current behavior (documented in the preamble).

If users wish to continue to be notified of messages containing their display name it is recommended that clients create a specific keyword rule for this, e.g. a content rule of the form:

{
  "actions": [
    "notify",
    {
      "set_tweak": "sound",
      "value": "default"
    },
    {
      "set_tweak": "highlight"
    }
  ],
  "pattern": "alice",
  "rule_id": "alice",
  "enabled": true
}

Impact on replies

Users are notified of replies via the .m.rule.contains_display_name or the .m.rule.contains_user_name push rule matching the rich reply fallback. Unfortunately these push rules will be disabled for events which contain the m.mentions property, i.e. all newly created events (see above). Clients should include the sender of the event being replied to as well as any mentioned users in that event (excluding yourself) in the new event's m.mentions property. The room property MUST NOT be copied over.

This signals that it is the intention of the sender to mention all of those people. This behavior may not make sense in all situations (e.g. an email-like client could provide both a "reply" and "reply all", while a microblogging client may wish to provide a "quote reply", dropping all mentions from the original event) and clients may wish to allow users to modify the list of mentioned users.

For example, if there is an event:

{
  "sender": "@dan:example.org",
  "event_id": "$initial_event",
  "content": {
    "body": "Alice: Have you heard from Bob?",
    "m.mentions": {
      "user_ids": ["@alice:example.org", "@bob:example.org"]
    }
  },
  // other fields as required by events
}

And a reply from Alice:

{
  "content": {
    "body": "> <@dan:example.org> Alice: Have you heard from Bob?\n\nNo, but I saw him with Charlie earlier.",
    "m.mentions": {
      "user_ids": [
        // Include the sender of $initial_event (optional).
        "@dan:example.org",
        // The users mentioned, minus yourself (optional).
        "@bob:example.org",
        // New mentions, as normal.
        "@charlie:example.org"
      ]
    },
    "m.relates_to": {
      "m.in_reply_to": {
          "event_id": "$initial_event"
      }
    }
  },
  // other fields as required by events
}

If a user wishes to be notified of all replies to their messages, other solutions should be investigated, such as MSC3664. This would give more equal power to both senders and receivers of events.

Impact on edits

Similarly to replies, users are notified of message edits via the .m.rule.contains_display_name or the .m.rule.contains_user_name push rule matching the fallback content. Generally this is undesirable and users do not need to be notified for the same message multiple times (e.g. if a user is fixing a typo).

Replacement events may have m.mentions properties in two locations:

  • One at the top-level of the content, which should contain any users to mention for this edit.
  • One inside the m.new_content property, which should contain the full list of mentioned users in any version of the event, unless a mention is removed (see below).

It is recommended that clients use an empty top-level m.mentions property when editing an event, unless the edit is significant or if additional users are mentioned in the latest version.

For example, if there is an event:

{
  "sender": "@dan:example.org",
  "event_id": "$initial_event",
  "content": {
    "body": "Hello Alice!",
    "m.mentions": {
      "user_ids": ["@alice:example.org"]
    }
  },
  // other fields as required by events
}

And an edit after realizing that Bob is also in the room:

{
  "content": {
    "body": "* Hello Alice & Bob!",
    "m.mentions": {
      "user_ids": [
        // Include only the newly mentioned user.
        "@bob:example.org"
      ]
    },
    "m.new_content": {
      "body": "Hello Alice & Bob!",
      "m.mentions": {
        "user_ids": [
          // Include all mentioned users.
          "@alice:example.org",
          "@bob:example.org"
        ]
      },
    },
    "m.relates_to": {
      "rel_type": "m.replace",
      "event_id": "$initial_event"
    }
  },
  // other fields as required by events
}

Mentions can also be removed as part of an edit. In this case, the top-level m.mentions property would not include the removed user IDs (you cannot cancel the notification from the previous event) or any previously notified users, and the removed user would also be removed from the m.new_content proprerty's copy of m.mentions.

This should limit duplicate, unnecessary notifications for users. If a user wishes to receive notifications for edits of events they were mentioned in then they could setup a push rule for the content.m\\.new_content.m\\.mentions property or potentially leverage MSC3664.

This implies that:

  • If a client highlights a message visually (e.g. by coloring it red), then it should look at the m.mentions under m.new_content for edited messages. Otherwise, in the example above, Alice would not see the message as red, even though the intent was for her to be mentioned.
  • Any sort of processing of push rules, e.g. to display a notification (sound, toast, push notification), should occur without any special rules. I.e. the .m.rule.is_user_mention and .m.rule.is_room_mention should look at the m.mentions directly under content and not match for Alice.

Impact on bridging

For protocols with a similar mechanism for listing mentioned users this should strengthen the bridging contract as it enables bridges to stop mutating the content of messages. The bridge should be able to map from the remote user ID to the bridged user ID and include that in the m.mentions property of the Matrix event & the proper field in the bridged protocol4.

For bridged protocols that do not have this mechanism, the bridge will only be able to stop mutating content on messages bridged into Matrix. Messages bridged out of Matrix will still need to embed the mention into the text content.5

Potential issues

Abuse potential

This proposal makes it trivial to "hide" mentions since it does not require the mentioned Matrix IDs to be part of the displayed text. This is only a limitation for current clients: mentions could be exposed in the user interface directly. For example, a de-emphasized "notified" list could be shown on messages, similar to CCing users on an e-mail.

Although not including mentions in the displayed text could be used as an abuse vector, it does not enable additional malicious behavior than what is possible today. From discussions and research while writing this MSC there are moderation benefits to using a separate property for mentions:

  • The number of mentions is trivially limited by moderation tooling, e.g. it may be appropriate for a community room to only allow 10 mentions. Events not abiding by this could be rejected automatically (or users could be banned automatically).
  • Various forms of "mention bombing" are no longer possible.
  • It is simpler to collect metrics on how mentions are being used (it is no longer necessary to process the textual body for every user's display name and local part).

Overall this proposal seems to be neutral or positive in the ability to combat malicious behavior.

Encrypted mentions & /notifications

A previous version of this proposal (and the alternative MSC1796) suggested leaving the m.mentions property in cleartext. This was deemed too large of a metadata leak and removed from this proposal (and MSC1796 was closed). A downside of this is that homeservers (still) will not be able to differentiate between notifications and mentions in many cases.

This mostly affects how often homeservers push to devices (see MSC3996 for more information), but also means that the /notifications?only=highlight API is not useful in encrypted rooms.

Future extensions

Combating abuse

Some ideas for combating abuse came from our discussion and research which are worth sharing. These ideas are not a requirement for implementing this MSC, and generally do not depend on it. (They could be implemented today with enough effort.)

It was recommended that clients could expose why an event has caused a notification and give users inline tools to combat abuse. For example, a client might show a tooltip:

The sender of the message (@alice:example.org) mentioned you in this event.

Block @alice:example.org from sending you messages? [Yes] [No]

Additionally, if a user sending a message is about to mention many people it can be useful to confirm whether they wish to do that (or prompt them to do an @room mention instead).

Moderators may find tooling to quickly find messages which mention many users useful in combating mention spammers. (Note that this should be made easier by this MSC.)

A future MSC might wish to explore features for trusted contacts or soft-ignores to give users more control over who can generate notifications.

Muted except for mentions push rules

It might be desirable to have a "muted-except-for-mentions" feature for large (encrypted) rooms. This is particularly useful on iOS where a push notification can be decrypted via a background process but cannot be suppressed. This means it is not possible for the client to handle this feature and it must be handled on the server, unfortunately this would not be possible with the current proposal since the m.mentions property is encrypted (and the server cannot act on it).

Solving this problem is left to a future MSC, such as MSC3996 which builds on this proposal.

Pillifying @room

Some clients attempt to create a "pill" out of @room mentions, but this is not a requirement of the Matrix specification. The current user and rooms mentions section could be expanded for this situation.

Extensible events

Handling of this property in MSC1767-style extensible events is deliberately left for a future MSC to address, if needed.

Role mentions

It is possible to add additional properties to the m.mentions object, e.g. a foreseeable usecase would be a roles property which could include values such as admins or mods. Defining this in detail is left to a future MSC.

Cancelling notifications

It might be useful for a future MSC to investigate cancelling notifications if a user's mention is removed while editing events. This could be quite difficult as it is unclear if the mentioned user has already received the notification or not.

Alternatives

Prior proposals

There are a few prior proposals which tackle subsets of the above problem:

  • MSC1796: similar to the proposal in this MSC, but limited to encrypted events (and kept in cleartext).

  • MSC2463: excludes searching inside a Matrix ID for localparts / display names of other users.

  • MSC3517: searches for Matrix IDs (instead of display name / localpart) and only searches specific event types & properties.

  • Custom push rules to search for matrix.to links instead of display name / localpart.

    The above generates a new push rule to replace .m.rule.contains_display_name and .m.rule.contains_user_name:

    {
       "conditions": [
         {
             "kind": "event_match",
             "key": "content.formatted_body",
             "pattern": "*https://matrix.to/#/@alice:example.org*"
         }
       ],
       "actions" : [
         "notify",
         {
           "set_tweak": "sound",
           "value": "default"
         },
         {
           "set_tweak": "highlight"
         }
       ]
    }
    

The last two proposals use a similar idea of attempting to find "pills" in the formatted_body, this has some downsides though:

  • It doesn't allow for hidden mentions, which can be useful in some situations.
  • It does not work for event types other than m.room.message.

It also adds significant implementation complexity since the HTML messages must now be parsed for notifications. This is expensive and introduces potential security issues.

Room version for backwards compatibility

Alternative backwards compatibility suggestions included using a new room version, similar to MSC3932 for extensible events. This does not seem like a good fit since room versions are not usually interested in non-state events. It would additionally require a stable room version before use, which would unnecessarily delay usage. Another MSC can address this concern, such as in the extensible events series, if desirable to be gated by a room version for a "clean slate" approach.

Security considerations

None not already described.

Unstable prefix

During development the following mapping will be used:

What Stable Unstable
Event property m.mentions org.matrix.msc3952.mentions
Push rule ID .m.rule.* .org.matrix.msc3952.*

The server will include the org.matrix.msc3952_intentional_mentions flag in the unstable_features array of the /versions endpoint. If a client sees this flag it can choose to apply the deprecation logic discussed in the backwards compatibility section.

Dependencies

This depends on the following (accepted) MSCs:

  • MSC3758: Add event_property_is push rule condition kind
  • MSC3873: event_match dotted keys
  • MSC3966: event_property_contains push rule condition

  1. It is defined as the the "plain text version of the HTML [formatted_body] should be provided in the body", but there is no particular algorithm to convert from HTML to plain text except when converting mentions, where the plain text version uses the link anchor, not the link. ↩︎

  2. As proposed in issue 353. ↩︎

  3. Note that this isn't really a change in behavior, it is just making the behavior explicit. It is expected that users already considered "pilled" users to be mentions, and it was more unexpected when non-pilled users were mentioned. This MSC fixes the latter case. ↩︎

  4. Some protocols which provide structured data for mentions include Twitter, Mastodon, Discord, and Microsoft Teams. ↩︎

  5. Unfortunately some protocols do not provide structured data: the message itself must be parsed for mentions, e.g. IRC or Slack. ↩︎