Compare commits

..

4 commits

Author SHA1 Message Date
Tulir Asokan c68e687a8c
Define where to find via for following tombstone/predecessor (#2352)
Some checks are pending
Spec / 🔎 Validate OpenAPI specifications (push) Waiting to run
Spec / 🔎 Check Event schema examples (push) Waiting to run
Spec / 🔎 Check OpenAPI definitions examples (push) Waiting to run
Spec / 🔎 Check JSON Schemas inline examples (push) Waiting to run
Spec / ⚙️ Calculate baseURL for later jobs (push) Waiting to run
Spec / 🐍 Build OpenAPI definitions (push) Blocked by required conditions
Spec / 📢 Run towncrier for changelog (push) Waiting to run
Spec / 📖 Build the spec (push) Blocked by required conditions
Spec / 🔎 Validate generated HTML (push) Blocked by required conditions
Spec / 📖 Build the historical backup spec (push) Blocked by required conditions
Spec / Create release (push) Blocked by required conditions
Spell Check / Spell Check with Typos (push) Waiting to run
Signed-off-by: Tulir Asokan <tulir@maunium.net>
Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com>
2026-04-21 22:20:15 +00:00
Kévin Commaille 0cff46c7d4
Upgrade docsy to v0.14.3 (#2346)
Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2026-04-21 22:43:08 +01:00
timedout 09e745bcdf
Specify replaces_state in ClientEvent (#2345)
Signed-off-by: timedout <git@nexy7574.co.uk>
2026-04-21 21:53:17 +01:00
timedout fafd11f809
Clarify how multiple signatures should be handled during verification (#2341)
Signed-off-by: timedout <git@nexy7574.co.uk>
2026-04-21 21:24:28 +01:00
14 changed files with 59 additions and 36 deletions

View file

@ -1,7 +1,7 @@
name: "Spec"
env:
HUGO_VERSION: 0.153.3
HUGO_VERSION: 0.155.3
PYTHON_VERSION: 3.13
NODE_VERSION: 24

View file

@ -61,7 +61,7 @@ place after an MSC has been accepted, not as part of a proposal itself.
1. Install the extended version (often the OS default) of Hugo:
<https://gohugo.io/getting-started/installing>. Note that at least Hugo
v0.146.0 is required.
v0.155.0 is required.
Alternatively, use the Docker image at
https://hub.docker.com/r/klakegg/hugo/. (The "extended edition" is required

View file

@ -0,0 +1 @@
Specify `unsigned.replaces_state` in client-formatted events. Contributed by @nexy7574.

View file

@ -0,0 +1 @@
Clarify how to find `via` parameter when following room upgrades.

View file

@ -0,0 +1 @@
Upgrade Docsy theme to v0.14.3.

View file

@ -0,0 +1 @@
Clarify how multiple signatures should be handled during signature verification. Contributed by @nexy7574.

View file

@ -20,6 +20,10 @@ old room. Another approach may be to virtually merge the rooms such that
the old room's timeline seamlessly continues into the new timeline
without the user having to jump between the rooms.
When joining a room using the room ID in an `m.room.tombstone` event or
`predecessor` field on `m.room.create`, clients SHOULD parse the event
`sender` and use the resulting server name as a `via` parameter.
{{% http-api spec="client-server" api="room_upgrades" %}}
#### Server behaviour

View file

@ -1484,34 +1484,30 @@ the Policy Server for a signature too.
When a server receives an event over federation from another server, the
receiving server should check the hashes and signatures on that event.
First the signature is checked. The event is redacted following the
[redaction
algorithm](/client-server-api#redactions), and
the resultant object is checked for a signature from the originating
First the signatures are checked. The event is redacted following the
[redaction algorithm](/client-server-api#redactions), and
the resultant object is checked for signatures from the originating
server, following the algorithm described in [Checking for a
signature](/appendices#checking-for-a-signature). Note that this
step should succeed whether we have been sent the full event or a
redacted copy.
The signatures expected on an event are:
For room versions 3 and later, unless the event is a 3rd party invite, only the
signature(s) from the originating server (the server the `sender` belongs to)
are required for verification. Room versions 1 and 2 also require that a
signature is present from the domain in the `event_id`, if it differs from the
originating server. If a signature is from an unknown or expired key, it is
skipped.
- The `sender`'s server, unless the invite was created as a result of
3rd party invite. The sender must already match the 3rd party
invite, and the server which actually sends the event may be a
different server.
- For room versions 1 and 2, the server which created the `event_id`.
Other room versions do not track the `event_id` over federation and
therefore do not need a signature from those servers.
If the event is a 3rd party invite, the sender must already match the 3rd party
invite, and the server which actually sends the event may be a different
server.
If the signature is found to be valid, the expected content hash is
calculated as described below. The content hash in the `hashes` property
of the received event is base64-decoded, and the two are compared for
equality.
If the hash check fails, then it is assumed that this is because we have
only been given a redacted version of the event. To enforce this, the
receiving server should use the redacted copy it calculated rather than
the full copy it received.
Only signatures from known server keys are validated here. Any unknown keys are ignored.
In particular, the [policy server key](#validating-policy-server-signatures) is not
expected to be published and therefore should be skipped at this stage.
Additionally, any keys that are known to have expired prior to the event's
`origin_server_ts` are ignored.
{{% boxes/note %}}
{{% added-in v="1.18" %}}
@ -1519,6 +1515,16 @@ Events sent in rooms with [Policy Servers](#policy-servers) have [additional](#v
signature validation requirements.
{{% /boxes/note %}}
If all signatures from known unexpired keys from the originating server(s) are
found to be valid, the expected content hash is calculated as described below.
The content hash in the `hashes` property of the received event is base64-decoded,
and the two are compared for equality.
If the hash check fails, then it is assumed that this is because we have
only been given a redacted version of the event. To enforce this, the
receiving server should use the redacted copy it calculated rather than
the full copy it received.
### Calculating the reference hash for an event
The *reference hash* of an event covers the essential fields of an

View file

@ -105,9 +105,10 @@ properties:
type: string
prev_content:
description: |
The previous `content` for this event. This field is generated
by the local homeserver, and is only returned if the event is a state event,
and the client has permission to see the previous content.
The `content` of the previous state event that was replaced by this event.
This field is generated by the local homeserver, and is only returned if
the event is a state event, and the client has permission to see the
previous event.
x-changedInMatrixVersion:
"1.2": |
Previously, this field was specified at the top level of returned
@ -117,6 +118,13 @@ properties:
this.
title: EventContent
type: object
replaces_state:
description: |
The event ID of the state event replaced by this event. This field is generated
by the local homeserver, and is only returned if the event is a state event.
Unlike `prev_content`, this field is included regardless of history visibility.
type: string
x-addedInMatrixVersion: "1.19"
membership:
description: |
The room membership of the user making the request, at the time of the event.

2
go.mod
View file

@ -2,4 +2,4 @@ module github.com/matrix-org/matrix-spec
go 1.12
require github.com/matrix-org/docsy v0.0.0-20260106184755-71d103ebb20a // indirect
require github.com/matrix-org/docsy v0.0.0-20260331222549-f318855c7886 // indirect

4
go.sum
View file

@ -1,4 +1,4 @@
github.com/FortAwesome/Font-Awesome v0.0.0-20241216213156-af620534bfc3/go.mod h1:IUgezN/MFpCDIlFezw3L8j83oeiIuYoj28Miwr/KUYo=
github.com/matrix-org/docsy v0.0.0-20260106184755-71d103ebb20a h1:WB3unuZJy7ewAf33sxbtEwYnC+i+Jt1sJpAR3BtzvEo=
github.com/matrix-org/docsy v0.0.0-20260106184755-71d103ebb20a/go.mod h1:mdn1m5HJug6ZddQgrOyCrXNegbtdl5evHiqqbEQLzdI=
github.com/matrix-org/docsy v0.0.0-20260331222549-f318855c7886 h1:+Qowx/XQ8sQGTeVyoyIpcwOcdlB+Ft6x+QJkJEPDIpg=
github.com/matrix-org/docsy v0.0.0-20260331222549-f318855c7886/go.mod h1:mdn1m5HJug6ZddQgrOyCrXNegbtdl5evHiqqbEQLzdI=
github.com/twbs/bootstrap v5.3.8+incompatible/go.mod h1:fZTSrkpSf0/HkL0IIJzvVspTt1r9zuf7XlZau8kpcY0=

View file

@ -1,7 +1,7 @@
{{- /*
This is a heading render hook (https://gohugo.io/render-hooks/headings/)
using Docsy's heading self-links hook (https://www.docsy.dev/docs/adding-content/navigation/#heading-self-links).
using Docsy's heading self-links hook (https://www.docsy.dev/docs/content/navigation/#heading-self-links).
This is used when a heading is encountered in markdown content to generate
the HTML for that heading. A self-link anchor is added at the end of the

View file

@ -2,7 +2,6 @@
A copy of the navbar.html partial in Docsy, modified to:
* remove `data-bs-theme` at L20, otherwise the title disappears on hover.
* replace the site title with "specification" at L31.
* include the spec version from the config at L34-35, which is calculated
using an inline `version-string` partial.
@ -16,7 +15,8 @@
{{ $baseURL := urls.Parse $.Site.Params.Baseurl -}}
<nav class="td-navbar js-navbar-scroll
{{- if $cover }} td-navbar-cover {{- end }}">
{{- if $cover }} td-navbar-cover td-navbar-transparent {{- end }}"
{{- if eq (.Param "ui.navbar_theme") "dark" }} data-bs-theme="dark" {{- end }}>
<div class="td-navbar-container container-fluid flex-column flex-md-row">
<a class="navbar-brand" href="{{ .Site.Home.RelPermalink }}">
{{- /**/ -}}
@ -35,7 +35,7 @@
<span class="navbar-version"> &mdash; {{ partial "version-string" . }}</span>
{{- /**/ -}}
</a>
<div class="td-navbar-nav-scroll td-navbar-nav-scroll--indicator" id="main_navbar">
<div class="td-navbar__main td-navbar-nav-scroll td-navbar-nav-scroll--indicator" id="main_navbar">
<div class="scroll-indicator scroll-left"></div>
<ul class="navbar-nav">
{{ $p := . -}}
@ -80,7 +80,7 @@
</ul>
<div class="scroll-indicator scroll-right"></div>
</div>
<div class="d-none d-lg-block td-navbar__search">
<div class="td-navbar__search d-none d-lg-block">
{{ partial "search-input.html" . }}
</div>
</div>

View file

@ -14,6 +14,7 @@
{{ $cacheSidebar := .cacheSidebar -}}
{{ with $context -}}
{{/* When the sidebar is cached, "active" class is set client side. */ -}}
{{ $shouldDelayActive := $cacheSidebar -}}
@ -174,7 +175,7 @@
{{- end -}}
<span class="
{{- if $active }}td-sidebar-nav-active-item{{ end -}}
{{- if and $s.Params.sidebar_root_for site.Params.ui.sidebar_root_enabled }} td-sidebar-root-up-icon{{ end -}}
{{- if and $treeRoot $s.Params.sidebar_root_for site.Params.ui.sidebar_root_enabled }} td-sidebar-root-up-icon{{ end -}}
">
{{- $s.LinkTitle -}}
</span></a>
@ -190,4 +191,4 @@
</ul>
{{- end }}
</li>
{{- end }}
{{- end -}}