Compare commits

...

36 commits

Author SHA1 Message Date
Johannes Marbach 3b9253da9a
Merge e3ca1ba2b8 into fafd11f809 2026-04-21 21:26:04 +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
Kévin Commaille 486a8f8764
Improvements to "Events" section (#2349)
Some checks failed
Spec / 🔎 Validate OpenAPI specifications (push) Has been cancelled
Spec / 🔎 Check Event schema examples (push) Has been cancelled
Spec / 🔎 Check OpenAPI definitions examples (push) Has been cancelled
Spec / 🔎 Check JSON Schemas inline examples (push) Has been cancelled
Spec / ⚙️ Calculate baseURL for later jobs (push) Has been cancelled
Spec / 📢 Run towncrier for changelog (push) Has been cancelled
Spell Check / Spell Check with Typos (push) Has been cancelled
Spec / 🐍 Build OpenAPI definitions (push) Has been cancelled
Spec / 📖 Build the spec (push) Has been cancelled
Spec / 🔎 Validate generated HTML (push) Has been cancelled
Spec / 📖 Build the historical backup spec (push) Has been cancelled
Spec / Create release (push) Has been cancelled
* Clarify string formats of event endpoints and schemas

Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>

* Fix links to endpoints

Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>

* Add changelog

Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>

---------

Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2026-04-14 21:30:06 -04:00
Johannes Marbach e3ca1ba2b8 Load main results in pages of 3
Signed-off-by: Johannes Marbach <n0-0ne+github@mailbox.org>
2026-03-24 15:48:57 +01:00
Johannes Marbach 4f0eba6355 Fix broken close button
Signed-off-by: Johannes Marbach <n0-0ne+github@mailbox.org>
2026-03-24 15:17:22 +01:00
Johannes Marbach 8fb380d465 Load and render main results one by one
Signed-off-by: Johannes Marbach <n0-0ne+github@mailbox.org>
2026-03-24 15:11:52 +01:00
Johannes Marbach 754e9c82b8 Document why we cannot use wasm-unsafe-eval yet
Signed-off-by: Johannes Marbach <n0-0ne+github@mailbox.org>
2026-03-24 11:34:59 +01:00
Johannes Marbach 86675ad4a4 Only start searching upon hitting Enter
Signed-off-by: Johannes Marbach <n0-0ne+github@mailbox.org>
2026-03-24 11:32:27 +01:00
Johannes Marbach 49fec082f5 Use empty and append to insert the html
Signed-off-by: Johannes Marbach <n0-0ne+github@mailbox.org>
2026-03-24 11:24:13 +01:00
Johannes Marbach 0cc8ee38d5 Use btn-link style for expander
Signed-off-by: Johannes Marbach <n0-0ne+github@mailbox.org>
2026-03-24 11:22:21 +01:00
Johannes Marbach af1e9382ca Use a <button> for the closer
Signed-off-by: Johannes Marbach <n0-0ne+github@mailbox.org>
2026-03-24 11:16:37 +01:00
Johannes Marbach 84b5e98f25 Switch to (outline) buttons for the expander
Signed-off-by: Johannes Marbach <n0-0ne+github@mailbox.org>
2026-03-24 11:07:57 +01:00
Johannes Marbach 0cf8fca4da Unwrap jQuery object
Signed-off-by: Johannes Marbach <n0-0ne+github@mailbox.org>
2026-03-24 10:59:39 +01:00
Johannes Marbach bccbff878d Fix relative path to pagefind script
Signed-off-by: Johannes Marbach <n0-0ne+github@mailbox.org>
2026-03-24 10:56:03 +01:00
Johannes Marbach 308bcfd20e Pin pagefind version
Signed-off-by: Johannes Marbach <n0-0ne+github@mailbox.org>
2026-03-24 10:41:01 +01:00
Johannes Marbach 1f5d598754
Fix typo in HTML ID
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-03-24 10:23:11 +01:00
Johannes Marbach 59c7282ddb
Use correct pluralisation for zero results
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-03-24 10:22:03 +01:00
Johannes Marbach f49668d722
Fix typo
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-03-24 10:20:31 +01:00
Johannes Marbach 1c2e58d66f
Use relative path for pagefind JS
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-03-24 10:17:37 +01:00
Johannes Marbach de25ba5265 Document how to build the search index locally
Signed-off-by: Johannes Marbach <n0-0ne+github@mailbox.org>
2026-03-20 14:14:45 +01:00
Johannes Marbach e9a29f27dc Fix CSP
Signed-off-by: Johannes Marbach <n0-0ne+github@mailbox.org>
2026-03-20 14:05:19 +01:00
Andrew Morgan 2fd3c28a7a Attempt to fix page jump upon expanding search results 2026-03-20 12:54:14 +00:00
Johannes Marbach 15f36d1934 Only use plural of 'results' when actually necessary
Signed-off-by: Johannes Marbach <n0-0ne+github@mailbox.org>
2026-03-20 13:44:11 +01:00
Johannes Marbach 9edb9b3e5b Add triangle indicator on expander buttons and remove left margins
Signed-off-by: Johannes Marbach <n0-0ne+github@mailbox.org>
2026-03-20 13:38:12 +01:00
Johannes Marbach 94fca47a7d Add total number of subresults per main result
Signed-off-by: Johannes Marbach <n0-0ne+github@mailbox.org>
2026-03-20 13:37:41 +01:00
Johannes Marbach a56969149f Stop bluring the input
Signed-off-by: Johannes Marbach <n0-0ne+github@mailbox.org>
2026-03-20 13:18:36 +01:00
Johannes Marbach 72205be3dc Set cursor style and a11y label on close button
Signed-off-by: Johannes Marbach <n0-0ne+github@mailbox.org>
2026-03-20 13:13:05 +01:00
Johannes Marbach 8e383835b9 Toggle expander label between show and hide
Signed-off-by: Johannes Marbach <n0-0ne+github@mailbox.org>
2026-03-20 13:08:53 +01:00
Johannes Marbach 92b7e714e7 Add loading spinner while the search is running
Signed-off-by: Johannes Marbach <n0-0ne+github@mailbox.org>
2026-03-20 12:52:01 +01:00
Johannes Marbach 6f05c2c78e Improve search field placeholder
Signed-off-by: Johannes Marbach <n0-0ne+github@mailbox.org>
2026-03-19 15:13:29 +01:00
Johannes Marbach 9a1b7ffa7c Group subresults
Signed-off-by: Johannes Marbach <n0-0ne+github@mailbox.org>
2026-03-19 08:50:45 +01:00
Johannes Marbach e679a00720 Override search-input template
Signed-off-by: Johannes Marbach <n0-0ne+github@mailbox.org>
2026-03-17 14:16:24 +01:00
Johannes Marbach 7dfd746e06 Remove leftovers
Signed-off-by: Johannes Marbach <n0-0ne+github@mailbox.org>
2026-03-17 13:21:42 +01:00
Johannes Marbach 77f7e8104a Merge branch 'main' into johannes/page-search 2026-03-17 13:09:06 +01:00
Johannes Marbach b3d4f9a96e Change to using Pagefind via the built-in Docsy search UI
Signed-off-by: Johannes Marbach <n0-0ne+github@mailbox.org>
2026-03-17 13:04:43 +01:00
Johannes Marbach fdd2a9abe8 Add page search widget
Signed-off-by: Johannes Marbach <n0-0ne+github@mailbox.org>
2026-03-06 15:25:50 +01:00
36 changed files with 647 additions and 36 deletions

View file

@ -236,6 +236,10 @@ jobs:
run: |
tar -C "spec${baseURL}" --strip-components=1 -xzf openapi.tar.gz
- name: "🔍 pagefind indexing"
run: |
npm run pagefind -- --site "spec${baseURL}"
- name: "📦 Tarball creation"
run: |
cd spec

View file

@ -79,6 +79,18 @@ We use a highly customized [Docsy](https://www.docsy.dev/) theme for our generat
Awesome. If you're looking at making design-related changes to the spec site, please coordinate with us in
[#matrix-docs:matrix.org](https://matrix.to/#/#matrix-docs:matrix.org) before opening a PR.
## Page search
The spec uses [Pagefind](https://pagefind.app/) to provide a page search widget. To test this locally, you'll need to generate the
search index _after_ building the static site.
```
hugo build && npm run pagefind -- --site public && hugo serve
```
Note that while `hugo serve` supports hot reloading, changes made to the site content won't reflect in the search index without
rebuilding it.
## Building the specification
If for some reason you're not a CI/CD system and want to render a static version of the spec for yourself, follow the above

290
assets/js/offline-search.js Normal file
View file

@ -0,0 +1,290 @@
/*
Copyright 2026 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/*
Adapted from [1] to combine Docsy's built-in search UI with the Pagefind
search backend.
[1]: https://github.com/matrix-org/docsy/blob/71d103ebb20ace3d528178c4b6d92b6cc4f7fd53/assets/js/offline-search.js
*/
(function ($) {
"use strict";
$(document).ready(async function () {
// This is going to be loaded from ${deployment}/js/main.js so to use a relative path
// to the Pagefind script we need to navigate one level up.
const pagefind = await import("../pagefind/pagefind.js");
const $searchInput = $(".td-search input");
//
// Lazily initialise Pagefind only when the user is about to start a search.
//
$searchInput.focus(() => {
pagefind.init();
});
//
// Set up search input handler.
//
$searchInput.on("keypress", (event) => {
// Start searching only upon Enter.
if (event.which === 13) {
event.preventDefault();
render($(event.target));
return;
}
});
// Prevent reloading page by enter key on sidebar search.
$searchInput.closest("form").on("submit", () => {
return false;
});
//
// Callback for searching and rendering the results.
//
const render = async ($targetSearchInput) => {
//
// Dispose any existing popover.
//
disposePopover($targetSearchInput);
//
// Check if we need to do a search at all.
//
const searchQuery = $targetSearchInput.val();
if (searchQuery === "") {
return;
}
//
// Prepare the results popover.
//
const $html = $("<div>");
// Add the header and close button.
$html.append($("<div>")
.css({
display: "flex",
justifyContent: "space-between",
marginBottom: "1em",
})
.append($("<span>")
.text("Search results")
.css({ fontWeight: "bold" }))
.append($("<button>")
.addClass("td-offline-search-results__close-button")
.addClass("btn")
.addClass("btn-sm")
.addClass("btn-link")
.attr("type", "button")
.attr("aria-label", "Close")
.on("click", () => {
$targetSearchInput.val("");
disposePopover($targetSearchInput);
})
)
);
// Add the main search results body.
const $searchResultBody = $("<div>").css({
maxHeight: `calc(100vh - ${
$targetSearchInput.offset().top - $(window).scrollTop() + 180
}px)`,
overflowY: "auto",
});
$html.append($searchResultBody);
// Append a spinner while we're busy.
const $spinner = createSpinner();
$searchResultBody.append($spinner)
// Display the popover.
const popover = new bootstrap.Popover($targetSearchInput[0], {
content: $html[0],
html: true,
customClass: "td-offline-search-results",
placement: "bottom",
});
popover.show();
//
// Kick off the search, load the results and inject them into the popover.
//
const search = await pagefind.debouncedSearch(searchQuery);
if (search === null) {
// A more recent search call has been made, nothing to do.
return;
}
if (search.results.length === 0) {
$searchResultBody.append(
$("<p>").text(`No results found for query "${searchQuery}"`)
);
} else {
await loadAndRenderResults(search.results, 0, $spinner, $searchResultBody);
}
};
});
})(jQuery);
//
// Helpers
//
const disposePopover = ($targetSearchInput) => {
const popover = bootstrap.Popover.getInstance($targetSearchInput[0]);
if (popover !== null) {
popover.dispose();
}
}
const createSpinner = () => {
return $("<div>")
.addClass("spinner-container")
.append($("<div>")
.addClass("spinner-border")
.attr("role", "status")
.append($("<div>")
.addClass("visually-hidden")
.text("Loading...")))
.append($("<p>")
.text("Loading..."));
}
const loadAndRenderResults = async (results, offset, $spinner, $searchResultBody) => {
// Load and render the first three results and hide the remainder behind a
// button to not freeze the browser by loading results that may not be
// displayed.
const LIMIT = 3;
for (const [index, result] of results.entries()) {
if (index < LIMIT) {
// Insert a container for the result *before* the spinner. This
// will push down the spinner as new content is loaded and keep
// it at the end of the popover.
const $container = $("<div>");
$spinner.before($container);
renderResult(await result.data(), index + offset, $container);
} else if (index === LIMIT) {
const num_hidden_results = results.length - index;
const $loader = $("<button>")
.attr("type", "button")
.addClass("td-offline-search-results__expander-button")
.addClass("btn")
.addClass("btn-sm")
.addClass("btn-link")
.text(`Load more results from ${num_hidden_results} other ${pagesString(num_hidden_results)}`)
.on("click", async () => {
// Remove the button.
$loader.remove();
// Add a spinner while we're busy.
const $spinner = createSpinner();
$searchResultBody.append($spinner)
// Load and render the results.
await loadAndRenderResults(results.slice(LIMIT), LIMIT + offset, $spinner, $searchResultBody);
});
$spinner.before($loader)
}
}
// Remove the spinner now that everything was loaded.
$spinner.remove();
}
const renderResult = (data, index, $container) => {
// Add the main result's page title.
$container.append($("<div>")
.append($("<a>")
.css({
fontSize: "1.2rem",
})
.attr("href", data.url)
.text(data.meta.title))
.append($("<span>")
.addClass("text-body-secondary")
.text(` ${data.sub_results.length} ${resultsString(data.sub_results.length)}`)));
// Render the first 3 subresults per page and wrap the rest
// in a collapsed container.
const LIMIT = 3;
let $wrapper = null;
data.sub_results.forEach((s, index_s) => {
if (index_s === LIMIT) {
const num_hidden_results = data.sub_results.length - index_s;
const wrapper_id = `collapsible-subresults-${index}`;
const $action = $("<span>").text("▶ Show");
const $expander = $("<button>")
.attr("data-bs-toggle", "collapse")
.attr("data-bs-target", `#${wrapper_id}`)
.attr("aria-expanded", "false")
.attr("aria-controls", wrapper_id)
.attr("type", "button")
.addClass("td-offline-search-results__expander-button")
.addClass("btn")
.addClass("btn-sm")
.addClass("btn-link")
.append($action)
.append($("<span>").text(` ${num_hidden_results} more ${resultsString(num_hidden_results)} from ${data.meta.title}`));
$container.append($("<p>").append($expander));
$wrapper = $("<div>")
.addClass("collapse td-offline-search-results__subresults")
.attr("id", wrapper_id)
.on("hide.bs.collapse", _ => $action.text("▶ Show"))
.on("show.bs.collapse", _ => $action.text("▼ Hide"));
$container.append($wrapper);
}
const $entry = $("<div>")
.css("margin-top", "0.5rem");
$entry.append(
$("<a>")
.addClass("d-block")
.attr("href", s.url)
.text(s.title)
);
$entry.append($("<p>").html(s.excerpt));
if (index_s < LIMIT) {
$container.append($entry);
} else {
$wrapper.append($entry);
}
});
};
const resultsString = (n) => {
return n === 1 ? "result" : "results";
};
const pagesString = (n) => {
return n === 1 ? "page" : "pages";
};

View file

@ -661,3 +661,32 @@ dd {
margin: 0;
}
}
/* Style for the page search widget */
.td-offline-search-results {
width: 100%;
max-width: 460px;
}
.td-offline-search-results .td-offline-search-results__subresults.collapse.show {
// Prevent the first child margin from collapsing upward when Bootstrap
// finishes the collapse animation, which otherwise causes a small jump.
display: flow-root;
}
.td-offline-search-results .spinner-container {
text-align: center;
}
.td-offline-search-results__close-button {
// Prevent the label from rendering white on white.
--bs-btn-color: unset;
}
.td-offline-search-results__expander-button {
// Prevent the label from rendering white on white.
--bs-btn-color: unset;
// Avoid any extra inset.
--bs-btn-padding-x: 0;
--bs-btn-padding-y: 0;
}

View file

@ -0,0 +1 @@
Clarify formats of string types.

View file

@ -0,0 +1 @@
Add page search widget.

View file

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

View file

@ -66,6 +66,7 @@ description = "Home of the Matrix specification for decentralised communication"
[params]
copyright = "The Matrix.org Foundation C.I.C."
offlineSearch = true
[params.version]
# must be one of "unstable", "current", "historical"
@ -151,7 +152,10 @@ sidebar_menu_compact = true
[server.headers.values]
# `style-src 'unsafe-inline'` is needed to correctly render the maths in the Olm spec:
# https://github.com/KaTeX/KaTeX/issues/4096
Content-Security-Policy = "default-src 'self'; style-src 'self' 'unsafe-inline'; script-src 'self'; img-src 'self' data:; connect-src 'self'; font-src 'self' data:; media-src 'self'; child-src 'self'; form-action 'self'; object-src 'self'"
# `script-src 'unsafe-eval'` is needed because Pagefind relies on it to load its Wasm.
# In future, we should switch to `wasm-unsafe-eval` but this doesn't yet work in Safari:
# https://github.com/Pagefind/pagefind/blob/main/docs/content/docs/hosting.md
Content-Security-Policy = "default-src 'self'; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-eval'; img-src 'self' data:; connect-src 'self'; font-src 'self' data:; media-src 'self'; child-src 'self'; form-action 'self'; object-src 'self'"
X-XSS-Protection = "1; mode=block"
X-Content-Type-Options = "nosniff"
# Strict-Transport-Security = "max-age=31536000; includeSubDomains; preload"

View file

@ -3348,8 +3348,8 @@ room at the start of the returned timeline. The response also includes a
`next_batch` field, which should be used as the value of the `since`
parameter in the next call to `/sync`. Finally, the response includes,
for each room, a `prev_batch` field, which can be passed as a `from`/`to`
parameter to the [`/rooms/<room_id>/messages`](/client-server-api/#get_matrixclientv3roomsroomidmessages) API to retrieve earlier
messages.
parameter to the [`/rooms/{roomId}/messages`](#get_matrixclientv3roomsroomidmessages)
API to retrieve earlier messages.
For example, a `/sync` request might return a range of four events
`E2`, `E3`, `E4` and `E5` within a given room, omitting two prior events
@ -3368,7 +3368,8 @@ response to the previous call as the `since` parameter. The client
should also pass a `timeout` parameter. The server will then hold open
the HTTP connection for a short period of time waiting for new events,
returning early if an event occurs. Only the `/sync` API (and the
deprecated `/events` API) support long-polling in this way.
deprecated [`/events`](#get_matrixclientv3events) API) support
long-polling in this way.
Continuing the example above, an incremental sync might report
a single new event `E6`. The response can be visualised as:
@ -3388,7 +3389,7 @@ containing only the most recent message events. A state "delta" is also
returned, summarising any state changes in the omitted part of the
timeline. The client may therefore end up with "gaps" in its knowledge
of the message timeline. The client can fill these gaps using the
[`/rooms/<room_id>/messages`](/client-server-api/#get_matrixclientv3roomsroomidmessages) API.
[`/rooms/{roomId}/messages`](#get_matrixclientv3roomsroomidmessages) API.
Continuing our example, suppose we make a third `/sync` request asking for
events since the last sync, by passing the `next_batch` token `x-y-z` as
@ -3411,7 +3412,7 @@ The limited response includes a state delta which describes how the state
of the room changes over the gap. This delta explains how to build the state
prior to returned timeline (i.e. at `E7`) from the state the client knows
(i.e. at `E6`). To close the gap, the client should make a request to
[`/rooms/<room_id>/messages`](/client-server-api/#get_matrixclientv3roomsroomidmessages)
[`/rooms/{roomId}/messages`](#get_matrixclientv3roomsroomidmessages)
with the query parameters `from=x-y-z` and `to=d-e-f`.
{{% boxes/warning %}}

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

@ -24,8 +24,9 @@ allOf:
room_id:
description: The ID of the room associated with this event.
type: string
format: mx-room-id
pattern: "^!"
example: '!jEsUZKDJdhlrceRyVU:example.org'
unsigned:
properties:
redacted_because:
@ -43,6 +44,6 @@ allOf:
"unsigned": {
"age": 1257,
}
}
}
required:
- room_id

View file

@ -28,6 +28,8 @@ properties:
event_id:
description: The globally unique identifier for this event.
type: string
format: mx-event-id
pattern: "^\\$"
example: '$26RqwJMLw-yds1GAH_QxjHRC1Da9oasK0e5VLnck_45'
type:
description: The type of the event.
@ -47,6 +49,8 @@ properties:
sender:
description: Contains the fully-qualified ID of the user who sent this event.
type: string
format: mx-user-id
pattern: "^@"
example: "@example:example.org"
origin_server_ts:
description: |-

View file

@ -39,5 +39,7 @@ properties:
such.
event_id:
type: string
format: mx-event-id
pattern: "^\\$"
description: The event ID of the event that this event relates to.
required: ['rel_type', 'event_id']

View file

@ -37,6 +37,8 @@ paths:
example: "!636q39766251:example.com"
schema:
type: string
format: mx-room-id
pattern: "^!"
- in: query
name: from
x-changedInMatrixVersion:

View file

@ -148,6 +148,8 @@ paths:
properties:
room_id:
type: string
format: mx-room-id
pattern: "^!"
description: The ID of this room.
membership:
type: string
@ -337,6 +339,8 @@ paths:
example: $asfDuShaf7Gafaw:matrix.org
schema:
type: string
format: mx-event-id
pattern: "^\\$"
responses:
"200":
description: The full event.

View file

@ -43,13 +43,17 @@ paths:
example: "!637q39766251:example.com"
schema:
type: string
format: mx-room-id
pattern: "^!"
- in: path
name: eventId
description: The ID of the event to redact
description: The ID of the event to redact.
required: true
example: bai2b1i9:matrix.org
example: $bai2b1i9:matrix.org
schema:
type: string
format: mx-event-id
pattern: "^\\$"
- in: path
name: txnId
description: |-
@ -82,6 +86,8 @@ paths:
properties:
event_id:
type: string
format: mx-event-id
pattern: "^\\$"
description: A unique identifier for the event.
examples:
response:

View file

@ -233,6 +233,8 @@ components:
example: "!636q39766251:matrix.org"
schema:
type: string
format: mx-room-id
pattern: "^!"
eventId:
in: path
name: eventId
@ -241,6 +243,8 @@ components:
example: $asfDuShaf7Gafaw
schema:
type: string
format: mx-event-id
pattern: "^\\$"
from:
in: query
name: from

View file

@ -56,6 +56,8 @@ paths:
example: "!636q39766251:matrix.org"
schema:
type: string
format: mx-room-id
pattern: "^!"
- in: query
name: ts
description: |-
@ -86,6 +88,8 @@ paths:
properties:
event_id:
type: string
format: mx-event-id
pattern: "^\\$"
description: The ID of the event found
origin_server_ts:
type: integer

View file

@ -27,6 +27,8 @@ paths:
example: "!636q39766251:example.com"
schema:
type: string
format: mx-room-id
pattern: "^!"
responses:
"200":
description: The current state of the room
@ -38,6 +40,8 @@ paths:
properties:
room_id:
type: string
format: mx-room-id
pattern: "^!"
description: The ID of this room.
membership:
type: string

View file

@ -49,6 +49,8 @@ paths:
example: "!636q39766251:example.com"
schema:
type: string
format: mx-room-id
pattern: "^!"
- in: path
name: eventType
description: The type of event to send.
@ -86,6 +88,8 @@ paths:
properties:
event_id:
type: string
format: mx-event-id
pattern: "^\\$"
description: A unique identifier for the event.
required:
- event_id

View file

@ -49,6 +49,8 @@ paths:
example: "!636q39766251:example.com"
schema:
type: string
format: mx-room-id
pattern: "^!"
- in: path
name: eventType
description: The type of event to send.
@ -86,6 +88,8 @@ paths:
properties:
event_id:
type: string
format: mx-event-id
pattern: "^\\$"
description: A unique identifier for the event.
required:
- event_id

View file

@ -34,6 +34,8 @@ paths:
example: "!636q39766251:matrix.org"
schema:
type: string
format: mx-room-id
pattern: "^!"
- in: path
name: eventId
description: The event ID to get.
@ -41,6 +43,8 @@ paths:
example: $asfDuShaf7Gafaw:matrix.org
schema:
type: string
format: mx-event-id
pattern: "^\\$"
responses:
"200":
description: The full event.
@ -89,6 +93,8 @@ paths:
example: "!636q39766251:example.com"
schema:
type: string
format: mx-room-id
pattern: "^!"
- in: path
name: eventType
description: The type of state to look up.
@ -158,6 +164,8 @@ paths:
example: "!636q39766251:example.com"
schema:
type: string
format: mx-room-id
pattern: "^!"
responses:
"200":
description: The current state of the room
@ -211,6 +219,8 @@ paths:
example: "!636q39766251:example.com"
schema:
type: string
format: mx-room-id
pattern: "^!"
- in: query
name: at
description: |-
@ -305,6 +315,8 @@ paths:
example: "!636q39766251:example.com"
schema:
type: string
format: mx-room-id
pattern: "^!"
security:
- accessTokenQuery: []
- accessTokenBearer: []

View file

@ -209,6 +209,8 @@ paths:
field may be omitted.
items:
type: string
format: mx-user-id
pattern: "^@"
m.joined_member_count:
type: integer
description: |-

View file

@ -20,10 +20,13 @@ properties:
The signatures are calculated using the process described at
[Signing JSON](/appendices/#signing-json).
type: object
additionalProperties:
type: object
additionalProperties:
type: string
patternProperties:
"":
x-pattern-format: mx-server-name
type: object
additionalProperties:
type: string
format: mx-unpadded-base64
example: {
"magic.forest": {
"ed25519:3": "fQpGIW1Snz+pwLZu6sTy2aHy/DYWWTspTJRPyNp0PKkymfIsNffysMl6ObMMFdIJhk6g6pwlIqZ54rxo8SLmAg"

View file

@ -11,5 +11,7 @@ allOf:
The ID of the room associated with this event. Will not be present on events
that arrive through `/sync`, despite being required everywhere else.
type: string
format: mx-room-id
pattern: "^!"
required:
- room_id

View file

@ -39,6 +39,8 @@ properties:
sender:
description: The `sender` for the event.
type: string
format: mx-user-id
pattern: "^@"
required:
- type
- state_key

View file

@ -29,9 +29,13 @@ allOf:
event_id:
description: The globally unique event identifier.
type: string
format: mx-event-id
pattern: "^\\$"
sender:
description: Contains the fully-qualified ID of the user who sent this event.
type: string
format: mx-user-id
pattern: "^@"
origin_server_ts:
description: Timestamp in milliseconds on originating homeserver
when this event was sent.

View file

@ -16,6 +16,8 @@ properties:
The canonical alias for the room. If not present, null, or empty the
room should be considered to have no canonical alias.
type: string
format: mx-room-alias
pattern: "^#"
alt_aliases:
description: |
Alternative aliases the room advertises. This list can have aliases
@ -23,6 +25,8 @@ properties:
type: array
items:
type: string
format: mx-room-alias
pattern: "^#"
type: object
state_key:
description: A zero-length string.

View file

@ -12,6 +12,8 @@ properties:
The `user_id` of the room creator. **Required** for, and only present in, room versions 1 - 10. Starting with
room version 11 the event `sender` should be used instead.
type: string
format: mx-user-id
pattern: "^@"
m.federate:
description: Whether users on other servers can join this room. Defaults to `true` if key does not exist.
type: boolean
@ -32,9 +34,13 @@ properties:
properties:
room_id:
type: string
format: mx-room-id
pattern: "^!"
description: The ID of the old room.
event_id:
type: string
format: mx-event-id
pattern: "^\\$"
deprecated: true
x-changedInMatrixVersion:
"1.16": |-
@ -51,6 +57,8 @@ properties:
type: array
items:
type: string
format: mx-user-id
pattern: "^@"
description: Additional user ID to consider a creator of the room, if supported by the room version.
x-addedInMatrixVersion: "1.16"
description: |-

View file

@ -55,6 +55,8 @@ properties:
enum: ['m.room_membership']
room_id:
type: string
format: mx-room-id
pattern: "^!"
description: |-
Required if `type` is `m.room_membership`. The room ID to check the
user's membership against. If the user is joined to this room, they

View file

@ -76,6 +76,8 @@ properties:
join_authorised_via_users_server:
x-addedInMatrixVersion: "1.2"
type: string
format: mx-user-id
pattern: "^@"
description: |-
Usually found on `join` events, this field is used to denote which homeserver (through
representation of a user with sufficient power level) authorised the user's join. More
@ -127,6 +129,8 @@ properties:
`join`, the user ID sending the event does not need to match the user ID in the `state_key`,
unlike other events. Regular authorisation rules still apply.
type: string
format: mx-user-id
pattern: "^@"
type:
enum:
- m.room.member

View file

@ -10,6 +10,8 @@ properties:
redacts:
description: The event ID that was redacted. Required for, and present starting in, room version 11.
type: string
format: mx-event-id
pattern: "^\\$"
reason:
description: 'The reason for the redaction, if any.'
type: string
@ -17,6 +19,8 @@ properties:
redacts:
description: Required for, and only present in, room versions 1 - 10. The event ID that was redacted.
type: string
format: mx-event-id
pattern: "^\\$"
type:
enum:
- m.room.redaction

View file

@ -66,6 +66,11 @@ mx-mxc-uri:
url: client-server-api#matrix-content-mxc-uris
# regex: "^mxc:\\/\\/"
mx-unpadded-base64:
title: Unpadded Base64
url: appendices#unpadded-base64
# no regex
uri:
title: URI
url: https://datatracker.ietf.org/doc/html/rfc3986

View file

@ -0,0 +1,10 @@
<div class="td-search td-search--offline">
<div class="td-search__icon"></div>
<input
type="search"
class="td-search__input form-control"
placeholder="Search the spec"
aria-label="Search the spec"
autocomplete="off"
>
</div>

159
package-lock.json generated
View file

@ -12,6 +12,7 @@
"@fullhuman/postcss-purgecss": "^6.0.0",
"autoprefixer": "^10.4.20",
"node-fetch": "^2.6.7",
"pagefind": "^1.4.0",
"postcss": "^8.4.49",
"postcss-cli": "^11.0.0"
}
@ -170,6 +171,90 @@
"node": ">= 8"
}
},
"node_modules/@pagefind/darwin-arm64": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/@pagefind/darwin-arm64/-/darwin-arm64-1.4.0.tgz",
"integrity": "sha512-2vMqkbv3lbx1Awea90gTaBsvpzgRs7MuSgKDxW0m9oV1GPZCZbZBJg/qL83GIUEN2BFlY46dtUZi54pwH+/pTQ==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
]
},
"node_modules/@pagefind/darwin-x64": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/@pagefind/darwin-x64/-/darwin-x64-1.4.0.tgz",
"integrity": "sha512-e7JPIS6L9/cJfow+/IAqknsGqEPjJnVXGjpGm25bnq+NPdoD3c/7fAwr1OXkG4Ocjx6ZGSCijXEV4ryMcH2E3A==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
]
},
"node_modules/@pagefind/freebsd-x64": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/@pagefind/freebsd-x64/-/freebsd-x64-1.4.0.tgz",
"integrity": "sha512-WcJVypXSZ+9HpiqZjFXMUobfFfZZ6NzIYtkhQ9eOhZrQpeY5uQFqNWLCk7w9RkMUwBv1HAMDW3YJQl/8OqsV0Q==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
]
},
"node_modules/@pagefind/linux-arm64": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/@pagefind/linux-arm64/-/linux-arm64-1.4.0.tgz",
"integrity": "sha512-PIt8dkqt4W06KGmQjONw7EZbhDF+uXI7i0XtRLN1vjCUxM9vGPdtJc2mUyVPevjomrGz5M86M8bqTr6cgDp1Uw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@pagefind/linux-x64": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/@pagefind/linux-x64/-/linux-x64-1.4.0.tgz",
"integrity": "sha512-z4oddcWwQ0UHrTHR8psLnVlz6USGJ/eOlDPTDYZ4cI8TK8PgwRUPQZp9D2iJPNIPcS6Qx/E4TebjuGJOyK8Mmg==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@pagefind/windows-x64": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/@pagefind/windows-x64/-/windows-x64-1.4.0.tgz",
"integrity": "sha512-NkT+YAdgS2FPCn8mIA9bQhiBs+xmniMGq1LFPDhcFn0+2yIUEiIG06t7bsZlhdjknEQRTSdT7YitP6fC5qwP0g==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
]
},
"node_modules/@pkgjs/parseargs": {
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
@ -914,6 +999,24 @@
"dev": true,
"license": "BlueOak-1.0.0"
},
"node_modules/pagefind": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/pagefind/-/pagefind-1.4.0.tgz",
"integrity": "sha512-z2kY1mQlL4J8q5EIsQkLzQjilovKzfNVhX8De6oyE6uHpfFtyBaqUpcl/XzJC/4fjD8vBDyh1zolimIcVrCn9g==",
"dev": true,
"license": "MIT",
"bin": {
"pagefind": "lib/runner/bin.cjs"
},
"optionalDependencies": {
"@pagefind/darwin-arm64": "1.4.0",
"@pagefind/darwin-x64": "1.4.0",
"@pagefind/freebsd-x64": "1.4.0",
"@pagefind/linux-arm64": "1.4.0",
"@pagefind/linux-x64": "1.4.0",
"@pagefind/windows-x64": "1.4.0"
}
},
"node_modules/path-key": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
@ -1652,6 +1755,48 @@
"fastq": "^1.6.0"
}
},
"@pagefind/darwin-arm64": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/@pagefind/darwin-arm64/-/darwin-arm64-1.4.0.tgz",
"integrity": "sha512-2vMqkbv3lbx1Awea90gTaBsvpzgRs7MuSgKDxW0m9oV1GPZCZbZBJg/qL83GIUEN2BFlY46dtUZi54pwH+/pTQ==",
"dev": true,
"optional": true
},
"@pagefind/darwin-x64": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/@pagefind/darwin-x64/-/darwin-x64-1.4.0.tgz",
"integrity": "sha512-e7JPIS6L9/cJfow+/IAqknsGqEPjJnVXGjpGm25bnq+NPdoD3c/7fAwr1OXkG4Ocjx6ZGSCijXEV4ryMcH2E3A==",
"dev": true,
"optional": true
},
"@pagefind/freebsd-x64": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/@pagefind/freebsd-x64/-/freebsd-x64-1.4.0.tgz",
"integrity": "sha512-WcJVypXSZ+9HpiqZjFXMUobfFfZZ6NzIYtkhQ9eOhZrQpeY5uQFqNWLCk7w9RkMUwBv1HAMDW3YJQl/8OqsV0Q==",
"dev": true,
"optional": true
},
"@pagefind/linux-arm64": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/@pagefind/linux-arm64/-/linux-arm64-1.4.0.tgz",
"integrity": "sha512-PIt8dkqt4W06KGmQjONw7EZbhDF+uXI7i0XtRLN1vjCUxM9vGPdtJc2mUyVPevjomrGz5M86M8bqTr6cgDp1Uw==",
"dev": true,
"optional": true
},
"@pagefind/linux-x64": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/@pagefind/linux-x64/-/linux-x64-1.4.0.tgz",
"integrity": "sha512-z4oddcWwQ0UHrTHR8psLnVlz6USGJ/eOlDPTDYZ4cI8TK8PgwRUPQZp9D2iJPNIPcS6Qx/E4TebjuGJOyK8Mmg==",
"dev": true,
"optional": true
},
"@pagefind/windows-x64": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/@pagefind/windows-x64/-/windows-x64-1.4.0.tgz",
"integrity": "sha512-NkT+YAdgS2FPCn8mIA9bQhiBs+xmniMGq1LFPDhcFn0+2yIUEiIG06t7bsZlhdjknEQRTSdT7YitP6fC5qwP0g==",
"dev": true,
"optional": true
},
"@pkgjs/parseargs": {
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
@ -2117,6 +2262,20 @@
"integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==",
"dev": true
},
"pagefind": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/pagefind/-/pagefind-1.4.0.tgz",
"integrity": "sha512-z2kY1mQlL4J8q5EIsQkLzQjilovKzfNVhX8De6oyE6uHpfFtyBaqUpcl/XzJC/4fjD8vBDyh1zolimIcVrCn9g==",
"dev": true,
"requires": {
"@pagefind/darwin-arm64": "1.4.0",
"@pagefind/darwin-x64": "1.4.0",
"@pagefind/freebsd-x64": "1.4.0",
"@pagefind/linux-arm64": "1.4.0",
"@pagefind/linux-x64": "1.4.0",
"@pagefind/windows-x64": "1.4.0"
}
},
"path-key": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",

View file

@ -5,7 +5,8 @@
"main": "none.js",
"scripts": {
"get-proposals": "node ./scripts/proposals.js",
"test": "echo \"Error: no test specified\" && exit 1"
"test": "echo \"Error: no test specified\" && exit 1",
"pagefind": "pagefind $@"
},
"repository": {
"type": "git",
@ -22,6 +23,7 @@
"@fullhuman/postcss-purgecss": "^6.0.0",
"autoprefixer": "^10.4.20",
"node-fetch": "^2.6.7",
"pagefind": "^1.4.0",
"postcss": "^8.4.49",
"postcss-cli": "^11.0.0"
}