Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clarify the meaning of "public rooms" in the room directory #2104

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Rooms published in `/publicRooms` don't necessarily have `public` join rules or `world_readable` history visibility.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Rooms published in `/publicRooms` don't necessarily have `public` join rules or `world_readable` history visibility.
33 changes: 32 additions & 1 deletion content/client-server-api/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -2831,7 +2831,38 @@ re-invited.

{{% http-api spec="client-server" api="banning" %}}

### Listing rooms
### Room directory

Homeservers MAY publish a room directory to allow users to discover rooms. A room
can have one of two visibility settings in the directory:

`public`
The room will be shown in the published room directory.

`private`
The room will be hidden from the published room directory.

Clients can define a room's initial visibility in the directory via the `visibility`
parameter in [`/createRoom`](#post_matrixclientv3createroom). Irrespective of room
creation, clients can query and change a room's visibility in the directory through
the endpoints listed below, provided that the server permits this.

{{% boxes/warning %}}
The visibility setting merely defines whether a room is included in the published
room directory or not. It doesn't make any guarantees about the room's
[join rule](#mroomjoin_rules) or [history visibility](#room-history-visibility).

In particular, a visibility setting of `public` should not be confused with a `public`
join rule. Rooms with a join rule of `knock`, for instance, could reasonably be published
in the directory, too.

Similarly, a visibility setting of `public` does not necessarily imply a `world_readable`
history visibility.

To increase performance, servers MAY apply additional filters when listing the
directory, for instance, by automatically excluding rooms with `invite` join rules
that are not `world_readable` regardless of their visibility.
{{% /boxes/warning %}}

{{% http-api spec="client-server" api="list_public_rooms" %}}

Expand Down
7 changes: 3 additions & 4 deletions content/server-server-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -1045,11 +1045,10 @@ user's Matrix ID and the token delivered when the invite was stored,
this verification will prove that the `m.room.member` invite event comes
from the user owning the invited third-party identifier.

## Public Room Directory
## Room Directory

To complement the [Client-Server
API](/client-server-api)'s room directory,
homeservers need a way to query the public rooms for another server.
To complement the [room directory in the Client-Server API](/client-server-api#room-directory),
homeservers need a way to query the published rooms of another server.
This can be done by making a request to the `/publicRooms` endpoint for
the server the room directory should be retrieved for.

Expand Down
8 changes: 2 additions & 6 deletions data/api/client-server/create_room.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -87,12 +87,8 @@ paths:
- public
- private
description: |-
A `public` visibility indicates that the room will be shown
in the published room list. A `private` visibility will hide
the room from the published room list. Rooms default to
`private` visibility if this key is not included. NB: This
should not be confused with `join_rules` which also uses the
word `public`.
The room's visibility in the server's [room directory](#room-directory).
Defaults to `private`.
room_alias_name:
type: string
description: |-
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
# limitations under the License.

type: object
title: "PublicRoomsChunk"
title: "PublishedRoomsChunk"
properties:
canonical_alias:
type: string
Expand Down
21 changes: 4 additions & 17 deletions data/api/client-server/definitions/public_rooms_response.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,28 +13,15 @@
# limitations under the License.

type: object
description: A list of the rooms on the server.
description: A list of the published rooms on the server.
required: ["chunk"]
properties:
chunk:
type: array
description: |-
A paginated chunk of public rooms.
A paginated chunk of published rooms.
items:
allOf:
- $ref: "public_rooms_chunk.yaml"
- type: object
title: PublicRoomsChunk
properties:
# Override description of join_rule
join_rule:
type: string
description: |-
The room's join rule. When not present, the room is assumed to
be `public`. Note that rooms with `invite` join rules are not
expected here, but rooms with `knock` rules are given their
near-public nature.
Comment on lines -33 to -36
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This came in through fa6cc8a but I cannot find any references to it in matrix-org/matrix-spec-proposals#2403. I think it is trying to say that knockable rooms can be made visible in the directory? If so, I think this should rather be moved to where the meaning of visibility is described because here it makes it seem as if servers should do this automatically.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe that it's more a clarification about which join rules clients can expect to find here.

I agree that this should be next to the meaning of visibility.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have added a phrase about knockable rooms in the warning box.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@anoadragon453 says:

@Johennes Synapse considers rooms with knock and knock_restricted join rules part of its /publicRooms response. This stems from MSC2403, which specifically added rooms with the knock join rule to /publicRooms, so that users could find rooms to knock on.

Does this change the definition of a "public room" to one that has the join_rule public, knock or knock_restricted?

Copy link
Contributor Author

@Johennes Johennes Mar 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the context @anoadragon453! 🙏

So when responding to /publicRooms, Synapse first retrieves all rooms with public visibility but then applies an additional filter to only include these cases:

join_rules = '{JoinRules.PUBLIC}'
OR join_rules = '{JoinRules.KNOCK}'
OR join_rules = '{JoinRules.KNOCK_RESTRICTED}'
OR history_visibility = 'world_readable'

I'm struggling a bit with understanding how the room directory is meant to work. Other than the slightly unclear statement "rooms with knock rules are given their near-public nature", the spec doesn't mention any restrictions on what rooms can be included in /publicRooms. Therefore, my assumption was that rooms with a public visibility would show up regardless of their join rules or history visibility.

In fact, MSC2403 seems to only define join_rule as a new field in the response of /publicRooms. It doesn't appear to specify any filtering and instead says:

The spec does not prevent us from adding rooms with 'knock' join_rules to the public rooms directory.

That matches my assumption but not what Synapse actually does.

😵‍💫

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct, the room is only considered if visibility is public when modifying the public rooms list with PUT /_matrix/client/v3/directory/list/room/{roomId}.

To speak to Synapse's implementation, the join_rule restriction within the query was only added during a performance improvement change. I could see this requirement being useful, in that if a room is set to private, it would immediately no longer show in the public rooms list (as it is not publicly-joinable). While the spec may not require homeservers to manage their lists in that way, Synapse can choose to do so.

Regardless, it's clear that Synapse's filtering of its public rooms list here should not influence the spec's definition of a public room. So I think we can ignore this and consider a join_rule of public the only variant that constitutes a room as public.

Copy link
Contributor Author

@Johennes Johennes Mar 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To speak to Synapse's implementation, the join_rule restriction within the query was only added during a performance improvement change. I could see this requirement being useful, in that if a room is set to private, it would immediately no longer show in the public rooms list (as it is not publicly-joinable). While the spec may not require homeservers to manage their lists in that way, Synapse can choose to do so.

Oh, interesting. This sounds sensible. Maybe we could add this additional filtering as a MAY hint into the spec?

So I think we can ignore this and consider a join_rule of public the only variant that constitutes a room as public.

You mean visibility of public?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, interesting. This sounds sensible. Maybe we could add this additional filtering as a MAY hint into the spec?

Sounds reasonable to me.

You mean visibility of public?

Hah, yes sorry. join_rules aren't mandated affect the filtering. Terminology!

example: "public"
$ref: "public_rooms_chunk.yaml"
next_batch:
type: string
description: |-
Expand All @@ -50,7 +37,7 @@ properties:
total_room_count_estimate:
type: integer
description: |-
An estimate on the total number of public rooms, if the
An estimate on the total number of published rooms, if the
server has an estimate.
example: {
"chunk": [
Expand Down
36 changes: 17 additions & 19 deletions data/api/client-server/list_public_rooms.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@ paths:
"/directory/list/room/{roomId}":
get:
summary: Gets the visibility of a room in the directory
description: Gets the visibility of a given room on the server's public room
directory.
description: Gets the visibility of a given room in the server's room directory.
operationId: getRoomVisibilityOnDirectory
parameters:
- in: path
Expand All @@ -32,7 +31,7 @@ paths:
type: string
responses:
"200":
description: The visibility of the room in the directory
description: The visibility of the room in the directory.
content:
application/json:
schema:
Expand All @@ -50,7 +49,7 @@ paths:
"visibility": "public"
}
"404":
description: The room is not known to the server
description: The room is not known to the server.
content:
application/json:
schema:
Expand All @@ -66,12 +65,11 @@ paths:
put:
summary: Sets the visibility of a room in the room directory
description: |-
Sets the visibility of a given room in the server's public room
directory.
Sets the visibility of a given room in the server's room directory.

Servers may choose to implement additional access control checks
here, for instance that room visibility can only be changed by
the room creator or a server administrator.
Servers MAY implement additional access control checks, for instance,
to ensure that a room's visibility can only be changed by the room creator
or a server administrator.
operationId: setRoomVisibilityOnDirectory
security:
- accessTokenQuery: []
Expand All @@ -97,7 +95,7 @@ paths:
- public
description: |-
The new visibility setting for the room.
Defaults to 'public'.
Defaults to `public`.
example: {
"visibility": "public"
}
Expand All @@ -114,7 +112,7 @@ paths:
response:
value: {}
"404":
description: The room is not known to the server
description: The room is not known to the server.
content:
application/json:
schema:
Expand All @@ -129,9 +127,9 @@ paths:
- Room discovery
/publicRooms:
get:
summary: Lists the public rooms on the server.
summary: Lists a server's published room directory
description: |-
Lists the public rooms on the server.
Lists a server's published room directory.

This API returns paginated responses. The rooms are ordered by the number
of joined members, with the largest rooms first.
Expand All @@ -154,23 +152,23 @@ paths:
- in: query
name: server
description: |-
The server to fetch the public room lists from. Defaults to the
The server to fetch the room directory from. Defaults to the
local server. Case sensitive.
schema:
type: string
responses:
"200":
description: A list of the rooms on the server.
description: A list of the published rooms on the server.
content:
application/json:
schema:
$ref: definitions/public_rooms_response.yaml
tags:
- Room discovery
post:
summary: Lists the public rooms on the server with optional filter.
summary: Lists a server's published room directory with an optional filter
description: |-
Lists the public rooms on the server, with optional filter.
Lists a server's published room directory with an optional filter.

This API returns paginated responses. The rooms are ordered by the number
of joined members, with the largest rooms first.
Expand All @@ -182,7 +180,7 @@ paths:
- in: query
name: server
description: |-
The server to fetch the public room lists from. Defaults to the
The server to fetch the room directory from. Defaults to the
local server. Case sensitive.
schema:
type: string
Expand Down Expand Up @@ -253,7 +251,7 @@ paths:
required: true
responses:
"200":
description: A list of the rooms on the server.
description: A filtered list of the published rooms on the server.
content:
application/json:
schema:
Expand Down
85 changes: 14 additions & 71 deletions data/api/server-server/public_rooms.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,20 @@
# limitations under the License.
openapi: 3.1.0
info:
title: Matrix Federation Public Rooms API
title: Matrix Federation Room Directory API
version: 1.0.0
paths:
/publicRooms:
get:
summary: Get all the public rooms for a homeserver
summary: Lists the server's published room directory.
description: |-
Gets all the public rooms for the homeserver. This should not return
rooms that are listed on another homeserver's directory, just those
listed on the receiving homeserver's directory.
Lists the server's published room directory.

This API returns paginated responses. The rooms are ordered by the number
of joined members, with the largest rooms first.

This should not return rooms that are listed on another homeserver's directory,
just those listed on the receiving homeserver's directory.
operationId: getPublicRooms
security:
- signedRequest: []
Expand Down Expand Up @@ -62,21 +66,18 @@ paths:
type: string
responses:
"200":
description: The public room list for the homeserver.
description: A list of the published rooms on the server.
content:
application/json:
schema:
$ref: ../client-server/definitions/public_rooms_response.yaml
post:
summary: Gets the public rooms on the server with optional filter.
summary: Lists the server's published room directory with an optional filter
description: |-
Lists the public rooms on the server, with optional filter.
Lists the server's published room directory with an optional filter.

This API returns paginated responses. The rooms are ordered by the number
of joined members, with the largest rooms first.

Note that this endpoint receives and returns the same format that is seen
in the Client-Server API's `POST /publicRooms` endpoint.
Comment on lines -77 to -79
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This felt slightly confusing because the CS endpoint has a server parameter that is missing here. I think the identity of the request and response bodies is clear enough without this note.

operationId: queryPublicRooms
security:
- signedRequest: []
Expand Down Expand Up @@ -147,69 +148,11 @@ paths:
required: true
responses:
"200":
description: A list of the rooms on the server.
description: A filtered list of the published rooms on the server.
content:
application/json:
schema:
type: object
description: A list of the rooms on the server.
required:
- chunk
Comment on lines -156 to -157
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAICT this is the only difference to public_rooms_response.yaml. I cannot imagine that chunk was supposed to be required on only one of the four endpoints so I have made this reuse the shared schema.

properties:
chunk:
title: PublicRoomsChunks
type: array
description: A paginated chunk of public rooms.
items:
allOf:
- $ref: ../client-server/definitions/public_rooms_chunk.yaml
- type: object
properties:
# Override description of join_rule
join_rule:
type: string
description: |-
The room's join rule. When not present, the room is assumed to
be `public`. Note that rooms with `invite` join rules are not
expected here, but rooms with `knock` rules are given their
near-public nature.
next_batch:
type: string
description: |-
A pagination token for the response. The absence of this token
means there are no more results to fetch and the client should
stop paginating.
prev_batch:
type: string
description: |-
A pagination token that allows fetching previous results. The
absence of this token means there are no results before this
batch, i.e. this is the first batch.
total_room_count_estimate:
type: integer
description: |-
An estimate on the total number of public rooms, if the
server has an estimate.
examples:
response:
value: {
"chunk": [
{
"avatar_url": "mxc://bleecker.street/CHEDDARandBRIE",
"guest_can_join": false,
"name": "CHEESE",
"num_joined_members": 37,
"room_id": "!ol19s:bleecker.street",
"topic": "Tasty tasty cheese",
"world_readable": true,
"join_rule": "public",
"room_type": "m.space"
}
],
"next_batch": "p190q",
"prev_batch": "p1902",
"total_room_count_estimate": 115
}
$ref: ../client-server/definitions/public_rooms_response.yaml
servers:
- url: "{protocol}://{hostname}{basePath}"
variables:
Expand Down