More stuff

This commit is contained in:
Neil Alexander 2022-05-10 17:45:03 +01:00
parent a4608dbb93
commit 043b2ee30a
No known key found for this signature in database
GPG key ID: A02A2019A2BB0944
18 changed files with 244 additions and 522 deletions

View file

@ -1,3 +1,8 @@
---
title: Contributing
parent: Development
---
# Contributing to Dendrite
Everyone is welcome to contribute to Dendrite! We aim to make it as easy as
@ -16,13 +21,13 @@ We use [golangci-lint](https://github.com/golangci/golangci-lint) to lint
Dendrite which can be executed via:
```
$ golangci-lint run
golangci-lint run
```
We also have unit tests which we run via:
```
$ go test ./...
go test ./...
```
## Continuous Integration
@ -64,7 +69,6 @@ passing tests.
If these two steps report no problems, the code should be able to pass the CI
tests.
## Picking Things To Do
If you're new then feel free to pick up an issue labelled [good first

View file

@ -1,140 +0,0 @@
# Design
## Log Based Architecture
### Decomposition and Decoupling
A matrix homeserver can be built around append-only event logs built from the
messages, receipts, presence, typing notifications, device messages and other
events sent by users on the homeservers or by other homeservers.
The server would then decompose into two categories: writers that add new
entries to the logs and readers that read those entries.
The event logs then serve to decouple the two components, the writers and
readers need only agree on the format of the entries in the event log.
This format could be largely derived from the wire format of the events used
in the client and federation protocols:
C-S API +---------+ Event Log +---------+ C-S API
---------> | |+ (e.g. kafka) | |+ --------->
| Writers || =============> | Readers ||
---------> | || | || --------->
S-S API +---------+| +---------+| S-S API
+---------+ +---------+
However the way matrix handles state events in a room creates a few
complications for this model.
1) Writers require the room state at an event to check if it is allowed.
2) Readers require the room state at an event to determine the users and
servers that are allowed to see the event.
3) A client can query the current state of the room from a reader.
The writers and readers cannot extract the necessary information directly from
the event logs because it would take too long to extract the information as the
state is built up by collecting individual state events from the event history.
The writers and readers therefore need access to something that stores copies
of the event state in a form that can be efficiently queried. One possibility
would be for the readers and writers to maintain copies of the current state
in local databases. A second possibility would be to add a dedicated component
that maintained the state of the room and exposed an API that the readers and
writers could query to get the state. The second has the advantage that the
state is calculated and stored in a single location.
C-S API +---------+ Log +--------+ Log +---------+ C-S API
---------> | |+ ======> | | ======> | |+ --------->
| Writers || | Room | | Readers ||
---------> | || <------ | Server | ------> | || --------->
S-S API +---------+| Query | | Query +---------+| S-S API
+---------+ +--------+ +---------+
The room server can annotate the events it logs to the readers with room state
so that the readers can avoid querying the room server unnecessarily.
[This architecture can be extended to cover most of the APIs.](WIRING.md)
## How things are supposed to work.
### Local client sends an event in an existing room.
0) The client sends a PUT `/_matrix/client/r0/rooms/{roomId}/send` request
and an HTTP loadbalancer routes the request to a ClientAPI.
1) The ClientAPI:
* Authenticates the local user using the `access_token` sent in the HTTP
request.
* Checks if it has already processed or is processing a request with the
same `txnID`.
* Calculates which state events are needed to auth the request.
* Queries the necessary state events and the latest events in the room
from the RoomServer.
* Confirms that the room exists and checks whether the event is allowed by
the auth checks.
* Builds and signs the events.
* Writes the event to a "InputRoomEvent" kafka topic.
* Send a `200 OK` response to the client.
2) The RoomServer reads the event from "InputRoomEvent" kafka topic:
* Checks if it has already has a copy of the event.
* Checks if the event is allowed by the auth checks using the auth events
at the event.
* Calculates the room state at the event.
* Works out what the latest events in the room after processing this event
are.
* Calculate how the changes in the latest events affect the current state
of the room.
* TODO: Workout what events determine the visibility of this event to other
users
* Writes the event along with the changes in current state to an
"OutputRoomEvent" kafka topic. It writes all the events for a room to
the same kafka partition.
3a) The ClientSync reads the event from the "OutputRoomEvent" kafka topic:
* Updates its copy of the current state for the room.
* Works out which users need to be notified about the event.
* Wakes up any pending `/_matrix/client/r0/sync` requests for those users.
* Adds the event to the recent timeline events for the room.
3b) The FederationSender reads the event from the "OutputRoomEvent" kafka topic:
* Updates its copy of the current state for the room.
* Works out which remote servers need to be notified about the event.
* Sends a `/_matrix/federation/v1/send` request to those servers.
* Or if there is a request in progress then add the event to a queue to be
sent when the previous request finishes.
### Remote server sends an event in an existing room.
0) The remote server sends a `PUT /_matrix/federation/v1/send` request and an
HTTP loadbalancer routes the request to a FederationReceiver.
1) The FederationReceiver:
* Authenticates the remote server using the "X-Matrix" authorisation header.
* Checks if it has already processed or is processing a request with the
same `txnID`.
* Checks the signatures for the events.
Fetches the ed25519 keys for the event senders if necessary.
* Queries the RoomServer for a copy of the state of the room at each event.
* If the RoomServer doesn't know the state of the room at an event then
query the state of the room at the event from the remote server using
`GET /_matrix/federation/v1/state_ids` falling back to
`GET /_matrix/federation/v1/state` if necessary.
* Once the state at each event is known check whether the events are
allowed by the auth checks against the state at each event.
* For each event that is allowed write the event to the "InputRoomEvent"
kafka topic.
* Send a 200 OK response to the remote server listing which events were
successfully processed and which events failed
2) The RoomServer processes the event the same as it would a local event.
3a) The ClientSync processes the event the same as it would a local event.

View file

@ -1,26 +1,31 @@
---
title: FAQ
nav_order: 1
---
# Frequently Asked Questions
### Is Dendrite stable?
## Is Dendrite stable?
Mostly, although there are still bugs and missing features. If you are a confident power user and you are happy to spend some time debugging things when they go wrong, then please try out Dendrite. If you are a community, organisation or business that demands stability and uptime, then Dendrite is not for you yet - please install Synapse instead.
### Is Dendrite feature-complete?
## Is Dendrite feature-complete?
No, although a good portion of the Matrix specification has been implemented. Mostly missing are client features - see the readme at the root of the repository for more information.
### Is there a migration path from Synapse to Dendrite?
## Is there a migration path from Synapse to Dendrite?
No, not at present. There will be in the future when Dendrite reaches version 1.0.
### Can I use Dendrite with an existing Synapse database?
## Can I use Dendrite with an existing Synapse database?
No, Dendrite has a very different database schema to Synapse and the two are not interchangeable.
### Should I run a monolith or a polylith deployment?
## Should I run a monolith or a polylith deployment?
Monolith deployments are always preferred where possible, and at this time, are far better tested than polylith deployments are. The only reason to consider a polylith deployment is if you wish to run different Dendrite components on separate physical machines.
### I've installed Dendrite but federation isn't working
## I've installed Dendrite but federation isn't working
Check the [Federation Tester](https://federationtester.matrix.org). You need at least:
@ -28,42 +33,43 @@ Check the [Federation Tester](https://federationtester.matrix.org). You need at
* A valid TLS certificate for that DNS name
* Either DNS SRV records or well-known files
### Does Dendrite work with my favourite client?
## Does Dendrite work with my favourite client?
It should do, although we are aware of some minor issues:
* **Element Android**: registration does not work, but logging in with an existing account does
* **Hydrogen**: occasionally sync can fail due to gaps in the `since` parameter, but clearing the cache fixes this
### Does Dendrite support push notifications?
## Does Dendrite support push notifications?
Yes, we have experimental support for push notifications. Configure them in the usual way in your Matrix client.
### Does Dendrite support application services/bridges?
## Does Dendrite support application services/bridges?
Possibly - Dendrite does have some application service support but it is not well tested. Please let us know by raising a GitHub issue if you try it and run into problems.
Bridges known to work (as of v0.5.1):
- [Telegram](https://docs.mau.fi/bridges/python/telegram/index.html)
- [WhatsApp](https://docs.mau.fi/bridges/go/whatsapp/index.html)
- [Signal](https://docs.mau.fi/bridges/python/signal/index.html)
- [probably all other mautrix bridges](https://docs.mau.fi/bridges/)
* [Telegram](https://docs.mau.fi/bridges/python/telegram/index.html)
* [WhatsApp](https://docs.mau.fi/bridges/go/whatsapp/index.html)
* [Signal](https://docs.mau.fi/bridges/python/signal/index.html)
* [probably all other mautrix bridges](https://docs.mau.fi/bridges/)
Remember to add the config file(s) to the `app_service_api` [config](https://github.com/matrix-org/dendrite/blob/de38be469a23813921d01bef3e14e95faab2a59e/dendrite-config.yaml#L130-L131).
### Is it possible to prevent communication with the outside world?
## Is it possible to prevent communication with the outside world?
Yes, you can do this by disabling federation - set `disable_federation` to `true` in the `global` section of the Dendrite configuration file.
Yes, you can do this by disabling federation - set `disable_federation` to `true` in the `global` section of the Dendrite configuration file.
### Should I use PostgreSQL or SQLite for my databases?
## Should I use PostgreSQL or SQLite for my databases?
Please use PostgreSQL wherever possible, especially if you are planning to run a homeserver that caters to more than a couple of users.
Please use PostgreSQL wherever possible, especially if you are planning to run a homeserver that caters to more than a couple of users.
### Dendrite is using a lot of CPU
## Dendrite is using a lot of CPU
Generally speaking, you should expect to see some CPU spikes, particularly if you are joining or participating in large rooms. However, constant/sustained high CPU usage is not expected - if you are experiencing that, please join `#dendrite-dev:matrix.org` and let us know, or file a GitHub issue.
### Dendrite is using a lot of RAM
## Dendrite is using a lot of RAM
A lot of users report that Dendrite is using a lot of RAM, sometimes even gigabytes of it. This is usually due to Go's allocator behaviour, which tries to hold onto allocated memory until the operating system wants to reclaim it for something else. This can make the memory usage look significantly inflated in tools like `top`/`htop` when actually most of that memory is not really in use at all.
@ -71,11 +77,11 @@ If you want to prevent this behaviour so that the Go runtime releases memory nor
If you are running with `GODEBUG=madvdontneed=1` and still see hugely inflated memory usage then that's quite possibly a bug - please join `#dendrite-dev:matrix.org` and let us know, or file a GitHub issue.
### Dendrite is running out of PostgreSQL database connections
## Dendrite is running out of PostgreSQL database connections
You may need to revisit the connection limit of your PostgreSQL server and/or make changes to the `max_connections` lines in your Dendrite configuration. Be aware that each Dendrite component opens its own database connections and has its own connection limit, even in monolith mode!
### What is being reported when enabling anonymous stats?
## What is being reported when enabling anonymous stats?
If anonymous stats reporting is enabled, the following data is send to the defined endpoint.
@ -116,4 +122,4 @@ If anonymous stats reporting is enabled, the following data is send to the defin
"uptime_seconds": 30,
"version": "0.8.2"
}
```
```

View file

@ -1,8 +1,13 @@
---
title: Profiling
parent: Development
---
# Profiling Dendrite
If you are running into problems with Dendrite using excessive resources (e.g. CPU or RAM) then you can use the profiler to work out what is happening.
Dendrite contains an embedded profiler called `pprof`, which is a part of the standard Go toolchain.
Dendrite contains an embedded profiler called `pprof`, which is a part of the standard Go toolchain.
## Enable the profiler
@ -16,7 +21,7 @@ If pprof has been enabled successfully, a log line at startup will show that ppr
```
WARN[2020-12-03T13:32:33.669405000Z] [/Users/neilalexander/Desktop/dendrite/internal/log.go:87] SetupPprof
Starting pprof on localhost:65432
Starting pprof on localhost:65432
```
All examples from this point forward assume `PPROFLISTEN=localhost:65432` but you may need to adjust as necessary for your setup.

View file

@ -1,71 +0,0 @@
This document details how various components communicate with each other. There are two kinds of components:
- Public-facing: exposes CS/SS API endpoints and need to be routed to via client-api-proxy or equivalent.
- Internal-only: exposes internal APIs and produces Kafka events.
## Internal HTTP APIs
Not everything can be done using Kafka logs. For example, requesting the latest events in a room is much better suited to
a request/response model like HTTP or RPC. Therefore, components can expose "internal APIs" which sit outside of Kafka logs.
Note in Monolith mode these are actually direct function calls and are not serialised HTTP requests.
```
Tier 1 Sync FederationAPI ClientAPI MediaAPI
Public Facing | | | | | | | | | |
2 .-------3-----------------` | | | `--------|-|-|-|--11--------------------.
| | .--------4----------------------------------` | | | |
| | | .---5-----------` | | | | | |
| | | | .---6----------------------------` | | |
| | | | | | .-----7----------` | |
| | | | | 8 | | 10 |
| | | | | | | `---9----. | |
V V V V V V V V V V
Tier 2 Roomserver EDUServer FedSender AppService KeyServer ServerKeyAPI
Internal only | `------------------------12----------^ ^
`------------------------------------------------------------13----------`
Client ---> Server
```
- 2 (Sync -> Roomserver): When making backfill requests
- 3 (FedAPI -> Roomserver): Calculating (prev/auth events) and sending new events, processing backfill/state/state_ids requests
- 4 (ClientAPI -> Roomserver): Calculating (prev/auth events) and sending new events, processing /state requests
- 5 (FedAPI -> EDUServer): Sending typing/send-to-device events
- 6 (ClientAPI -> EDUServer): Sending typing/send-to-device events
- 7 (ClientAPI -> FedSender): Handling directory lookups
- 8 (FedAPI -> FedSender): Resetting backoffs when receiving traffic from a server. Querying joined hosts when handling alias lookup requests
- 9 (FedAPI -> AppService): Working out if the client is an appservice user
- 10 (ClientAPI -> AppService): Working out if the client is an appservice user
- 11 (FedAPI -> ServerKeyAPI): Verifying incoming event signatures
- 12 (FedSender -> ServerKeyAPI): Verifying event signatures of responses (e.g from send_join)
- 13 (Roomserver -> ServerKeyAPI): Verifying event signatures of backfilled events
In addition to this, all public facing components (Tier 1) talk to the `UserAPI` to verify access tokens and extract profile information where needed.
## Kafka logs
```
.----1--------------------------------------------.
V |
Tier 1 Sync FederationAPI ClientAPI MediaAPI
Public Facing ^ ^ ^
| | |
2 | |
| `-3------------. |
| | |
| | |
| | |
| .--------4-----|------------------------------`
| | |
Tier 2 Roomserver EDUServer FedSender AppService KeyServer ServerKeyAPI
Internal only | | ^ ^
| `-----5----------` |
`--------------------6--------`
Producer ----> Consumer
```
- 1 (ClientAPI -> Sync): For tracking account data
- 2 (Roomserver -> Sync): For all data to send to clients
- 3 (EDUServer -> Sync): For typing/send-to-device data to send to clients
- 4 (Roomserver -> ClientAPI): For tracking memberships for profile updates.
- 5 (EDUServer -> FedSender): For sending EDUs over federation
- 6 (Roomserver -> FedSender): For sending PDUs over federation, for tracking joined hosts.

View file

@ -1,229 +0,0 @@
# Wiring
The diagram is incomplete. The following things aren't shown on the diagram:
* Device Messages
* User Profiles
* Notification Counts
* Sending federation.
* Querying federation.
* Other things that aren't shown on the diagram.
Diagram:
W -> Writer
S -> Server/Store/Service/Something/Stuff
R -> Reader
+---+ +---+ +---+
+----------| W | +----------| S | +--------| R |
| +---+ | Receipts +---+ | Client +---+
| Federation |>=========================================>| Server |>=====================>| Sync |
| Receiver | | | | |
| | +---+ | | | |
| | +--------| W | | | | |
| | | Client +---+ | | | |
| | | Receipt |>=====>| | | |
| | | Updater | | | | |
| | +----------+ | | | |
| | | | | |
| | +---+ +---+ | | +---+ | |
| | +------------| W | +------| S | | | +--------| R | | |
| | | Federation +---+ | Room +---+ | | | Client +---+ | |
| | | Backfill |>=====>| Server |>=====>| |>=====>| Push | | |
| | +--------------+ | | +------------+ | | | |
| | | | | | | |
| | | |>==========================>| | | |
| | | | +----------+ | |
| | | | +---+ | |
| | | | +-------------| R | | |
| | | |>=====>| Application +---+ | |
| | | | | Services | | |
| | | | +--------------+ | |
| | | | +---+ | |
| | | | +--------| R | | |
| | | | | Client +---+ | |
| |>========================>| |>==========================>| Search | | |
| | | | | | | |
| | | | +----------+ | |
| | | | | |
| | | |>==========================================>| |
| | | | | |
| | +---+ | | +---+ | |
| | +--------| W | | | +----------| S | | |
| | | Client +---+ | | | Presence +---+ | |
| | | API |>=====>| |>=====>| Server |>=====================>| |
| | | /send | +--------+ | | | |
| | | | | | | |
| | | |>======================>| |<=====================<| |
| | +----------+ | | | |
| | | | | |
| | +---+ | | | |
| | +--------| W | | | | |
| | | Client +---+ | | | |
| | | Presence |>=====>| | | |
| | | Setter | | | | |
| | +----------+ | | | |
| | | | | |
| | | | | |
| |>=========================================>| | | |
| | +------------+ | |
| | | |
| | +---+ | |
| | +----------| S | | |
| | | EDU +---+ | |
| |>=========================================>| Server |>=====================>| |
+------------+ | | +----------+
+---+ | |
+--------| W | | |
| Client +---+ | |
| Typing |>=====>| |
| Setter | | |
+----------+ +------------+
# Component Descriptions
Many of the components are logical rather than physical. For example it is
possible that all of the client API writers will end up being glued together
and always deployed as a single unit.
Outbound federation requests will probably need to be funnelled through a
choke-point to implement ratelimiting and backoff correctly.
## Federation Send
* Handles `/federation/v1/send/` requests.
* Fetches missing ``prev_events`` from the remote server if needed.
* Fetches missing room state from the remote server if needed.
* Checks signatures on remote events, downloading keys if needed.
* Queries information needed to process events from the Room Server.
* Writes room events to logs.
* Writes presence updates to logs.
* Writes receipt updates to logs.
* Writes typing updates to logs.
* Writes other updates to logs.
## Client API /send
* Handles puts to `/client/v1/rooms/` that create room events.
* Queries information needed to process events from the Room Server.
* Talks to remote servers if needed for joins and invites.
* Writes room event pdus.
* Writes presence updates to logs.
## Client Presence Setter
* Handles puts to the [client API presence paths](https://matrix.org/docs/spec/client_server/unstable.html#id41).
* Writes presence updates to logs.
## Client Typing Setter
* Handles puts to the [client API typing paths](https://matrix.org/docs/spec/client_server/unstable.html#id32).
* Writes typing updates to logs.
## Client Receipt Updater
* Handles puts to the [client API receipt paths](https://matrix.org/docs/spec/client_server/unstable.html#id36).
* Writes receipt updates to logs.
## Federation Backfill
* Backfills events from other servers
* Writes the resulting room events to logs.
* Is a different component from the room server itself cause it'll
be easier if the room server component isn't making outbound HTTP requests
to remote servers
## Room Server
* Reads new and backfilled room events from the logs written by FS, FB and CRS.
* Tracks the current state of the room and the state at each event.
* Probably does auth checks on the incoming events.
* Handles state resolution as part of working out the current state and the
state at each event.
* Writes updates to the current state and new events to logs.
* Shards by room ID.
## Receipt Server
* Reads new updates to receipts from the logs written by the FS and CRU.
* Somehow learns enough information from the room server to workout how the
current receipt markers move with each update.
* Writes the new marker positions to logs
* Shards by room ID?
* It may be impossible to implement without folding it into the Room Server
forever coupling the components together.
## EDU Server
* Reads new updates to typing from the logs written by the FS and CTS.
* Updates the current list of people typing in a room.
* Writes the current list of people typing in a room to the logs.
* Shards by room ID?
## Presence Server
* Reads the current state of the rooms from the logs to track the intersection
of room membership between users.
* Reads updates to presence from the logs written by the FS and the CPS.
* Reads when clients sync from the logs from the Client Sync.
* Tracks any timers for users.
* Writes the changes to presence state to the logs.
* Shards by user ID somehow?
## Client Sync
* Handle /client/v2/sync requests.
* Reads new events and the current state of the rooms from logs written by the Room Server.
* Reads new receipts positions from the logs written by the Receipts Server.
* Reads changes to presence from the logs written by the Presence Server.
* Reads changes to typing from the logs written by the EDU Server.
* Writes when a client starts and stops syncing to the logs.
## Client Search
* Handle whatever the client API path for event search is?
* Reads new events and the current state of the rooms from logs writeen by the Room Server.
* Maintains a full text search index of somekind.
## Client Push
* Pushes unread messages to remote push servers.
* Reads new events and the current state of the rooms from logs writeen by the Room Server.
* Reads the position of the read marker from the Receipts Server.
* Makes outbound HTTP hits to the push server for the client device.
## Application Service
* Receives events from the Room Server.
* Filters events and sends them to each registered application service.
* Runs a separate goroutine for each application service.
# Internal Component API
Some dendrite components use internal APIs to communicate information back
and forth between each other. There are two implementations of each API, one
that uses HTTP requests and one that does not. The HTTP implementation is
used in multi-process mode, so processes on separate computers may still
communicate, whereas in single-process or Monolith mode, the direct
implementation is used. HTTP is preferred here to kafka streams as it allows
for request responses.
Running `dendrite-monolith-server` will set up direct connections between
components, whereas running each individual component (which are only run in
multi-process mode) will set up HTTP-based connections.
The functions that make HTTP requests to internal APIs of a component are
located in `/<component name>/api/<name>.go`, named according to what
functionality they cover. Each of these requests are handled in `/<component
name>/<name>/<name>.go`.
As an example, the `appservices` component allows other Dendrite components
to query external application services via its internal API. A component
would call the desired function in `/appservices/api/query.go`. In
multi-process mode, this would send an internal HTTP request, which would
be handled by a function in `/appservices/query/query.go`. In single-process
mode, no internal HTTP request occurs, instead functions are simply called
directly, thus requiring no changes on the calling component's end.

View file

@ -0,0 +1,29 @@
# Server Key Format
Dendrite stores the server signing key in the PEM format with the following structure.
```
-----BEGIN MATRIX PRIVATE KEY-----
Key-ID: ed25519:<Key Handle>
<Base64 Encoded Key Data>
-----END MATRIX PRIVATE KEY-----
```
## Converting Synapse Keys
If you have signing keys from a previous synapse server, you should ideally configure them as `old_private_keys` in your Dendrite config file. Synapse stores signing keys in the following format.
```
ed25519 <Key Handle> <Base64 Encoded Key Data>
```
To convert this key to Dendrite's PEM format, use the following template. **It is important to include the equals sign, as the key data needs to be padded to 32 bytes.**
```
-----BEGIN MATRIX PRIVATE KEY-----
Key-ID: ed25519:<Key Handle>
<Base64 Encoded Key Data>=
-----END MATRIX PRIVATE KEY-----
```

View file

@ -1,3 +1,8 @@
---
title: Code Style
parent: Development
---
# Code Style
In addition to standard Go code style (`gofmt`, `goimports`), we use `golangci-lint`
@ -16,8 +21,7 @@ The linters can be run using [build/scripts/find-lint.sh](/build/scripts/find-li
(see file for docs) or as part of a build/test/lint cycle using
[build/scripts/build-test-lint.sh](/build/scripts/build-test-lint.sh).
## Labels
##  Labels
In addition to `TODO` and `FIXME` we also use `NOTSPEC` to identify deviations
from the Matrix specification.
@ -44,7 +48,6 @@ This is useful when logging to systems that natively understand log fields, as
it allows people to search and process the fields without having to parse the
log message.
## Visual Studio Code
If you use VSCode then the following is an example of a workspace setting that

8
docs/development.md Normal file
View file

@ -0,0 +1,8 @@
---
title: Development
has_children: true
---
# Development
The documents in this section will be useful when developing against Dendrite.

View file

@ -3,3 +3,5 @@ layout: home
---
Dendrite is a second-generation Matrix homeserver written in Go!
This documentation is a work in progress.

View file

@ -1,6 +1,7 @@
---
title: Installation
has_children: true
nav_order: 2
---
# Installing Dendrite
@ -21,13 +22,6 @@ Dendrite can be run in one of two configurations:
In almost all cases, it is **recommended to run in monolith mode with PostgreSQL databases**.
Regardless of whether you are running in polylith or monolith mode, each Dendrite component that
requires storage has its own database connections. Both Postgres and SQLite are supported and can
be mixed-and-matched across components as needed in the configuration file.
Be advised that Dendrite is still in development and it's not recommended for
use in production environments just yet!
## Requirements
Dendrite requires:

View file

@ -1,6 +1,61 @@
---
title: Monolith
parent: Installation
has_toc: true
---
# Monolith installation
# Monolith Installation
## Requirements
In order to build a polylith deployment, you will need to install:
* Go 1.16 or later
* PostgreSQL 12 or later
## Build Dendrite
On UNIX systems, the `build.sh` script will build all variants of Dendrite.
```bash
./build.sh
```
The `bin` directory will contain the built binaries.
## PostgreSQL setup
First of all, you will need to create a PostgreSQL user that Dendrite can use
to connect to the database.
```bash
sudo -u postgres createuser -P dendrite
```
At this point you have a choice on whether to run all of the Dendrite
components from a single database, or for each component to have its
own database. For most deployments, running from a single database will
be sufficient, although you may wish to separate them if you plan to
split out the databases across multiple machines in the future.
On macOS, omit `sudo -u postgres` from the below commands.
### Single database
```bash
sudo -u postgres createdb -O dendrite dendrite
```
... in which case your connection string will look like `postgres://user:pass@database/dendrite`.
### Separate databases
```bash
for i in mediaapi syncapi roomserver federationapi appservice keyserver userapi_accounts; do
sudo -u postgres createdb -O dendrite dendrite_$i
done
```
... in which case your connection string will look like `postgres://user:pass@database/dendrite_componentname`.
## Configuration file

View file

@ -1,6 +1,27 @@
---
title: Polylith
parent: Installation
has_toc: true
---
# Polylith installation
# Polylith Installation
## Requirements
In order to build a polylith deployment, you will need:
* Go 1.16 or later
* PostgreSQL 12 or later
* NATS Server 2.8.0 or later
## Build Dendrite
On UNIX systems, the `build.sh` script will build all variants of Dendrite.
```sh
./build.sh
```
## Database setup
## Configuration file

View file

@ -1,27 +1,34 @@
## Peer-to-peer Matrix
---
title: P2P Matrix
nav_exclude: true
---
# P2P Matrix
These are the instructions for setting up P2P Dendrite, current as of May 2020. There's both Go stuff and JS stuff to do to set this up.
### Dendrite
## Dendrite
#### Build
### Build
- The `main` branch has a WASM-only binary for dendrite: `./cmd/dendritejs`.
- Build it and copy assets to riot-web.
```
$ ./build-dendritejs.sh
$ cp bin/main.wasm ../riot-web/src/vector/dendrite.wasm
./build-dendritejs.sh
cp bin/main.wasm ../riot-web/src/vector/dendrite.wasm
```
#### Test
### Test
To check that the Dendrite side is working well as Wasm, you can run the
Wasm-specific tests:
```
$ ./test-dendritejs.sh
./test-dendritejs.sh
```
### Rendezvous
## Rendezvous
This is how peers discover each other and communicate.
@ -29,18 +36,18 @@ By default, Dendrite uses the Matrix-hosted websocket star relay server at TODO
This is currently hard-coded in `./cmd/dendritejs/main.go` - you can also use a local one if you run your own relay:
```
$ npm install --global libp2p-websocket-star-rendezvous
$ rendezvous --port=9090 --host=127.0.0.1
npm install --global libp2p-websocket-star-rendezvous
rendezvous --port=9090 --host=127.0.0.1
```
Then use `/ip4/127.0.0.1/tcp/9090/ws/p2p-websocket-star/`.
### Riot-web
## Riot-web
You need to check out this repo:
```
$ git clone git@github.com:matrix-org/go-http-js-libp2p.git
git clone git@github.com:matrix-org/go-http-js-libp2p.git
```
Make sure to `yarn install` in the repo. Then:
@ -53,26 +60,30 @@ if (!global.fs && global.require) {
global.fs = require("fs");
}
```
- Add the diff at https://github.com/vector-im/riot-web/compare/matthew/p2p?expand=1 - ignore the `package.json` stuff.
- Add the diff at <https://github.com/vector-im/riot-web/compare/matthew/p2p?expand=1> - ignore the `package.json` stuff.
- Add the following symlinks: they HAVE to be symlinks as the diff in `webpack.config.js` references specific paths.
```
$ cd node_modules
$ ln -s ../../go-http-js-libp2p
cd node_modules
ln -s ../../go-http-js-libp2p
```
NB: If you don't run the server with `yarn start` you need to make sure your server is sending the header `Service-Worker-Allowed: /`.
TODO: Make a Docker image with all of this in it and a volume mount for `dendrite.wasm`.
### Running
## Running
You need a Chrome and a Firefox running to test locally as service workers don't work in incognito tabs.
- For Chrome, use `chrome://serviceworker-internals/` to unregister/see logs.
- For Firefox, use `about:debugging#/runtime/this-firefox` to unregister. Use the console window to see logs.
Assuming you've `yarn start`ed Riot-Web, go to `http://localhost:8080` and register with `http://localhost:8080` as your HS URL.
You can:
- join rooms by room alias e.g `/join #foo:bar`.
- invite specific users to a room.
- explore the published room list. All members of the room can re-publish aliases (unlike Synapse).
- join rooms by room alias e.g `/join #foo:bar`.
- invite specific users to a room.
- explore the published room list. All members of the room can re-publish aliases (unlike Synapse).

View file

@ -1,26 +1,33 @@
---
nav_exclude: true
---
## Peeking
Local peeking is implemented as per [MSC2753](https://github.com/matrix-org/matrix-doc/pull/2753).
Implementationwise, this means:
* Users call `/peek` and `/unpeek` on the clientapi from a given device.
* The clientapi delegates these via HTTP to the roomserver, which coordinates peeking in general for a given room
* The roomserver writes an NewPeek event into the kafka log headed to the syncserver
* The syncserver tracks the existence of the local peek in the syncapi_peeks table in its DB, and then starts waking up the peeking devices for the room in question, putting it in the `peek` section of the /sync response.
* Users call `/peek` and `/unpeek` on the clientapi from a given device.
* The clientapi delegates these via HTTP to the roomserver, which coordinates peeking in general for a given room
* The roomserver writes an NewPeek event into the kafka log headed to the syncserver
* The syncserver tracks the existence of the local peek in the syncapi_peeks table in its DB, and then starts waking up the peeking devices for the room in question, putting it in the `peek` section of the /sync response.
Peeking over federation is implemented as per [MSC2444](https://github.com/matrix-org/matrix-doc/pull/2444).
For requests to peek our rooms ("inbound peeks"):
* Remote servers call `/peek` on federationapi
* The federationapi queries the federationsender to check if this is renewing an inbound peek or not.
* If not, it hits the PerformInboundPeek on the roomserver to ask it for the current state of the room.
* The roomserver atomically (in theory) adds a NewInboundPeek to its kafka stream to tell the federationserver to start peeking.
* The federationsender receives the event, tracks the inbound peek in the federationsender_inbound_peeks table, and starts sending events to the peeking server.
* The federationsender evicts stale inbound peeks which haven't been renewed.
* Remote servers call `/peek` on federationapi
* The federationapi queries the federationsender to check if this is renewing an inbound peek or not.
* If not, it hits the PerformInboundPeek on the roomserver to ask it for the current state of the room.
* The roomserver atomically (in theory) adds a NewInboundPeek to its kafka stream to tell the federationserver to start peeking.
* The federationsender receives the event, tracks the inbound peek in the federationsender_inbound_peeks table, and starts sending events to the peeking server.
* The federationsender evicts stale inbound peeks which haven't been renewed.
For peeking into other server's rooms ("outbound peeks"):
* The `roomserver` will kick the `federationsender` much as it does for a federated `/join` in order to trigger a federated outbound `/peek`
* The `federationsender` tracks the existence of the outbound peek in in its federationsender_outbound_peeks table.
* The `federationsender` regularly renews the remote peek as long as there are still peeking devices syncing for it.
* TBD: how do we tell if there are no devices currently syncing for a given peeked room? The syncserver needs to tell the roomserver
somehow who then needs to warn the federationsender.
* The `roomserver` will kick the `federationsender` much as it does for a federated `/join` in order to trigger a federated outbound `/peek`
* The `federationsender` tracks the existence of the outbound peek in in its federationsender_outbound_peeks table.
* The `federationsender` regularly renews the remote peek as long as there are still peeking devices syncing for it.
* TBD: how do we tell if there are no devices currently syncing for a given peeked room? The syncserver needs to tell the roomserver
somehow who then needs to warn the federationsender.

View file

@ -1,3 +1,8 @@
---
title: SyTest
parent: Development
---
# SyTest
Dendrite uses [SyTest](https://github.com/matrix-org/sytest) for its
@ -43,6 +48,7 @@ source code. The test results TAP file and homeserver logging output will go to
add any tests to `sytest-whitelist`.
When debugging, the following Docker `run` options may also be useful:
* `-v /path/to/sytest/:/sytest/`: Use your local SyTest repository at
`/path/to/sytest` instead of pulling from GitHub. This is useful when you want
to speed things up or make modifications to SyTest.
@ -58,6 +64,7 @@ When debugging, the following Docker `run` options may also be useful:
The docker command also supports a single positional argument for the test file to
run, so you can run a single `.pl` file rather than the whole test suite. For example:
```
docker run --rm --name sytest -v "/Users/kegan/github/sytest:/sytest"
-v "/Users/kegan/github/dendrite:/src" -v "/Users/kegan/logs:/logs"
@ -118,7 +125,7 @@ POSTGRES=1 ./run-tests.pl -I Dendrite::Monolith -d ../dendrite/bin -W ../dendrit
where `tee` lets you see the results while they're being piped to the file, and
`POSTGRES=1` enables testing with PostgeSQL. If the `POSTGRES` environment
variable is not set or is set to 0, SyTest will fall back to SQLite 3. For more
flags and options, see https://github.com/matrix-org/sytest#running.
flags and options, see <https://github.com/matrix-org/sytest#running>.
Once the tests are complete, run the helper script to see if you need to add
any newly passing test names to `sytest-whitelist` in the project's root

View file

@ -1,3 +1,9 @@
---
title: OpenTracing
has_children: true
parent: Development
---
Opentracing
===========
@ -23,7 +29,6 @@ This is useful to see where the time is being spent processing a request on a
component. However, opentracing allows tracking of spans across components. This
makes it possible to see exactly what work goes into processing a request:
```
Component 1 |<─────────────────── HTTP ────────────────────>|
|<──────────────── RPC ─────────────────>|
@ -39,7 +44,6 @@ deserialized span as the parent).
A collection of spans that are related is called a trace.
Spans are passed through the code via contexts, rather than manually. It is
therefore important that all spans that are created are immediately added to the
current context. Thankfully the opentracing library gives helper functions for
@ -53,11 +57,11 @@ defer span.Finish()
This will create a new span, adding any span already in `ctx` as a parent to the
new span.
Adding Information
------------------
Opentracing allows adding information to a trace via three mechanisms:
- "tags" ─ A span can be tagged with a key/value pair. This is typically
information that relates to the span, e.g. for spans created for incoming HTTP
requests could include the request path and response codes as tags, spans for
@ -69,12 +73,10 @@ Opentracing allows adding information to a trace via three mechanisms:
inspecting the traces, but can be used to add context to logs or tags in child
spans.
See
[specification.md](https://github.com/opentracing/specification/blob/master/specification.md)
for some of the common tags and log fields used.
Span Relationships
------------------
@ -86,7 +88,6 @@ A second relation type is `followsFrom`, where the parent has no dependence on
the child span. This usually indicates some sort of fire and forget behaviour,
e.g. adding a message to a pipeline or inserting into a kafka topic.
Jaeger
------
@ -99,6 +100,7 @@ giving a UI for viewing and interacting with traces.
To enable jaeger a `Tracer` object must be instansiated from the config (as well
as having a jaeger server running somewhere, usually locally). A `Tracer` does
several things:
- Decides which traces to save and send to the server. There are multiple
schemes for doing this, with a simple example being to save a certain fraction
of traces.

View file

@ -1,3 +1,8 @@
---
title: Setup
parent: Opentracing
---
## OpenTracing Setup
![Trace when sending an event into a room](/docs/tracing/jaeger.png)
@ -9,6 +14,7 @@ This document explains how to set up Jaeger locally on a single machine.
### Set up the Jaeger backend
The [easiest way](https://www.jaegertracing.io/docs/1.18/getting-started/) is to use the all-in-one Docker image:
```
$ docker run -d --name jaeger \
-e COLLECTOR_ZIPKIN_HTTP_PORT=9411 \
@ -26,6 +32,7 @@ $ docker run -d --name jaeger \
### Configuring Dendrite to talk to Jaeger
Modify your config to look like: (this will send every single span to Jaeger which will be slow on large instances, but for local testing it's fine)
```
tracing:
enabled: true
@ -40,10 +47,11 @@ tracing:
```
then run the monolith server with `--api true` to use polylith components which do tracing spans:
```
$ ./dendrite-monolith-server --tls-cert server.crt --tls-key server.key --config dendrite.yaml --api true
./dendrite-monolith-server --tls-cert server.crt --tls-key server.key --config dendrite.yaml --api true
```
### Checking traces
Visit http://localhost:16686 to see traces under `DendriteMonolith`.
Visit <http://localhost:16686> to see traces under `DendriteMonolith`.