From fdc29e552956821d9ad6b9652785f99599b29376 Mon Sep 17 00:00:00 2001 From: ylm Date: Thu, 22 Oct 2026 10:44:30 -0400 Subject: [PATCH 1/8] Propose ADR 011: reveal a user's Matrix id only to users who share a realm with them Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_019vCUo5Tv7Mu8Bor9YUFjJm --- ...ly-to-users-who-share-a-realm-with-them.md | 123 ++++++++++++++++++ docs/adr/README.md | 3 +- 2 files changed, 125 insertions(+), 1 deletion(-) create mode 100644 docs/adr/011-reveal-a-users-matrix-id-only-to-users-who-share-a-realm-with-them.md diff --git a/docs/adr/011-reveal-a-users-matrix-id-only-to-users-who-share-a-realm-with-them.md b/docs/adr/011-reveal-a-users-matrix-id-only-to-users-who-share-a-realm-with-them.md new file mode 100644 index 00000000000..2de6d0f52c8 --- /dev/null +++ b/docs/adr/011-reveal-a-users-matrix-id-only-to-users-who-share-a-realm-with-them.md @@ -0,0 +1,123 @@ +--- +status: proposed +date: 2026-10-22 +scope: + - packages/realm-server/** + - packages/runtime-common/matrix-client.ts + - packages/host/app/services/matrix-service.ts + - packages/host/app/services/message-service.ts + - packages/host/app/services/realm-server.ts + - packages/host/app/tools/invite-user-to-room.ts + - packages/host/app/tools/create-ai-assistant-room.ts + - packages/ai-bot/** + - packages/bot-runner/** + - packages/matrix/support/synapse/** +--- + +# 011. Reveal a user's Matrix id only to users who share a realm with them + +A user's Matrix user id is the name they sign in with. Boxel does not show it +to other users, except to the users who have been granted access to the same +realm. Matrix shows every member of a room to every other member, and Synapse +has no setting that hides them. So the rule is kept by controlling who shares a +room, and by turning off the homeserver features that let users find each +other. A design that delivers one event to many users therefore can't put them +in one room. + +## 1. Decision + +The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", +"SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this +document are to be interpreted as described in BCP 14 (RFC 2119, RFC 8174) when, +and only when, they appear in all capitals, as shown here. + +In this ADR, an end user is a person's Matrix account. The realm server's +users, the AI bot and the other bots are service users, not end users. A +Matrix id includes its localpart, so these rules cover the username alone too. + +### 1.1. Matrix ids are private by default + +Boxel MUST NOT reveal an end user's Matrix id to another end user, except as +§1.2 and §1.4 allow. This covers every channel, including room membership, +room state and timelines, user directory search, presence, realm events and +realm-server API responses. + +### 1.2. Users who share a realm may see each other + +When two end users each hold a realm permission grant that names them in the +same realm, Boxel MAY reveal each one's Matrix id to the other. This includes +the realm owner. The `'*'` grant names no one, so it does not count. + +### 1.3. Reading a world-readable realm reveals no one + +Reading a world-readable realm, or subscribing to its events, MUST NOT reveal +the reader's Matrix id to another end user. It MUST NOT reveal another end +user's Matrix id to the reader, except as §1.2 and §1.4 allow. + +### 1.4. A realm's URL may name its owner + +A realm's URL MAY contain the Matrix username of the user who owns it. This +covers the realm-server path `///` and the published-space +host `.`. + +### 1.5. End users share a room only when they may see each other + +Two end users MUST NOT both be joined to, or invited to, the same Matrix room +unless §1.2 lets each see the other's Matrix id. A room MAY hold one end user +and any number of service users. + +### 1.6. The homeserver doesn't let users find each other + +The homeserver MUST NOT return one end user to another in user directory +search. It MUST either disable the user directory or filter it, for example +with a module that implements `check_username_for_spam`. Presence MUST stay +disabled. + +## 2. Rationale + +The team decided that Boxel must not expose one user's Matrix username to +another without a reason the user agreed to. Sharing a realm is that reason: +the owner chose whom to grant access to, and collaborators in a realm already +see each other's activity in it. + +Matrix gives no way to hide a room's members from each other. A member can +list the room's members, and every join is an event in the room's timeline, +which each member receives through sync. Synapse has no setting that changes +this, as of v1.164.0rc1. Blocking the member-list endpoints at a proxy leaves the +timeline. So the rule has to be kept by deciding who shares a room (§1.5), +not by hiding members after the fact. + +Synapse's user directory also returns the members of any room with a `public` +join rule to every user on the server, whether or not they share the room. +One public room would make its members searchable by everyone, so §1.6 turns +the directory off as a backstop. + +## 3. Considered options + +- **Reveal ids only within a shared realm (chosen).** It allows what Boxel + does today in private realms, and it gives a test a reviewer can apply. +- **Never reveal one user's id to another.** Collaborators in a realm already + see each other's activity, and realm owners manage grants by Matrix id. +- **Reveal ids when a user accepts an invite from another user.** Boxel has no + invite flow between end users yet. When it does, its consent rule belongs in + a change to this ADR. +- **Let users share rooms, and hide the members.** Synapse can't hide members + from each other, and the timeline still carries every join. + +## 4. Consequences + +A design that delivers one event to many end users who don't share a realm, +such as the events of a world-readable realm, can't use a room they all join. +It can send to each user's own room, send to-device messages, or deliver +through a realm-server channel. A shared room for a world-readable realm's +events, as proposed in #6906, breaks §1.3 and §1.5. + +## 5. Remediation + +- `packages/host/app/tools/invite-user-to-room.ts` (`InviteUserToRoomTool`) + breaks §1.5. It invites any Matrix id to a room, with no check that the + invitee shares a realm with the room's other end users. +- `packages/matrix/support/synapse/dev/homeserver.yaml`, + `packages/matrix/support/synapse/test/homeserver.yaml` and + `packages/matrix/support/synapse/test-without-registration-token/homeserver.yaml` + break §1.6. They leave the user directory at its default, which is on. diff --git a/docs/adr/README.md b/docs/adr/README.md index cc079cf80c3..f1ddabe2e13 100644 --- a/docs/adr/README.md +++ b/docs/adr/README.md @@ -17,9 +17,10 @@ or backfill one. | [001](001-record-architecture-decisions.md) | Record architecture decisions as short, normative ADRs | proposed | | [002](002-keep-identifiers-canonical-resolve-at-network-boundary.md) | Keep identifiers as canonical RRIs, and resolve them to URLs only at the network boundary | proposed | | [003](003-reach-cloud-services-through-adapters.md) | Reach proprietary cloud services only through adapters | proposed | -| [004](004-federate-realm-servers-each-paired-with-its-own-homeserver.md) | Federate realm servers, each paired with its own Matrix homeserver | proposed | +| [004](004-federate-realm-servers-each-paired-with-its-own-homeserver.md) | Federate realm servers, each paired with its own Matrix homeserver | proposed | | [005](005-authorize-key-changes-only-with-a-session-card-code-cant-reach.md) | Authorize changes to a user's keys only with a session that card code can't reach | proposed | | [006](006-grant-realm-permissions-per-realm-and-widen-them-only-with-a-policy.md) | Grant realm permissions per realm, and widen them only with a realm policy | proposed | | [007](007-keep-motion-a-reactive-layer-that-state-never-waits-on.md) | Keep motion a reactive layer that application state never waits on | proposed | | [008](008-build-the-secure-origins-pages-as-their-own-ember-app.md) | Build the secure origin's pages as their own Ember app | proposed | | [009](009-build-new-ui-with-pret-ui-not-boxel-ui.md) | Build new UI with Pret UI, and deprecate boxel-ui | proposed | +| [011](011-reveal-a-users-matrix-id-only-to-users-who-share-a-realm-with-them.md) | Reveal a user's Matrix id only to users who share a realm with them | proposed | From 12267ff603714032069b1bb5f831012e41ac9035 Mon Sep 17 00:00:00 2001 From: ylm Date: Thu, 22 Oct 2026 10:47:33 -0400 Subject: [PATCH 2/8] ADR 011: cover profile lookups and invites in the homeserver rule Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_019vCUo5Tv7Mu8Bor9YUFjJm --- ...ly-to-users-who-share-a-realm-with-them.md | 35 ++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/docs/adr/011-reveal-a-users-matrix-id-only-to-users-who-share-a-realm-with-them.md b/docs/adr/011-reveal-a-users-matrix-id-only-to-users-who-share-a-realm-with-them.md index 2de6d0f52c8..7563a954a9c 100644 --- a/docs/adr/011-reveal-a-users-matrix-id-only-to-users-who-share-a-realm-with-them.md +++ b/docs/adr/011-reveal-a-users-matrix-id-only-to-users-who-share-a-realm-with-them.md @@ -73,6 +73,20 @@ search. It MUST either disable the user directory or filter it, for example with a module that implements `check_username_for_spam`. Presence MUST stay disabled. +The homeserver MUST answer a profile request only when it is authenticated +and the requester shares a room with the user +(`require_auth_for_profile_requests` and +`limit_profile_requests_to_users_who_share_rooms`). It MUST NOT answer profile +requests over federation (`allow_profile_lookup_over_federation: false`). + +The homeserver MUST reject an invite from one end user to another unless §1.2 +lets each see the other's Matrix id, for example with a module that implements +`user_may_invite`. It MUST NOT put the inviter's profile into an invite +(`include_profile_data_on_invite: false`). + +These rules apply to every homeserver Boxel runs, including the configuration +kept outside this repository. + ## 2. Rationale The team decided that Boxel must not expose one user's Matrix username to @@ -92,6 +106,13 @@ join rule to every user on the server, whether or not they share the room. One public room would make its members searchable by everyone, so §1.6 turns the directory off as a backstop. +Room membership isn't the only way to learn a Matrix id. By default, Synapse +answers a profile request for any user, without authentication and over +federation, so anyone can confirm that a guessed Matrix id exists. It also +lets any user invite any other, and the invite shows the inviter's id and +profile. Any client can call these APIs directly, so the host app's own +behavior can't keep the rule. The homeserver has to. + ## 3. Considered options - **Reveal ids only within a shared realm (chosen).** It allows what Boxel @@ -120,4 +141,16 @@ events, as proposed in #6906, breaks §1.3 and §1.5. - `packages/matrix/support/synapse/dev/homeserver.yaml`, `packages/matrix/support/synapse/test/homeserver.yaml` and `packages/matrix/support/synapse/test-without-registration-token/homeserver.yaml` - break §1.6. They leave the user directory at its default, which is on. + break §1.6. They leave the user directory, unauthenticated profile requests, + profile lookup over federation, and profile data in invites at their + defaults, which are all on. +- `packages/runtime-common/db-queries/realm-permission-queries.ts` (its use + of `fetchMatrixProfile`) and + `packages/realm-server/middleware/multi-realm-authorization.ts` + (`hasMatrixProfile`) depend on what §1.6 forbids. They ask the homeserver + for the profile of a user they share no room with, the first without + authentication, to learn whether the account exists. Under §1.6 that lookup + fails, and a failed lookup withholds the user's `users` grant. They need + another way to check an account before the homeserver applies §1.6. +- `packages/matrix/support/synapse/modules/` breaks §1.6. It has no module + that rejects invites between end users who don't share a realm. From 76f14a786f1b1932d03f22bab8bf806d8ff788f5 Mon Sep 17 00:00:00 2001 From: ylm Date: Thu, 22 Oct 2026 10:49:41 -0400 Subject: [PATCH 3/8] ADR 011: allow an owner's username in realm URLs, but no list of realms Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_019vCUo5Tv7Mu8Bor9YUFjJm --- ...-id-only-to-users-who-share-a-realm-with-them.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/docs/adr/011-reveal-a-users-matrix-id-only-to-users-who-share-a-realm-with-them.md b/docs/adr/011-reveal-a-users-matrix-id-only-to-users-who-share-a-realm-with-them.md index 7563a954a9c..0f1e47f3f02 100644 --- a/docs/adr/011-reveal-a-users-matrix-id-only-to-users-who-share-a-realm-with-them.md +++ b/docs/adr/011-reveal-a-users-matrix-id-only-to-users-who-share-a-realm-with-them.md @@ -4,6 +4,7 @@ date: 2026-10-22 scope: - packages/realm-server/** - packages/runtime-common/matrix-client.ts + - packages/runtime-common/db-queries/realm-permission-queries.ts - packages/host/app/services/matrix-service.ts - packages/host/app/services/message-service.ts - packages/host/app/services/realm-server.ts @@ -58,7 +59,12 @@ user's Matrix id to the reader, except as §1.2 and §1.4 allow. A realm's URL MAY contain the Matrix username of the user who owns it. This covers the realm-server path `///` and the published-space -host `.`. +host `.`. A user learns such a URL only when someone gives it +to them, so the owner decides who sees it. + +Boxel MUST NOT offer a public or queryable list of realm URLs. An endpoint MAY +return the URLs of the realms a user holds a grant on that names them. It MUST +NOT return the URLs of other realms, including world-readable ones. ### 1.5. End users share a room only when they may see each other @@ -135,6 +141,11 @@ events, as proposed in #6906, breaks §1.3 and §1.5. ## 5. Remediation +- `packages/realm-server/handlers/handle-fetch-public-realms.ts` and + `packages/runtime-common/db-queries/realm-permission-queries.ts` + (`fetchPublicRealmSummaries`) break §1.4. `GET /_public-realms` returns the + URL of every world-readable realm to any signed-in user, and each URL names + its owner. - `packages/host/app/tools/invite-user-to-room.ts` (`InviteUserToRoomTool`) breaks §1.5. It invites any Matrix id to a room, with no check that the invitee shares a realm with the room's other end users. From 74106255a7f1252d9040057c99e60d2a960efbd9 Mon Sep 17 00:00:00 2001 From: ylm Date: Thu, 22 Oct 2026 10:51:16 -0400 Subject: [PATCH 4/8] ADR 011: allow listing world-readable realms that aren't published Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_019vCUo5Tv7Mu8Bor9YUFjJm --- ...only-to-users-who-share-a-realm-with-them.md | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/docs/adr/011-reveal-a-users-matrix-id-only-to-users-who-share-a-realm-with-them.md b/docs/adr/011-reveal-a-users-matrix-id-only-to-users-who-share-a-realm-with-them.md index 0f1e47f3f02..f7e54913261 100644 --- a/docs/adr/011-reveal-a-users-matrix-id-only-to-users-who-share-a-realm-with-them.md +++ b/docs/adr/011-reveal-a-users-matrix-id-only-to-users-who-share-a-realm-with-them.md @@ -59,12 +59,14 @@ user's Matrix id to the reader, except as §1.2 and §1.4 allow. A realm's URL MAY contain the Matrix username of the user who owns it. This covers the realm-server path `///` and the published-space -host `.`. A user learns such a URL only when someone gives it -to them, so the owner decides who sees it. +host `.`. -Boxel MUST NOT offer a public or queryable list of realm URLs. An endpoint MAY -return the URLs of the realms a user holds a grant on that names them. It MUST -NOT return the URLs of other realms, including world-readable ones. +Boxel MUST NOT offer a public or queryable list of realm URLs, except as this +section allows. An endpoint MAY list the realms a user holds a grant on that +names them. An endpoint MAY list world-readable realms that are not published +realms, with their URLs and names: an owner who makes a realm world-readable +agrees to have it found. A list MUST NOT include a published realm. Outside +these lists, a user learns a realm's URL only when someone gives it to them. ### 1.5. End users share a room only when they may see each other @@ -141,11 +143,6 @@ events, as proposed in #6906, breaks §1.3 and §1.5. ## 5. Remediation -- `packages/realm-server/handlers/handle-fetch-public-realms.ts` and - `packages/runtime-common/db-queries/realm-permission-queries.ts` - (`fetchPublicRealmSummaries`) break §1.4. `GET /_public-realms` returns the - URL of every world-readable realm to any signed-in user, and each URL names - its owner. - `packages/host/app/tools/invite-user-to-room.ts` (`InviteUserToRoomTool`) breaks §1.5. It invites any Matrix id to a room, with no check that the invitee shares a realm with the room's other end users. From 4d9a9b16cd72cddfc686487cbecfcd631d24b2b7 Mon Sep 17 00:00:00 2001 From: ylm Date: Thu, 22 Oct 2026 11:44:04 -0400 Subject: [PATCH 5/8] ADR 011: add forking Synapse as a considered option Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_019vCUo5Tv7Mu8Bor9YUFjJm --- ...rs-matrix-id-only-to-users-who-share-a-realm-with-them.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/adr/011-reveal-a-users-matrix-id-only-to-users-who-share-a-realm-with-them.md b/docs/adr/011-reveal-a-users-matrix-id-only-to-users-who-share-a-realm-with-them.md index f7e54913261..61ba704dbf7 100644 --- a/docs/adr/011-reveal-a-users-matrix-id-only-to-users-who-share-a-realm-with-them.md +++ b/docs/adr/011-reveal-a-users-matrix-id-only-to-users-who-share-a-realm-with-them.md @@ -132,6 +132,11 @@ behavior can't keep the rule. The homeserver has to. a change to this ADR. - **Let users share rooms, and hide the members.** Synapse can't hide members from each other, and the timeline still carries every join. +- **Fork or reimplement Synapse to control what it reveals.** A homeserver of + our own could stop listing a room's members and leave other users' joins out + of each member's timeline, so end users could share a room without seeing + each other. That is a large, ongoing undertaking, and it isn't planned now. + If Boxel takes it on, a change to this ADR can relax §1.5. ## 4. Consequences From 7fbb46baed3237bae1752e877ebcff24acc07505 Mon Sep 17 00:00:00 2001 From: ylm Date: Sun, 25 Oct 2026 23:01:55 -0400 Subject: [PATCH 6/8] ADR 011: cite the Synapse version Boxel runs Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01TobToyDmxQvQyu8azoFwGP --- ...x-id-only-to-users-who-share-a-realm-with-them.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/docs/adr/011-reveal-a-users-matrix-id-only-to-users-who-share-a-realm-with-them.md b/docs/adr/011-reveal-a-users-matrix-id-only-to-users-who-share-a-realm-with-them.md index 61ba704dbf7..dac5aef801a 100644 --- a/docs/adr/011-reveal-a-users-matrix-id-only-to-users-who-share-a-realm-with-them.md +++ b/docs/adr/011-reveal-a-users-matrix-id-only-to-users-who-share-a-realm-with-them.md @@ -105,14 +105,16 @@ see each other's activity in it. Matrix gives no way to hide a room's members from each other. A member can list the room's members, and every join is an event in the room's timeline, which each member receives through sync. Synapse has no setting that changes -this, as of v1.164.0rc1. Blocking the member-list endpoints at a proxy leaves the -timeline. So the rule has to be kept by deciding who shares a room (§1.5), +this, as of v1.163.0, the version Boxel runs. Blocking the member-list +endpoints at a proxy leaves the timeline. So the rule has to be kept by deciding who shares a room (§1.5), not by hiding members after the fact. Synapse's user directory also returns the members of any room with a `public` join rule to every user on the server, whether or not they share the room. One public room would make its members searchable by everyone, so §1.6 turns -the directory off as a backstop. +the directory off as a backstop. Filtering it instead is possible: Synapse +passes `check_username_for_spam` the id of the user who is searching, so a +module can apply §1.2 to each result. Room membership isn't the only way to learn a Matrix id. By default, Synapse answers a profile request for any user, without authentication and over @@ -156,7 +158,9 @@ events, as proposed in #6906, breaks §1.3 and §1.5. `packages/matrix/support/synapse/test-without-registration-token/homeserver.yaml` break §1.6. They leave the user directory, unauthenticated profile requests, profile lookup over federation, and profile data in invites at their - defaults, which are all on. + defaults, which are all on. `limit_profile_requests_to_users_who_share_rooms` + has to be turned on together with `require_auth_for_profile_requests`: + Synapse refuses to start with the first and not the second. - `packages/runtime-common/db-queries/realm-permission-queries.ts` (its use of `fetchMatrixProfile`) and `packages/realm-server/middleware/multi-realm-authorization.ts` From bfc265103f777b459406a687fab13517cce1ed29 Mon Sep 17 00:00:00 2001 From: ylm Date: Sun, 25 Oct 2026 23:25:03 -0400 Subject: [PATCH 7/8] ADR 011: address review of grants, registration, federation and scope Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01TobToyDmxQvQyu8azoFwGP --- ...ly-to-users-who-share-a-realm-with-them.md | 77 +++++++++++++++---- 1 file changed, 60 insertions(+), 17 deletions(-) diff --git a/docs/adr/011-reveal-a-users-matrix-id-only-to-users-who-share-a-realm-with-them.md b/docs/adr/011-reveal-a-users-matrix-id-only-to-users-who-share-a-realm-with-them.md index dac5aef801a..f109158c039 100644 --- a/docs/adr/011-reveal-a-users-matrix-id-only-to-users-who-share-a-realm-with-them.md +++ b/docs/adr/011-reveal-a-users-matrix-id-only-to-users-who-share-a-realm-with-them.md @@ -8,8 +8,9 @@ scope: - packages/host/app/services/matrix-service.ts - packages/host/app/services/message-service.ts - packages/host/app/services/realm-server.ts - - packages/host/app/tools/invite-user-to-room.ts - - packages/host/app/tools/create-ai-assistant-room.ts + - packages/host/app/services/ai-assistant-panel-service.ts + - packages/host/app/tools/** + - packages/host/app/components/matrix/register-user.gts - packages/ai-bot/** - packages/bot-runner/** - packages/matrix/support/synapse/** @@ -47,12 +48,16 @@ realm-server API responses. When two end users each hold a realm permission grant that names them in the same realm, Boxel MAY reveal each one's Matrix id to the other. This includes -the realm owner. The `'*'` grant names no one, so it does not count. +the realm owner. The `'*'` and `users` grants name no one, so they do not +count. A `RealmPolicy` grant (ADR 006 §1.3) does not count either, even one +whose predicate matches a single user: only a permission row whose subject is +the user names them. -### 1.3. Reading a world-readable realm reveals no one +### 1.3. Reading an open realm reveals no one -Reading a world-readable realm, or subscribing to its events, MUST NOT reveal -the reader's Matrix id to another end user. It MUST NOT reveal another end +An open realm is one readable through a `'*'` or `users` grant. Reading an +open realm, or subscribing to its events, MUST NOT reveal the reader's Matrix +id to another end user. It MUST NOT reveal another end user's Matrix id to the reader, except as §1.2 and §1.4 allow. ### 1.4. A realm's URL may name its owner @@ -92,9 +97,21 @@ lets each see the other's Matrix id, for example with a module that implements `user_may_invite`. It MUST NOT put the inviter's profile into an invite (`include_profile_data_on_invite: false`). +The homeserver MAY tell a person who is registering whether a username is +taken (`/register/available`), and MUST rate-limit those answers. + These rules apply to every homeserver Boxel runs, including the configuration kept outside this repository. +### 1.7. Users of other homeservers + +ADR 004 lets users of one homeserver hold grants on realms another homeserver +serves, but federation isn't built yet. A design that puts end users of +different homeservers in one room, or lets one invite another, MUST first +change this ADR to say how the invite check of §1.6 learns grants held on +another realm server. A homeserver can see only its own realm server's +grants. + ## 2. Rationale The team decided that Boxel must not expose one user's Matrix username to @@ -106,8 +123,14 @@ Matrix gives no way to hide a room's members from each other. A member can list the room's members, and every join is an event in the room's timeline, which each member receives through sync. Synapse has no setting that changes this, as of v1.163.0, the version Boxel runs. Blocking the member-list -endpoints at a proxy leaves the timeline. So the rule has to be kept by deciding who shares a room (§1.5), -not by hiding members after the fact. +endpoints at a proxy leaves the timeline. So the rule has to be kept by +deciding who shares a room (§1.5), not by hiding members after the fact. + +A realm readable through a `users` grant has the same shape as a +world-readable one: many readers who chose the realm, not each other. On one +homeserver, `users` is close to everyone. Policy grants are left out because +the homeserver's invite check can't evaluate a predicate, and a predicate can +match as many unrelated users as `users` does. Synapse's user directory also returns the members of any room with a `public` join rule to every user on the server, whether or not they share the room. @@ -123,6 +146,18 @@ lets any user invite any other, and the invite shows the inviter's id and profile. Any client can call these APIs directly, so the host app's own behavior can't keep the rule. The homeserver has to. +Registration is the exception. It has to refuse a username that is taken, so +it always reveals that the username exists. Hiding it from +`/register/available` only moves the answer to the registration request +itself. Synapse rate-limits `/register/available` by client address, which +slows guessing, and a realm URL may name its owner anyway (§1.4). + +Federation (ADR 004) is deferred by §1.7 rather than designed here. A +homeserver Boxel doesn't run can't be held to §1.6, and the invite check has +no view of grants on another realm server. Identity checks don't need profile +lookups over federation: ADR 004 §1.3 verifies a user through the OpenID +userinfo endpoint, which §1.6 leaves open. + ## 3. Considered options - **Reveal ids only within a shared realm (chosen).** It allows what Boxel @@ -161,13 +196,21 @@ events, as proposed in #6906, breaks §1.3 and §1.5. defaults, which are all on. `limit_profile_requests_to_users_who_share_rooms` has to be turned on together with `require_auth_for_profile_requests`: Synapse refuses to start with the first and not the second. -- `packages/runtime-common/db-queries/realm-permission-queries.ts` (its use - of `fetchMatrixProfile`) and +- `packages/runtime-common/db-queries/realm-permission-queries.ts` + (`fetchEffectiveRealmPermissions`) and `packages/realm-server/middleware/multi-realm-authorization.ts` - (`hasMatrixProfile`) depend on what §1.6 forbids. They ask the homeserver - for the profile of a user they share no room with, the first without - authentication, to learn whether the account exists. Under §1.6 that lookup - fails, and a failed lookup withholds the user's `users` grant. They need - another way to check an account before the homeserver applies §1.6. -- `packages/matrix/support/synapse/modules/` breaks §1.6. It has no module - that rejects invites between end users who don't share a realm. + (`hasMatrixProfile`) depend on what §1.6 forbids. Both send an + unauthenticated profile request, through `fetchMatrixProfile`, to learn + whether the account exists. Under §1.6 that request fails, and a failed + lookup withholds the user's `users` grant. In + `multi-realm-authorization.ts` the user is the signed-in principal, whose + account the realm server verified when it issued their session, so the + lookup may not be needed there. `fetchEffectiveRealmPermissions` runs in worker + processes that hold no Matrix client, so it needs another way to check an + account. Both have to change before the homeserver applies §1.6. +- `packages/matrix/support/synapse/dev/homeserver.yaml`, + `packages/matrix/support/synapse/test/homeserver.yaml` and + `packages/matrix/support/synapse/test-without-registration-token/homeserver.yaml` + also break §1.6 because they load no module (`modules:`) that implements + `user_may_invite` to reject invites between end users who don't share a + realm. From 35fe836903bfdc29dcdca15982cc550408e962d8 Mon Sep 17 00:00:00 2001 From: ylm Date: Mon, 26 Oct 2026 00:14:23 -0400 Subject: [PATCH 8/8] ADR 011: list the staging and production homeserver configs under Remediation Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01TobToyDmxQvQyu8azoFwGP --- ...s-matrix-id-only-to-users-who-share-a-realm-with-them.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/adr/011-reveal-a-users-matrix-id-only-to-users-who-share-a-realm-with-them.md b/docs/adr/011-reveal-a-users-matrix-id-only-to-users-who-share-a-realm-with-them.md index f109158c039..c376a03fed7 100644 --- a/docs/adr/011-reveal-a-users-matrix-id-only-to-users-who-share-a-realm-with-them.md +++ b/docs/adr/011-reveal-a-users-matrix-id-only-to-users-who-share-a-realm-with-them.md @@ -214,3 +214,9 @@ events, as proposed in #6906, breaks §1.3 and §1.5. also break §1.6 because they load no module (`modules:`) that implements `user_may_invite` to reject invites between end users who don't share a realm. +- The homeserver configurations for staging and production are kept outside + this repository, so no entry here can name them. They break §1.6 in the same + ways as the files above until each one sets the profile and invite settings + and loads the invite module. `.github/workflows/sync-synapse-assets.yml` + copies `packages/matrix/support/synapse/modules/` to those homeservers, but + each configuration still has to list the module under `modules:`.