Phase 2: the event core
CI / Test (ubuntu-latest) (push) Canceled after 0s
CI / Test (windows-latest) (push) Canceled after 0s
CI / Storage tests on PostgreSQL (push) Canceled after 0s
CI / Complement (federation off) (push) Canceled after 0s
CI / golangci-lint (push) Canceled after 0s
CI / Smoke test the binary (push) Canceled after 0s
CI / Test (ubuntu-latest) (push) Canceled after 0s
CI / Test (windows-latest) (push) Canceled after 0s
CI / Storage tests on PostgreSQL (push) Canceled after 0s
CI / Complement (federation off) (push) Canceled after 0s
CI / golangci-lint (push) Canceled after 0s
CI / Smoke test the binary (push) Canceled after 0s
Implements PLAN.md Phase 2: the library every later phase is built on, as pure code under server/internal/matrix with no HTTP and no storage. canonicaljson: the canonical JSON encoding, hand-written because encoding/json escapes <, > and & and accepts floats. Integers only, in [-(2^53)+1, 2^53-1]; lone surrogate escapes rejected rather than turned into U+FFFD. signing: the Signing JSON algorithm alongside the existing key handling, and one lenient base64 decoder shared by the packages above it. id: identifier grammars as they appear inside events, deliberately laxer than userapi's account rules because rooms carry historical user IDs. roomversion: the registry. Versions 3 to 12 are supported, each written out in full as a Rules value with the flags the other packages read. Versions 1 and 2 are known but refused: their event format and state resolution v1 wait for federation. createRoom will default to 11, as the spec asks for a while after 1.16, with 12 available. event: PDU parsing and validation, content and reference hashes, event IDs, the redaction algorithm for every supported version, signing and verification, and a Builder. auth: the authorisation rules in spec order, power levels with their defaults, v12 creators with infinite power, the auth events selection algorithm, rule 3's auth_events checks, and the separate test for whether a redaction applies. Comments use the room version 12 rule numbering, with the version 11 numbers given where they differ. stateres: state resolution v2, and version 12's amendment (an empty starting state and the conflicted state subgraph). Tested against the spec's own vectors byte for byte: the canonical JSON examples, both JSON signing vectors and both event signing vectors. State resolution ports scenarios from Synapse's suite. canonicaljson and event.Parse have fuzz targets. The capabilities endpoint now advertises the registry instead of a hard-coded "11". docs/event-core.md describes the packages and the known gaps. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
6004bb01de
commit
2fbd5f53ce
30 files changed
+8928
-9
No files matched your search
@@ -0,0 +1,111 @@
|
||||
# The event core
|
||||
|
||||
`server/internal/matrix` is the heart of the homeserver: the operations
|
||||
whose results every server in a room must agree on, byte for byte. It is
|
||||
pure library code — no HTTP, no database, no clock — and that is deliberate.
|
||||
Everything here is deterministic and therefore testable against the spec's
|
||||
own vectors, which is the only way to be confident a room's history will
|
||||
still verify on somebody else's server in five years.
|
||||
|
||||
It corresponds to [PLAN.md](../PLAN.md) Phase 2.
|
||||
|
||||
## The packages
|
||||
|
||||
| Package | What |
|
||||
|---|---|
|
||||
| `canonicaljson` | the canonical JSON encoding: sorted keys, no whitespace, integers only, minimal escaping |
|
||||
| `signing` | ed25519 server keys (Phase 0) plus the Signing JSON algorithm |
|
||||
| `id` | the identifier grammars as they appear *inside events*, which are laxer than the ones for new accounts |
|
||||
| `roomversion` | the registry of room versions and of what each one changes |
|
||||
| `event` | the PDU model: parsing, validation, content and reference hashes, event IDs, redaction, signing |
|
||||
| `auth` | the authorisation rules, power levels, and the auth events selection algorithm |
|
||||
| `stateres` | state resolution v2, and version 12's amendment to it |
|
||||
|
||||
They stack in that order. `canonicaljson` depends on nothing, and
|
||||
`stateres` depends on everything.
|
||||
|
||||
## Reading the code against the spec
|
||||
|
||||
The authorisation rules in `auth` are written in the order and the shape
|
||||
the spec gives them, and the comments name the rule numbers from the
|
||||
[room version 11](https://spec.matrix.org/latest/rooms/v11/) and
|
||||
[version 12](https://spec.matrix.org/latest/rooms/v12/) pages, so the two
|
||||
can be read side by side. That is worth more here than concision: a rule
|
||||
that is subtly wrong does not fail a test, it forks a room.
|
||||
|
||||
Where the room versions differ, the difference is a field on
|
||||
`roomversion.Rules` rather than a comparison on the version string. Adding
|
||||
a room version should mean adding one entry to the table in
|
||||
`roomversion.go` and nothing else.
|
||||
|
||||
## What the tests check
|
||||
|
||||
The spec publishes test vectors, and they are the backbone of the suite:
|
||||
|
||||
- `canonicaljson` runs the worked examples from the appendices.
|
||||
- `signing` runs the two JSON signing vectors, byte for byte.
|
||||
- `event` runs the two event signing vectors, byte for byte — the content
|
||||
hash, the redaction, and the signature over the redacted form.
|
||||
- `stateres` ports several scenarios from Synapse's state resolution
|
||||
tests, including the topic reset that the algorithm is known to produce.
|
||||
|
||||
Beyond the vectors, the suite leans on properties rather than examples
|
||||
where it can: canonical JSON is idempotent, an event's ID survives
|
||||
redaction, a signature survives a rewritten `unsigned`, and state
|
||||
resolution does not depend on the order the state sets arrive in. Both
|
||||
`canonicaljson.Encode` and `event.Parse` have fuzz targets, since both
|
||||
read bytes straight off the network.
|
||||
|
||||
```sh
|
||||
cd server
|
||||
go test ./internal/matrix/...
|
||||
go test ./internal/matrix/canonicaljson -fuzz FuzzEncode -fuzztime 60s
|
||||
go test ./internal/matrix/event -fuzz FuzzParse -fuzztime 60s
|
||||
```
|
||||
|
||||
## Room versions
|
||||
|
||||
Versions 3 to 12 are implemented. `createRoom` will default to 11:
|
||||
the spec says servers SHOULD default to 12 but asks them to stay on 11 for
|
||||
a while after the Matrix 1.16 release so that clients and bridges catch up,
|
||||
and we follow that advice.
|
||||
|
||||
Versions 1 and 2 are in the registry but marked unsupported, so
|
||||
`roomversion.Get` refuses them with a clear error. They carry the event ID
|
||||
inside the event, write `prev_events` and `auth_events` as `[id, hashes]`
|
||||
tuples rather than plain lists, and version 1 uses state resolution v1 —
|
||||
enough of a separate event format to be its own piece of work. They are
|
||||
only needed to join rooms created before 2018 on other servers, so they
|
||||
wait for federation in Phase 5.
|
||||
|
||||
## Known gaps
|
||||
|
||||
- **Out-of-range integers in old rooms.** Room versions 1 to 5 may contain
|
||||
integers outside the range canonical JSON permits, and the spec asks
|
||||
servers to cope where they can. Warp rejects such an event whatever its
|
||||
room version. Handling it means a second, lenient encoder; it can wait
|
||||
until federation makes those rooms reachable.
|
||||
- **Signature checking during state resolution.** The restricted join rule
|
||||
asks for a signature from the server authorising the join.
|
||||
`stateres` does not re-check it, on the grounds that the event was
|
||||
checked when it was accepted into the room, and that state resolution
|
||||
must not do network I/O. Synapse draws the line in the same place. The
|
||||
consequence is that event intake must never be skipped — see
|
||||
`intakeVerified` in `stateres.go`.
|
||||
- **State resolution v1** is not implemented, for the same reason room
|
||||
versions 1 and 2 are not.
|
||||
|
||||
## What is not here
|
||||
|
||||
Two decisions that look like they belong to the authorisation rules do not:
|
||||
|
||||
- **Whether a redaction is applied.** The rules allow nearly every
|
||||
`m.room.redaction` event; whether the redaction takes effect is
|
||||
`auth.ShouldApplyRedaction`, which the roomserver runs once it holds both
|
||||
events.
|
||||
- **Soft failure.** An event can pass the rules against its own auth
|
||||
events and still be denied a place among the room's forward extremities
|
||||
because it fails against the *current* state. That is the roomserver's
|
||||
business, in Phase 3.
|
||||
|
||||
Neither needs the event core, and both need storage.
|
||||
Reference in new issue
Block a user