Implements PLAN.md Phase 7: the server half of end-to-end encryption.
Devices upload identity, one-time and fallback keys; other devices query
and claim them, across servers too, and every one-time key is handed out
exactly once. Cross-signing, signature uploads, device list tracking and
server-side key backup complete the set.
internal/keyserver is the component. Keys are stored in canonical JSON
so that re-uploading the same key is a byte comparison, and signatures
uploaded later are kept apart from the key they sign and merged in on
read, so a key's own JSON stays what its owner uploaded. Device keys must
be for the caller's own device; cross-signing keys must be the
uploader's, carry the right usage and, for self- and user-signing keys,
be signed by the master key. Signatures are verified against the key as
stored. Replacing a master key asks for the password; a first upload or
an upload of the keys already stored does not, as the spec allows. A
signature of another user's master key is shown only to the user who
made it and never federated.
Every write that changes what /keys/query answers moves the device list
stream in the same transaction, one position per device, and wakes the
syncs of everyone sharing a room. The stream is the sixth field of a sync
token; older tokens still parse. The syncapi works out device_lists
changed and left from the stream and from membership moves in the
window, skipping invites and knocks, and reports
device_one_time_keys_count and device_unused_fallback_key_types, the
latter as [] when empty. GET /keys/changes uses the same logic.
Over federation: /user/keys/query, /user/keys/claim and
/user/devices/{userId}, and m.device_list_update and m.signing_key_update
both ways. Remote keys are not cached; an update about a remote user
sharing a room moves them in the stream so local clients re-query.
Device display names are not sent to other servers. With federation off,
remote users are reported in failures at once.
Deleting a device deletes its keys, through a new userapi
DeviceObserver. Key backup keeps the better copy of each session by the
spec's rule, never reuses a deleted version's number, and answers writes
to a stale version with M_WRONG_ROOM_KEYS_VERSION.
Storage is migration 0008 for sqlite and postgres, with a PocketBase
stub. The Complement target sets gain the key, device list, fallback,
cross-signing and backup tests.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
83 lines
3.4 KiB
Markdown
83 lines
3.4 KiB
Markdown
# Complement
|
|
|
|
[Complement](https://github.com/matrix-org/complement) is the official
|
|
Matrix integration suite and this project's primary progress metric
|
|
(PLAN.md section 9). It runs one container per homeserver per test and
|
|
drives it over the real HTTP API.
|
|
|
|
```sh
|
|
make complement # the C-S set, federation off
|
|
make complement-federation # the federation set
|
|
bash run.sh TestLogin # one test
|
|
COMPLEMENT_DEBUG=1 make complement # with the container logs
|
|
```
|
|
|
|
Needs Docker. [run.sh](run.sh) builds the image, clones Complement into
|
|
`.complement/` at the repository root (git-ignored) and runs the subset for
|
|
the phases that are done.
|
|
|
|
## Files
|
|
|
|
| | |
|
|
|---|---|
|
|
| [Dockerfile](Dockerfile) | the homeserver image, built from the repository root |
|
|
| [entrypoint.sh](entrypoint.sh) | the deployment, entirely `WARP_*` environment variables |
|
|
| [run.sh](run.sh) | build, clone, run |
|
|
|
|
## Profiles
|
|
|
|
`COMPLEMENT_PROFILE` picks one; each has its own test set in `run.sh`.
|
|
|
|
**`csapi`** (the default, `make complement`) — the Client-Server tests in
|
|
`tests/csapi`, with `WARP_FEDERATION_ENABLED=false`. Port 8448 is still
|
|
served over TLS, because the CA is mounted either way, but the gate answers
|
|
every federation request with `M_UNRECOGNIZED`.
|
|
|
|
**`federation`** (`make complement-federation`) — the federation tests in
|
|
`tests/` and the multi-server tests in `tests/csapi`, with federation on.
|
|
The entrypoint has Complement's CA at `/complement/ca` sign a certificate
|
|
for the server's name, serves the API on 8448 with it, and points
|
|
`federation.tls_ca_file` at the same CA, so that Warp trusts the other
|
|
homeservers of the run and the servers Complement itself pretends to be.
|
|
The switch reaches the container through `COMPLEMENT_SHARE_ENV_PREFIX`:
|
|
`run.sh` sets `PASS_WARP_FEDERATION_ENABLED`, which Complement passes on
|
|
as `WARP_FEDERATION_ENABLED`.
|
|
|
|
The federation set leaves out partial-state joins (MSC3706,
|
|
`TestPartialStateJoin*`), which Warp does not implement, and anything that
|
|
needs application services (Phase 9).
|
|
|
|
## The test set
|
|
|
|
`COMPLEMENT_TESTS` in `run.sh` lists what is expected to pass. Phase 1:
|
|
|
|
- `TestRegistration`
|
|
- `TestLogin`
|
|
- `TestDeviceManagement`
|
|
|
|
Extend it as each phase lands rather than switching to the whole suite: a
|
|
pass count that drops because a later phase is missing is noise, not
|
|
information.
|
|
|
|
Two notes on the environment, both in `entrypoint.sh`:
|
|
|
|
- **Rate limiting is off.** The whole suite comes from the one address the
|
|
test runner has, so the limiter would spend the run returning
|
|
`M_LIMIT_EXCEEDED` and tell us nothing about the server. Synapse's own
|
|
Complement image does the same.
|
|
- **Registration is on**, because the tests create their own users.
|
|
|
|
## Tests that need later phases
|
|
|
|
Some subtests of the three above reach for endpoints that are not Phase 1
|
|
work. `TestDeviceManagement` calls `/sync` after creating a session, and
|
|
`TestLogout` checks that a revoked token cannot sync. Phase 1 answers
|
|
`/sync` with an empty but well-formed response for exactly that reason —
|
|
there are no rooms yet, so an empty sync is the complete truth, and it makes
|
|
token revocation observable. Phase 4 replaces it with the real syncapi.
|
|
|
|
The shared-secret registration subtests of `TestRegistration` probe
|
|
`/_synapse/admin/v1/register` and skip themselves when it 404s, which it
|
|
does here: that endpoint is Synapse's, and `warp create-account` covers the
|
|
same need.
|