|
| 1 | +# Matrix rooms in Talk |
| 2 | + |
| 3 | +Talk can show and use rooms of a [Matrix](https://matrix.org) account as conversations. Talk's |
| 4 | +server acts as a Matrix *client* (one device per linked Nextcloud user): it syncs the rooms of the |
| 5 | +linked account into ordinary Talk conversations, so the web interface as well as the unchanged |
| 6 | +mobile and desktop apps work with them. |
| 7 | + |
| 8 | +Status: **phase 4** – everything below plus: create Matrix rooms and direct chats from Talk, |
| 9 | +join rooms by address or from the public directory (*Matrix room …* in the conversation-list |
| 10 | +menu), restore encrypted history from the key backup (verified device or recovery key), |
| 11 | +mentions of Matrix-only members, `occ talk:matrix:rekey`. Phase 3 – everything from phases 1 and 2 plus reactions, edits, deletions and |
| 12 | +threads in both directions, read receipts to Matrix, inline media with thumbnails, "Save to |
| 13 | +Nextcloud" and uploads of shared files (also into encrypted rooms). Phase 2 – everything from phase 1 (account linking, rooms read/write, replies, |
| 14 | +invitations, membership, power levels, room name/topic) plus **end-to-end encrypted rooms**: |
| 15 | +Talk is a real Matrix E2EE device (Olm/Megolm implemented in PHP, interoperable with |
| 16 | +Element/vodozemac), decrypts incoming messages when they arrive, encrypts outgoing ones and |
| 17 | +shares room keys with the members' devices, decrypts encrypted attachments, and can be |
| 18 | +verified from another client via emoji (SAS). Media upload, reactions/edits/threads and room |
| 19 | +creation follow in later phases. See `docs/matrix/SPEC.md` for the full design. |
| 20 | + |
| 21 | +## Administration |
| 22 | + |
| 23 | +1. In *Administration settings → Talk → Matrix rooms* enable the feature. |
| 24 | +2. Add the homeserver(s) people may link accounts on. Talk resolves the server name via |
| 25 | + `.well-known/matrix/client` and validates `/_matrix/client/versions`; a client API URL can be set |
| 26 | + manually. Only these servers are ever contacted. |
| 27 | +3. Optionally restrict linking to groups and tune the synchronisation settings. |
| 28 | + |
| 29 | +Synchronisation runs in the background job `MatrixSync` every time cron runs (interval 10 s, budget |
| 30 | +25 s per run, per-account locks). Real latency is bounded by how often cron runs – **run cron every |
| 31 | +minute**. When a person looks at a Matrix conversation in a client, Talk additionally syncs inline if |
| 32 | +the account's data is older than *foreground sync age* (default 5 s), so the web UI feels near-real-time. |
| 33 | + |
| 34 | +`occ` commands: |
| 35 | + |
| 36 | +``` |
| 37 | +occ talk:matrix:homeserver add example.org [--name=Label] [--base-url=https://matrix.example.org] |
| 38 | +occ talk:matrix:homeserver list|remove|test <server-name> |
| 39 | +occ talk:matrix:account list |
| 40 | +occ talk:matrix:account sync --user=<uid> [--budget=20] |
| 41 | +occ talk:matrix:account unlink --user=<uid> |
| 42 | +occ talk:matrix:status [--output=json] # exit code 2 when accounts need a re-login |
| 43 | +occ talk:matrix:account request-keys --user=<uid> # ask the other devices for missing room keys again |
| 44 | +occ talk:matrix:rekey --old-secret=<previous config.php secret> [--dry-run] # after rotating "secret" |
| 45 | +``` |
| 46 | + |
| 47 | +App config keys (`occ config:app:set spreed <key>`): `matrix_enabled`, `matrix_allowed_groups` |
| 48 | +(JSON list), `matrix_sync_interval` (10–300 s, default 30), `matrix_idle_sync_interval` (default |
| 49 | +120), `matrix_max_parallel_syncs` (4), `matrix_foreground_sync_age` (5), `matrix_history_events` |
| 50 | +(200), `matrix_history_days` (30), `matrix_max_upload` (bytes), `matrix_typing_in`, `matrix_typing_out`, |
| 51 | +`matrix_e2ee_shared_lookup`, `matrix_e2ee_verified_only`. |
| 52 | + |
| 53 | +## Linking an account |
| 54 | + |
| 55 | +In Talk → Settings → *Matrix account* a person picks a homeserver and enters their Matrix username |
| 56 | +and password. Talk performs a `m.login.password` login once, stores only the access token (encrypted |
| 57 | +with the instance secret) and device id, and registers as a device named `Nextcloud Talk (<instance>)`. |
| 58 | +Rooms are imported immediately (bounded) and then kept in sync. If the homeserver invalidates the |
| 59 | +token, the person gets a notification and can log in again; the device id is reused. |
| 60 | + |
| 61 | +Unlinking logs the device out on the homeserver, deletes the stored token and removes the person from |
| 62 | +their Matrix conversations. Conversations without any remaining linked Nextcloud user are deleted. |
| 63 | + |
| 64 | +## End-to-end encryption |
| 65 | + |
| 66 | +* On linking, Talk creates an Olm account for the new device and uploads its device keys plus |
| 67 | + one-time keys. Key material and tokens are stored encrypted with the instance secret in the |
| 68 | + `talk_matrix_*` crypto tables; **decrypted messages are stored like any other Talk message** |
| 69 | + (this is what makes search, previews and all clients work — the Nextcloud server is inside |
| 70 | + the trust boundary, see the spec). |
| 71 | +* Incoming encrypted events are decrypted during sync. When the key is missing the message is |
| 72 | + shown as "🔒 Encrypted message – waiting for the key", a key request is sent to the person's |
| 73 | + other devices and to the sender, and the placeholder is replaced in place once the key arrives. |
| 74 | +* Sending creates/rotates a Megolm session per room (rotation by age/message count/membership |
| 75 | + change) and shares it via Olm with every device of every member (unless the device is blocked, |
| 76 | + or the admin enabled *only trusted devices*). |
| 77 | +* **Verify the Talk device** in Talk → Settings → Matrix account → *Verify this device*: the |
| 78 | + request appears on the other Matrix clients (Element etc.), both sides compare seven emoji, |
| 79 | + then the other client cross-signs the Talk device and shares keys for rooms it knows. Until |
| 80 | + a device is verified, other clients may refuse to share keys with it. |
| 81 | +* Key requests from the person's *own* devices are answered only when that device is |
| 82 | + cross-signed or was verified. Forwarded keys are accepted only from such devices. |
| 83 | +* Admin policy per homeserver: *Allow encrypted rooms* off → Talk neither decrypts nor sends |
| 84 | + into encrypted rooms (placeholders only). `matrix_e2ee_shared_lookup` (default on) lets a |
| 85 | + message be decrypted with another linked Nextcloud user's copy of the key when they are in the |
| 86 | + same room; `matrix_e2ee_verified_only` restricts key sharing to cross-signed/verified devices. |
| 87 | +* **Encrypted history from before the link**: Talk → Settings → Matrix account → *Restore |
| 88 | + encrypted history*. After a verification the other client usually hands over the backup key |
| 89 | + automatically; otherwise paste the recovery key (`EsTc …`). Talk downloads the server-side key |
| 90 | + backup, imports the sessions and replaces the placeholders. |
| 91 | +* Not yet: verifying *other* users, creating cross-signing keys. |
| 92 | + |
| 93 | +## How rooms map to conversations |
| 94 | + |
| 95 | +| Matrix | Talk | |
| 96 | +|---|---| |
| 97 | +| joined room | group conversation with `objectType = "matrix"`, `objectId = <room id>` | |
| 98 | +| room name / canonical alias / heroes | conversation name (Matrix room-name algorithm) | |
| 99 | +| topic | description | |
| 100 | +| `m.room.avatar` (or the peer's avatar in a direct chat) | conversation avatar | |
| 101 | +| linked Nextcloud users | `users` attendees | |
| 102 | +| other members | attendees with actor type `matrix` (actor id = Matrix user id, display name and avatar from Matrix; avatar served by `/apps/spreed/matrix/avatar/{token}/{size}/{mxid}`) | |
| 103 | +| power level ≥ 50 / creator | moderator / owner | |
| 104 | +| `m.room.message` text/notice/emote | chat message (HTML → Markdown, pills → mentions) | |
| 105 | +| `m.image`/`m.file`/… | `matrix-media` rich object (link served by `/apps/spreed/matrix/media/{eventId}`, `?thumbnail=1` for previews, `POST …/save` copies it into the user's files) | |
| 106 | +| `m.in_reply_to` | reply | |
| 107 | +| `m.thread` | Talk thread (created on first use) | |
| 108 | +| `m.reaction` / redaction of one | reaction added / removed | |
| 109 | +| `m.replace` (edit by the original sender) | message edited | |
| 110 | +| `m.room.redaction` | message deleted (Talk moderator rules do not apply – Matrix power levels do) | |
| 111 | +| Nextcloud file shared into the conversation | uploaded to the homeserver and posted as `m.file`/`m.image`/… (encrypted attachment in encrypted rooms) | |
| 112 | +| membership changes | system messages (`matrix_user_added`, `matrix_user_removed` for Matrix-only members) | |
| 113 | +| `m.room.tombstone` | conversation becomes read-only, `matrix_room_upgraded` | |
| 114 | +| calls | `matrix_call_unsupported` system message | |
| 115 | +| invite | pending invitation in the federation invitation API (`remoteServerUrl` = `matrix:<server>`) | |
| 116 | + |
| 117 | +Calls, lobby, SIP, listable, password, read-only toggle, message expiration, recording consent and |
| 118 | +permission changes are rejected with HTTP 405 `matrix-unsupported`. Renaming, topic, invites, kicks, |
| 119 | +promotions, leaving, reactions, edits, deletions and thread replies are executed on the Matrix side |
| 120 | +first (the homeserver enforces power levels); the Talk read marker is mirrored as an `m.read` receipt. |
| 121 | + |
| 122 | +## API additions |
| 123 | + |
| 124 | +* Capabilities: feature `matrix-rooms` (local), `config.matrix` with `enabled`, `can-link`, `linked`, |
| 125 | + `homeservers`, `e2ee-enabled`, `upload-enabled`, `typing-enabled`. |
| 126 | +* Conversation objects of Matrix rooms carry `matrixRoomId` and `matrixCapabilities` (room-level |
| 127 | + capabilities plus the current user's power-level dependent ones, e.g. `canSend`, `canInvite`, |
| 128 | + `canRename`, `isModerator`, `isDirect`, `encrypted`). |
| 129 | +* `GET|POST|PUT|DELETE /ocs/v2.php/apps/spreed/api/v1/matrix/account`, `POST …/matrix/account/sync` |
| 130 | +* Verification: `POST|GET|PUT|DELETE …/matrix/account/verification` (start, poll, confirm/reject, cancel) |
| 131 | +* Key backup: `POST …/matrix/account/backup` (`recoveryKey` optional) |
| 132 | +* Rooms: `POST …/matrix/room` (create/DM), `POST …/matrix/room/join` (`reference`), `GET …/matrix/room/directory` |
| 133 | +* Administration: `…/matrix/admin/homeserver[/{id}[/test]]`, `PUT …/matrix/admin/settings`, `GET …/matrix/admin/status` |
| 134 | +* Adding a participant to a Matrix conversation accepts `source=users` (linked Nextcloud user) or |
| 135 | + `source=matrix` with a Matrix user id. |
| 136 | + |
| 137 | +## The Matrix client library |
| 138 | + |
| 139 | +The protocol implementation lives in `matrix-client/` as the standalone, framework-free package |
| 140 | +`nextcloud/matrix-client-php` (namespace `Nextcloud\Matrix`). It has no Nextcloud dependency and |
| 141 | +its own test-suite; `examples/echo-bot.php` runs against a plain Synapse. Talk-specific code lives |
| 142 | +in `lib/Matrix/` (`OCA\Talk\Matrix`). |
0 commit comments