~/ Contact Uptime News Notes Donate :3

# Restoration of Synapse after losing 1 year of database history

## Introduction

In July 2025, my Synapse PostgreSQL had broken, so I needed to restore it from my 1-y.o. backup. I was thinking about what to do, and my friend sent me this article, so we understood what to do!

For example, there is a room called #example:example.org (roomid !example:example.org), whose member was @user:example.net. The problem is that every server knows @user:example.net is in the room #example:example.org. But our server doesn’t! So that is what we should aim at solving the problem.

## Architecture

We get 2 types of error messages:

- `synapse.handlers.receipts: [PUT-...] Ignoring receipt for room '!example:example.org' from server example.org as we're not in the room` (there is the same error message, but when you get a typing event from an unknown room)
- `synapse.federation.federation_server: [PUT-...] Ignoring PDU for unknown room_id: !example:example.org`

We can read the journal and parse the error message (I use a bash script to do that). Okay, we have a room id! In the given article, the author is the only user of their server, so they can just send POST /_matrix/client/v3/join/!example:example.org request, and everything will be good. But my server has multiple users, so I can’t use the easiest way. We need to get information about membership (so we need to get state).

As we are admins of our servers, we have their signing keys. We will need them because we are going to get state via Federation API requests. How? There are different ways to do that. However, all of them require at least 1 event id, so firstly we need to do GET /_matrix/federation/v1/timestamp_to_event. Determining request parameters (?dir as direction (b[ackward] or f[orward]) and &ts as timestamp in Unix time format in milliseconds), we have just got either the last or the first event id! In my case, I chose the way of getting the last event, so then I’ll have to make GET /_matrix/federation/v1/state/!example:example.org?event_id=$event_id, and I will get the actual state. Of course, both of the requests, as I said, should be signed using your server’s key (how to sign events).

(There is no support of timestamp_to_event in Conduit, so it would be a good idea to make support of requesting different servers)

After we receive the state, we need to parse it, but I think this can’t make a difficulty. As a result, we have a list of our users that were in the given room, like [“@user:example.net”, “@anotheruser:example.net”, “@exampleuser:example.net”]. Then, the simplest way is to get temporary user’s auth token via POST /_synapse/admin/v1/users/user_id/login and make POST /_matrix/client/v3/join/!example:example.org from the name of the user using their token. The problem is solved!

## External links

- Article that gave me the idea
- Matrix Federation API docs
  - How to sign Federation API requests
  - GET /_matrix/federation/v1/state/!example:example.org?event_id=$event_id
  - GET /_matrix/federation/v1/timestamp_to_event
- Matrix Client API docs
  - POST /_matrix/client/v3/join/!example:example.org
- Admin API POST /_synapse/admin/v1/users/user_id/login