Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
164 changes: 164 additions & 0 deletions proposals/4353-per-origin-linear-chain.md
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implementation requirements:

  • Server

Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
# MSC4353: Per origin linear chain

Currently, servers can produce events that are disconnected from the
server's own causal history. This allows servers to produce events
that are deteched or at conflict with their own decisions. This issue
is exacerbated as servers can also create events concurrent with their
own, and it is not actually possible to prove a given server is a
byzantine traitor/equivocator.

These conditions allow for servers to ignore their own causal history
in order to erase or modify it, leaving other servers without proof of
malice or inconsistency.

## Example using today's room model

![](./images/4353-current-matrix-ban-and-key.png)

Consider A scenario where there are three servers in a room: Blue,
Green, and Yellow. Consider that Blue immediately bans Yellow after
Yellow joins the room and the ban event directly references Yellow's
join in `prev_events`. Consider that concurrent to Blue's ban,
Yellow sends a message. So that Yellow's join event has to known
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Yellow sends a message. So that Yellow's join event has to known
Yellow sends a message. So that Yellow's join event has two known

causal successors, Blue's ban targetting Yellow, and Yellow's
message.

Now consider that Green sends an event referencing Yellow's message
in `prev_events`, and forwards this to Blue.

Now consider that Blue sends an event that references Green's event,
indirectly tying up the diverging branch, but inadvertanly introducing
Yellow's concurrent message into Blue's own recognised causal history.

### Discussion

This is a huge problem. Blue has inadvertently been tricked into referencing
an event that it has likely soft failed (under partial sync). This example
demonstrates that `prev_events` cannot be used as a causal frontier
for a given server's understanding of causal history, because
the server is not actually in control of its own history.

Additionally, it is impossible to know whether Blue's ban on Yellow is
an attempt to abuse the room model to erase Yellow's message
retroactively.

## Proposal

We propose that a new field is added to the PDU, the
`origin_predecessor`.

This means that every event will reference either one or no
predecessors from the same origin.

This has some major advantages:

- Intentional forks are explicit and detectable. Servers cannot create
events concurrent to their own causal predecessors without
explicitly ignoring their maintained forward extremity. The proposal
does not prevent this, but it does mean that such an equivocation is
detectable and provable. And so the sender's repuatation can become
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
detectable and provable. And so the sender's repuatation can become
detectable and provable. And so the sender's reputation can become

tarnished as they are now known to be a byzantine traitor.

- Within the context of the terminology from [MSC4349: Causal barriers
and
enforcement](https://github.com/matrix-org/matrix-spec-proposals/pull/4349),
when servers abuse causal barriers (e.g. membership events) to erase
either their own, or another server's history, this is now always
detectable.

In addition this enhances serveral other proposals both current and
future:

- In-DAG key based MSCs such as MSC4243 and MSC4345, and any
psuedo-identity proposal: it is simplier to reason about key
revocation or rotation when describing the causal frontier for which
the key was valid.

- Any attempt to specify an epoch based synchronisation for rooms
would both be simplified through the use of linear chains and also
and also more secure through equivocation detection.

- Ban events would no longer be plagued by soft failure, as a future
proposal could mandate that the ban is scoped to a target's
predecessor. In order to canonicalise the events that the target
sent before they were banned.

### An example of bans with linear origin chain

![](./images/4353-linear-origin-chain-ban.png)

Now with a linear origin chain in place, we can determine that Blue's
ban on Yellow was not an attempt to erase Yellow.

### An example of erasure with linear origin chain

![](./images/4353-linear-origin-chain-equivocator.png)

If Blue does attempt to retroactively erase Yellow, it must explicitly
create an event that is concurrent with its own origin chain. And thus
it is observable to everyone that Blue is an equivocator/byzantine
traitor. As Blue's own events will have multiple causal successors
sent by Blue.
Comment on lines +93 to +101
Copy link
Contributor Author

@Gnuxie Gnuxie Sep 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's still possible to erase history, it's just that doing so without creating a concurrent event requires you to explicitly acknowledge yellow's history as a part of the ban. And if bans are mandated to be scoped to a target's origin predecessor, it is then actually impossible to abuse to erase history. Since a condition for authorising the ban would be not to contradict your own causal frontier in auth rules.


### Situations for which there is no `origin_predecessor`

We specify the situations for which no `origin_predecessor`
can be expected:

1. The `m.room.create` event.
2. Any `m.room.member` event with a membership of `join` or
`knock`.

This validation is enforced by authorization rules.

Usually there will only be one `m.room.membership` event within a
room's history per sender that has no `origin_predecessor` unless the
sender is an equivocator.

## Potential issues

### Some current implementations may not synchronise within one room

Some implementations may not currently synchronise within one room. We
believe Synapse has a bug where forward extremities can be created
through sequential use of `/send`. It's not understood by me
whether this is a bug or an intentional design decision(?)

### Recovery from downtime/db failure

Extra care must be taken to ensure that implementations maintain
knowledge of their forward_extremity. Currently it doesn't matter if a
server sends events with only partial knowledge of their prior
history. Implementations may need to query other servers and ensure
they have complete knowledge of their own forward extremity.
Comment on lines +127 to +133
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe that if servers persist events before sending them, then the risk is limited to db recovery. While querying other servers for the extremity may be necessary in that scenario, equally the server key could be rotated within the context of #4345 instead. Which removes the risk entirely.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunatly it is entirely possible through a naive db restore that a server sends authorization events that conflict with its prior history that is now unknown to itself. So even if a definition of a traitor is liberalised it's still going to be possible to become one by accident. We don't actually propose that equivicators or inconsistent servers are noted or even acted upon in any way but it's still going to be important that we allow ways out (which key rotation seems to be the best of them).


### Leaving, forgetting, and rejoining

If knowledge of the final leave event is not maintained, a concurrent
join event must be created. Mitigiating this through modification of
the `/make_join` handshake may allow the trusted server to frame the
joiner as an equivocator.

## Alternatives

### Putting the `origin_predecessor` in `prev_events`

We could put the `origin_predecessor` in `prev_events` but:

- Where to find the `origin_predecessor` is no longer deterministic.

Instead we could also put it in the first element of `prev_events` but
this seems inappropriate and allows for implementors to be
accidentally ignorant of the reserved use.

## Security considerations

- None considered

## Unstable prefix

NA.

## Dependencies

None
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading