Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Visibility of Events over Federation

When Matrix homeservers share rooms with each other, they should generally share the events in the rooms as well. But for various reasons, there may be events that a homeserver should be able to see, which are not in its local database. This may be because a network failure may have caused a transaction to get dropped, or because a homeserver may have joined a room late and does not have the full backlog.

To remedy this, the Matrix server-server specification contains several endpoints which allow homeservers to query each other about the events in rooms. However, there is not a lot of specification about when these events actually should be sent, and when they should not. We obviously don’t want just anybody to be able to access any event on our homeserver. This document lays out some rules for what events we share with other homeservers, and which we do not.

Since the specification itself does not describe this, we should instead lean on the behaviour of Synapse. This document describes how Synapse handles the visibility of events over federation, and argues why our API should behave the same way.

What Does Synapse Do

The rules for fetching events in Synapse depend on the endpoint used. The Matrix server-server specification has two kinds of endpoints for requesting events: The endpoints which are restricted to returning state events, and those that return both state and message-like events.

For both types of endpoints, Synapse will refuse requests for events in which the requesting homeserver is not currently a member. For this purpose, Synapse has the function assert_host_in_room which checks whether the current state of the room contains a membership event for a user from the foreign homeserver where the membership is join. If no user from the foreign homeserver is currently in the room, it returns a 403 error.

Endpoints Which Return Only State Events

For endpoints which can only request state events, assert_host_in_room is the only check which will be performed before returning the requested events. In effect, any server in a room can view all state events in the history of the room.

An example of an endpoint which returns the current state of the room is GET /_matrix/federation/v1/state/{roomId} (Synapse handler).

Endpoints Which Return State and Message Events

For endpoints which may return message-like events Synapse performs additional filtering in addition checking for current membership. Only events which at least one of the users hosted on the requesting homeserver may be allowed to see are sent to the requesting homeserver in full, while other events are redacted before sending. Synapse does this using the function filter_events_for_server, which applies the redaction algorithm to all events that foreign homeservers ask for that no user on the homeserver is allowed to see according to the rules in the history visibility module of the client-server-specification.

An example of an endpoint that may return both state and message-like events is GET /_matrix/federation/v1/event/{eventId} (Synapse handler)

Reasoning Behind Redacting

Returning all the events in the DAG in redacted format, instead of simply not returning them at all, is necessary for each homeserver to have a complete view of the structure of the DAG, which in turn is necessary for state resolution. Many parts of state resolution, such as reverse topological power ordering and mainline ordering, depend on homeservers being able to sort state events topologically in the DAG. Sorting events topologically is achieved by sorting them on the “depth” field of the PDU. But blindly trusting the value of “depth” set by foreign homeservers can lead to security issues, and should therefore never be done. Since the DAG is a hash graph, where each event contains a reference to the hashes of previous events, the only way to know that each event is telling the truth about its position in the DAG, is by having access to the whole chain from the root to the event itself. Therefore, for every homeserver to be able to participate in the room and perform state resolution, every homeserver should be able to see the complete DAG, possibly with redacted events.

Sending events to a homeserver, that nobody on the homeserver should be able to see of course has security implications. In effect, a homeserver joining a room where it should have no access to history, will be able to see the timestamps of all messages ever sent in the room. However, redacting message-like events before sending them to the requesting homeserver at least removes the content of the messages.

What Arminius Should Do

A spec-issue is filed under #1519 in the Arminius repository.

Outside of that I think the safest path without a spec is duplicating Synapse’s behaviour, in order to maintain compatibility where there is no specification.

There are some areas where I think that the Synapse implementation is less than optimal, but improvements to that should be discussed in the framework of an MSC.