Compare commits

..

1 commit

Author SHA1 Message Date
Johannes Marbach cdcc2b9a03
Merge 7b3083885b into fca171427f 2025-04-22 17:33:14 +01:00
32 changed files with 57 additions and 292 deletions

View file

@ -2,7 +2,6 @@ name: "Spec"
env:
HUGO_VERSION: 0.139.0
PYTHON_VERSION: 3.13
on:
push:
@ -41,7 +40,7 @@ jobs:
- name: " Setup Python"
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
python-version: '3.9'
cache: 'pip'
cache-dependency-path: scripts/requirements.txt
- name: " Install dependencies"
@ -60,7 +59,7 @@ jobs:
- name: " Setup Python"
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
python-version: '3.9'
cache: 'pip'
cache-dependency-path: scripts/requirements.txt
- name: " Install dependencies"
@ -79,7 +78,7 @@ jobs:
- name: " Setup Python"
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
python-version: '3.9'
cache: 'pip'
cache-dependency-path: scripts/requirements.txt
- name: " Install dependencies"
@ -121,7 +120,7 @@ jobs:
- name: " Setup Python"
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
python-version: '3.9'
cache: 'pip'
cache-dependency-path: scripts/requirements.txt
- name: " Install dependencies"
@ -173,7 +172,7 @@ jobs:
- name: " Setup Python"
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
python-version: '3.9'
- name: " Install towncrier"
run: "pip install 'towncrier'"
- name: "Generate changelog"
@ -284,11 +283,10 @@ jobs:
npm i
npm run get-proposals
- name: "⚙️ hugo"
env:
HUGO_PARAMS_VERSION_STATUS: "historical"
# Create a baseURL like `/v1.2` out of the `v1.2` tag
run: |
hugo --baseURL "/${GITHUB_REF/refs\/tags\//}" -d "spec"
echo -e '[params.version]\nstatus="historical"' > historical.toml
hugo --config config.toml,historical.toml --baseURL "/${GITHUB_REF/refs\/tags\//}" -d "spec"
- name: "📥 Spec definition download"
uses: actions/download-artifact@v4

View file

@ -1 +0,0 @@
Add `m.topic` content block to enable rich text in `m.room.topic` events as per [MSC3765](https://github.com/matrix-org/matrix-spec-proposals/pull/3765).

View file

@ -1 +0,0 @@
"Public" rooms in profile look-ups are defined through their join rule and history visibility.

View file

@ -1 +0,0 @@
"Public" rooms in user directory queries are defined through their join rule and history visibility.

View file

@ -1 +0,0 @@
"Public" rooms with respect to call invites are defined through their join rule.

View file

@ -1 +0,0 @@
"Public" rooms have no specific meaning with respect to moderation policy lists.

View file

@ -1 +0,0 @@
"Public" rooms with respect to presence are defined through their join rule.

View file

@ -1 +0,0 @@
Clarify that Well-Known URIs are available on the server name's hostname. Contributed by @HarHarLinks.

View file

@ -1 +0,0 @@
Replace Hugo shortcodes in OpenAPI output.

View file

@ -1 +0,0 @@
Fix the historical info box when generating the historical spec in CI.

View file

@ -1 +0,0 @@
Add a note to the invite endpoints that invites to local users may be received twice over federation if the homeserver is already in the room.

View file

@ -1 +0,0 @@
Add `m.topic` content block to enable rich text in `m.room.topic` events as per [MSC3765](https://github.com/matrix-org/matrix-spec-proposals/pull/3765).

View file

@ -1 +0,0 @@
Clarify that auth event of `content.join_authorised_via_users_server` is only necessary for `m.room.member` with a `membership` of `join`.

View file

@ -1 +0,0 @@
Clarify that Well-Known URIs are available on the server name's hostname. Contributed by @HarHarLinks.

View file

@ -371,23 +371,15 @@ valid data was obtained, but no server is available to serve the client.
No further guess should be attempted and the user should make a
conscientious decision what to do next.
### Well-known URIs
Matrix facilitates automatic discovery for the Client-Server API base URL and more via the
[RFC 8615](https://datatracker.ietf.org/doc/html/rfc8615) "Well-Known URI" method.
This method uses JSON files at a predetermined location on the root path `/.well-known/` to
specify parameter values.
### Well-known URI
{{% boxes/note %}}
Diverging from the rest of the endpoints in the Client-Server spec, these files can not be provided
on the base URL that the Client-Server API is reachable on, as it is yet to be discovered.
Instead, they can be reached via HTTPS on the [server name](/appendices/#server-name)'s hostname as domain.
Servers hosting the `.well-known` JSON file SHOULD offer CORS headers,
as per the [CORS](#web-browser-clients) section in this specification.
{{% /boxes/note %}}
The flow for auto-discovery is as follows:
The `.well-known` method uses a JSON file at a predetermined location to
specify parameter values. The flow for this method is as follows:
1. Extract the [server name](/appendices/#server-name) from the user's Matrix ID by splitting the
Matrix ID at the first colon.
@ -423,17 +415,10 @@ The flow for auto-discovery is as follows:
{{% http-api spec="client-server" api="wellknown" %}}
{{% http-api spec="client-server" api="support" %}}
### API Versions
Upon connecting, the Matrix client and server need to negotiate which version of the specification
they commonly support, as the API evolves over time. The server advertises its supported versions
and optionally unstable features to the client, which can then go on to make requests to the
endpoints it supports.
{{% http-api spec="client-server" api="versions" %}}
{{% http-api spec="client-server" api="support" %}}
## Client Authentication
Most API endpoints require the user to identify themselves by presenting
@ -2866,15 +2851,10 @@ re-invited.
#### Server behaviour
Homeservers MUST at a minimum allow profile look-up for users who are
visible to the requester based on their membership in rooms known to the
homeserver. This means:
Homeservers MUST at a minimum allow profile look-up for:
- users that share a room with the requesting user
- users who are joined to rooms known to the homeserver that have a
`public` [join rule](#mroomjoin_rules)
- users who are joined to rooms known to the homeserver that have a
`world_readable` [history visibility](#room-history-visibility)
- users that reside in public rooms known to the homeserver
In all other cases, homeservers MAY deny profile look-up by responding with
403 and an error code of `M_FORBIDDEN`.

View file

@ -18,9 +18,8 @@ the entity making the decisions on filtering is best positioned to
interpret the rules how it sees fit.
Moderation policy lists are stored as room state events. There are no
restrictions on how the rooms can be configured in terms of
[join rules](#mroomjoin_rules), [history visibility](#room-history-visibility),
encryption, etc.
restrictions on how the rooms can be configured (they could be public,
private, encrypted, etc).
There are currently 3 kinds of entities which can be affected by rules:
`user`, `server`, and `room`. All 3 are described with

View file

@ -68,7 +68,5 @@ will cause the server to automatically set their presence to `online`.
#### Security considerations
Presence information is published to all users who share a room with the
target user. If the target user is a member of a room with a `public`
[join rule](#mroomjoin_rules), any other user in the federation is
able to gain access to the target user's presence. This could be undesirable.
Presence information is shared with all users who share a room with the
target user. In large public rooms this could be undesirable.

View file

@ -26,10 +26,9 @@ on certain keys of certain event types.
The supported keys to search over are:
- `content.body` in [`m.room.message`](/client-server-api/#mroommessage)
- `content.name` in [`m.room.name`](/client-server-api/#mroomname)
- In [`m.room.topic`](/client-server-api/#mroomtopic), `content.topic`
as well as the `body` of the `text/plain` representation in `content['m.topic']`.
- `content.body` in `m.room.message`
- `content.name` in `m.room.name`
- `content.topic` in `m.room.topic`
The search will *not* include rooms that are end to end encrypted.

View file

@ -202,13 +202,11 @@ specific user, and should be set to the Matrix user ID of that user. Invites
without an `invitee` field are defined to be intended for any member of the
room other than the sender of the event.
Clients should consider an incoming call if they see a non-expired invite event
where the `invitee` field is either absent or equal to their user's Matrix ID.
They should, however, evaluate whether or not to ring based on their user's trust
relationship with the callers and/or where the call was placed. As a starting
point, it is RECOMMENDED that clients ignore call invites in rooms with a
[join rule](#mroomjoin_rules) of `public`. When clients suppress ringing for an
incoming call invite, they SHOULD still display the call invite in the room and
Clients should consider an incoming call if they see a non-expired invite event where the `invitee` field is either
absent or equal to their user's Matrix ID, however they should evaluate whether or not to ring based on their
user's trust relationship with the callers and/or where the call was placed. As a starting point, it is
suggested that clients ignore call invites from users in public rooms. It is strongly recommended that
when clients do not ring for an incoming call invite, they still display the call invite in the room and
annotate that it was ignored.
##### Glare

View file

@ -119,8 +119,7 @@ to send. The process overall is as follows:
server must present a valid certificate for the hostname.
3. If the hostname is not an IP literal, a regular HTTPS request is
made to `https://<hostname>/.well-known/matrix/server` (according to
[RFC 8615](https://datatracker.ietf.org/doc/html/rfc8615)), expecting
made to `https://<hostname>/.well-known/matrix/server`, expecting
the schema defined later in this section. 30x redirects should be
followed, however redirection loops should be avoided. Responses
(successful or otherwise) to the `/.well-known` endpoint should be
@ -544,8 +543,8 @@ the following subset of the room state:
`third_party_invite` property, the current
`m.room.third_party_invite` event with `state_key` matching
`content.third_party_invite.signed.token`, if any.
- If `membership` is `join`, `content.join_authorised_via_users_server`
is present, and the [room version supports restricted rooms](/rooms/#feature-matrix),
- If `content.join_authorised_via_users_server` is present,
and the [room version supports restricted rooms](/rooms/#feature-matrix),
then the `m.room.member` event with `state_key` matching
`content.join_authorised_via_users_server`.

View file

@ -109,17 +109,15 @@ paths:
name:
type: string
description: |-
If this is included, an [`m.room.name`](/client-server-api/#mroomname) event
will be sent into the room to indicate the name for the room.
This overwrites any [`m.room.name`](/client-server-api/#mroomname)
event in `initial_state`.
If this is included, an `m.room.name` event will be sent
into the room to indicate the name of the room. See Room
Events for more information on `m.room.name`.
topic:
type: string
description: |-
If this is included, an [`m.room.topic`](/client-server-api/#mroomtopic)
event with a `text/plain` mimetype will be sent into the room
to indicate the topic for the room. This overwrites any
[`m.room.topic`](/client-server-api/#mroomtopic) event in `initial_state`.
If this is included, an `m.room.topic` event will be sent
into the room to indicate the topic for the room. See Room
Events for more information on `m.room.topic`.
invite:
type: array
description: |-

View file

@ -33,9 +33,7 @@ properties:
example: "!abcdefg:example.org"
topic:
type: string
description: |-
The plain text topic of the room. Omitted if no `text/plain` mimetype
exists in [`m.room.topic`](/client-server-api/#mroomtopic).
description: The topic of the room, if any.
example: "All things general"
world_readable:
type: boolean

View file

@ -22,12 +22,9 @@ paths:
description: |-
Gets server admin contact and support page of the domain.
{{% boxes/note %}}
Like the [well-known discovery URI](/client-server-api/#well-known-uris),
this endpoint should be accessed with the hostname of the homeserver's
[server name](/appendices/#server-name) by making a
Like the [well-known discovery URI](/client-server-api/#well-known-uri),
this should be accessed with the hostname of the homeserver by making a
GET request to `https://hostname/.well-known/matrix/support`.
{{% /boxes/note %}}
Note that this endpoint is not necessarily handled by the homeserver.
It may be served by another webserver, used for discovering support

View file

@ -20,17 +20,10 @@ paths:
post:
summary: Searches the user directory.
description: |-
Performs a search for users. The homeserver may determine which
subset of users are searched. However, the homeserver MUST at a
minimum consider users who are visible to the requester based
on their membership in rooms known to the homeserver. This means:
- users that share a room with the requesting user
- users who are joined to rooms known to the homeserver that have a
`public` [join rule](#mroomjoin_rules)
- users who are joined to rooms known to the homeserver that have a
`world_readable` [history visibility](#room-history-visibility)
Performs a search for users. The homeserver may
determine which subset of users are searched, however the homeserver
MUST at a minimum consider the users the requesting user shares a
room with and those who reside in public rooms (known to the homeserver).
The search MUST consider local users to the homeserver, and SHOULD
query remote users as part of the search.

View file

@ -26,12 +26,6 @@ paths:
suitably namespaced for each application and reduces the risk of
clashes.
{{% boxes/note %}}
This endpoint should be accessed with the hostname of the homeserver's
[server name](/appendices/#server-name) by making a
GET request to `https://hostname/.well-known/matrix/client`.
{{% /boxes/note %}}
Note that this endpoint is not necessarily handled by the homeserver,
but by another webserver, to be used for discovering the homeserver URL.
operationId: getWellknown

View file

@ -20,7 +20,7 @@ paths:
put:
summary: Invites a remote user to a room
description: |-
Invites a remote user to a room. Once the event has been signed by both the inviting
Invites a remote user to a room. Once the event has been signed by both the inviting
homeserver and the invited homeserver, it can be sent to all of the servers in the
room by the inviting homeserver.
@ -32,10 +32,6 @@ paths:
[room version specification](/rooms) for precise event formats. **The request and response
bodies here describe the common event fields in more detail and may be missing other
required fields for a PDU.**
Also note that if the remote homeserver is already in the room, it will receive the
invite event twice; once through this endpoint, and again through a [federation
transaction](/server-server-api/#transactions).
operationId: sendInviteV1
security:
- signedRequest: []

View file

@ -24,7 +24,7 @@ paths:
This API is nearly identical to the v1 API with the exception of the request
body being different, and the response format fixed.
Invites a remote user to a room. Once the event has been signed by both the inviting
Invites a remote user to a room. Once the event has been signed by both the inviting
homeserver and the invited homeserver, it can be sent to all of the servers in the
room by the inviting homeserver.
@ -36,10 +36,6 @@ paths:
[room version specification](/rooms) for precise event formats. **The request and response
bodies here describe the common event fields in more detail and may be missing other
required fields for a PDU.**
Also note that if the remote homeserver is already in the room, it will receive the
invite event twice; once through this endpoint, and again through a [federation
transaction](/server-server-api/#transactions).
operationId: sendInviteV2
security:
- signedRequest: []

View file

@ -24,12 +24,6 @@ paths:
Gets information about the delegated server for server-server communication
between Matrix homeservers. Servers should follow 30x redirects, carefully
avoiding redirect loops, and use normal X.509 certificate validation.
{{% boxes/note %}}
This endpoint should be accessed with the hostname of the homeserver's
[server name](/appendices/#server-name) by making a
GET request to `https://hostname/.well-known/matrix/server`.
{{% /boxes/note %}}
operationId: getWellKnown
responses:
"200":

View file

@ -3,14 +3,6 @@
"type": "m.room.topic",
"state_key": "",
"content": {
"m.topic": {
"m.text": [ {
"mimetype": "text/html",
"body": "An <em>interesting</em> room topic"
}, {
"body": "An interesting room topic"
}]
},
"topic": "An interesting room topic"
"topic": "A room topic"
}
}

View file

@ -1,28 +0,0 @@
type: array
description: |-
An ordered array of textual representations in different mimetypes.
Senders SHOULD specify at least one representation and SHOULD always
include a plaintext representation.
Receivers SHOULD use the first representation in the array that
they understand.
title: TextContentBlock
items:
type: object
title: TextualRepresentation
properties:
mimetype:
type: string
description: The mimetype. Defaults to `text/plain` if omitted.
example: "text/html"
body:
type: string
description: |-
The string content.
Clients SHOULD validate and sanitize the content as they do
for rich content associated with [`msgtype`](/client-server-api/#mroommessage-msgtypes)
of [`m.room.message`](/client-server-api/#mroommessage).
required:
- body

View file

@ -1,41 +1,20 @@
---
allOf:
- $ref: core-event-schema/state_event.yaml
description: |-
A topic is a short message detailing what is currently being discussed
in the room. It can also be used as a way to display extra information
about the room, which may not be suitable for the room name. The room
topic can also be set when creating a room using
[`/createRoom`](client-server-api/#post_matrixclientv3createroom), either
with the `topic` key or by specifying a full event in `initial_state`.
description: |-
A topic is a short message detailing what is currently being discussed in the room.
It can also be used as a way to display extra information about the room, which may not
be suitable for the room name.
The room topic can also be set when creating a room using `/createRoom` with the `topic` key.'
If the `topic` property is absent, null, or empty then the topic is unset. In other words,
an empty `topic` property effectively resets the room to having no topic.
In order to prevent formatting abuse in room topics, clients SHOULD
limit the length of topics during both entry and display, for instance,
by capping the number of displayed lines. Additionally, clients SHOULD
ignore things like headings and enumerations (or format them as regular
text).
properties:
content:
properties:
topic:
description: |-
The topic in plain text.
This SHOULD duplicate the content of the `text/plain`
representation in `m.topic` if any exists.
description: The topic text.
type: string
m.topic:
type: object
title: TopicContentBlock
x-addedInMatrixVersion: "1.15"
description: |-
Textual representation of the room topic in different mimetypes.
properties:
m.text:
$ref: components/m_text_content_block.yaml
required:
- topic
type: object

View file

@ -32,35 +32,6 @@ import yaml
scripts_dir = os.path.dirname(os.path.abspath(__file__))
api_dir = os.path.join(os.path.dirname(scripts_dir), "data", "api")
# Finds a Hugo shortcode in a string.
#
# A shortcode is defined as (newlines and whitespaces for presentation purpose):
#
# {{%
# <zero or more whitespaces>
# <name of shortcode>
# (optional <one or more whitespaces><list of parameters>)
# <zero or more whitespaces>
# %}}
#
# With:
#
# * <name of shortcode>: any word character and `-` and `/`. `re.ASCII` is used to only match
# ASCII characters in the name.
# * <list of parameters>: any character except `}`, must not start or end with a
# whitespace.
shortcode_regex = re.compile(r"""\{\{\% # {{%
\s* # zero or more whitespaces
(?P<name>[\w/-]+) # name of shortcode
(?:\s+(?P<params>[^\s\}][^\}]+[^\s\}]))? # optional list of parameters
\s* # zero or more whitespaces
\%\}\} # %}}""", re.ASCII | re.VERBOSE)
# Parses the parameters of a Hugo shortcode.
#
# For simplicity, this currently only supports the `key="value"` format.
shortcode_params_regex = re.compile(r"(?P<key>\w+)=\"(?P<value>[^\"]+)\"", re.ASCII)
def prefix_absolute_path_references(text, base_url):
"""Adds base_url to absolute-path references.
@ -73,90 +44,17 @@ def prefix_absolute_path_references(text, base_url):
"""
return text.replace("](/", "]({}/".format(base_url))
def replace_match(match, replacement):
"""Replaces the regex match by the replacement in the text."""
return match.string[:match.start()] + replacement + match.string[match.end():]
def replace_shortcode(shortcode):
"""Replaces the shortcode by a Markdown fallback in the text.
The supported shortcodes are:
* boxes/note, boxes/rationale, boxes/warning
* added-in, changed-in
All closing tags (`{{ /shortcode }}`) are replaced with the empty string.
"""
if shortcode['name'].startswith("/"):
# This is the end of the shortcode, just remove it.
return replace_match(shortcode, "")
# Parse the parameters of the shortcode
params = {}
if shortcode['params']:
for param in shortcode_params_regex.finditer(shortcode['params']):
if param['key']:
params[param['key']] = param['value']
match shortcode['name']:
case "boxes/note":
return replace_match(shortcode, "**NOTE:** ")
case "boxes/rationale":
return replace_match(shortcode, "**RATIONALE:** ")
case "boxes/warning":
return replace_match(shortcode, "**WARNING:** ")
case "added-in":
version = params['v']
if not version:
raise ValueError("Missing parameter `v` for `added-in` shortcode")
return replace_match(shortcode, f"**[Added in `v{version}`]** ")
case "changed-in":
version = params['v']
if not version:
raise ValueError("Missing parameter `v` for `changed-in` shortcode")
return replace_match(shortcode, f"**[Changed in `v{version}`]** ")
case _:
raise ValueError("Unknown shortcode", shortcode['name'])
def find_and_replace_shortcodes(text):
"""Finds Hugo shortcodes and replaces them by a Markdown fallback.
The supported shortcodes are:
* boxes/note, boxes/rationale, boxes/warning
* added-in, changed-in
"""
# We use a `while` loop with `search` instead of a `for` loop with
# `finditer`, because as soon as we start replacing text, the
# indices of the match are invalid.
while shortcode := shortcode_regex.search(text):
text = replace_shortcode(shortcode)
return text
def edit_descriptions(node, base_url):
"""Finds description nodes and apply fixes to them.
The fixes that are applied are:
* Make links absolute
* Replace Hugo shortcodes
"""
def edit_links(node, base_url):
"""Finds description nodes and makes any links in them absolute."""
if isinstance(node, dict):
for key in node:
if isinstance(node[key], str):
node[key] = prefix_absolute_path_references(node[key], base_url)
node[key] = find_and_replace_shortcodes(node[key])
else:
edit_descriptions(node[key], base_url)
edit_links(node[key], base_url)
elif isinstance(node, list):
for item in node:
edit_descriptions(item, base_url)
edit_links(item, base_url)
parser = argparse.ArgumentParser(
"dump-openapi.py - assemble the OpenAPI specs into a single JSON file"
@ -266,7 +164,7 @@ for filename in os.listdir(selected_api_dir):
if untagged != 0:
print("{} untagged operations, you may want to look into fixing that.".format(untagged))
edit_descriptions(output, base_url)
edit_links(output, base_url)
print("Generating %s" % output_file)