Merge pull request #1828 from matrix-org/travis/spec/rooms-v3
Add specification for room version 3: Event IDs as hashes
This commit is contained in:
commit
df01acc6e2
12 changed files with 633 additions and 352 deletions
|
@ -484,6 +484,7 @@ The available room versions are:
|
|||
|
||||
* `Version 1 <rooms/v1.html>`_ - **Stable**. The current version of most rooms.
|
||||
* `Version 2 <rooms/v2.html>`_ - **Stable**. Implements State Resolution Version 2.
|
||||
* `Version 3 <rooms/v3.html>`_ - **Stable**. Introduces events whose IDs are the event's hash.
|
||||
|
||||
Specification Versions
|
||||
----------------------
|
||||
|
|
|
@ -77,7 +77,7 @@ results of the resolution so far.
|
|||
* Add the first event in the list to :math:`R`.
|
||||
|
||||
* For each subsequent event in the list, check that the event would be
|
||||
allowed by the `authorization rules`_ for a room in state :math:`R`. If the
|
||||
allowed by the authorization rules for a room in state :math:`R`. If the
|
||||
event would be allowed, then update :math:`R` with the event and continue
|
||||
with the next event in the list. If it would not be allowed, stop and
|
||||
continue below with ``m.room.join_rules`` events.
|
||||
|
@ -95,4 +95,200 @@ A *conflict* occurs between states where those states have different
|
|||
affected are said to be *conflicting* events.
|
||||
|
||||
|
||||
.. _`authorization rules`: ../server_server/unstable.html#authorization-rules
|
||||
Authorization rules
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The types of state events that affect authorization are:
|
||||
|
||||
- ``m.room.create``
|
||||
- ``m.room.member``
|
||||
- ``m.room.join_rules``
|
||||
- ``m.room.power_levels``
|
||||
- ``m.room.third_party_invite``
|
||||
|
||||
The rules are as follows:
|
||||
|
||||
1. If type is ``m.room.create``:
|
||||
|
||||
a. If it has any previous events, reject.
|
||||
b. If the domain of the ``room_id`` does not match the domain of the
|
||||
``sender``, reject.
|
||||
c. If ``content.room_version`` is present and is not a recognised version,
|
||||
reject.
|
||||
d. If ``content`` has no ``creator`` field, reject.
|
||||
e. Otherwise, allow.
|
||||
|
||||
#. Reject if event has ``auth_events`` that:
|
||||
|
||||
a. have duplicate entries for a given ``type`` and ``state_key`` pair
|
||||
#. have entries whose ``type`` and ``state_key`` don't match those
|
||||
specified by the `auth events selection`_ algorithm described in the
|
||||
server specification.
|
||||
|
||||
#. If event does not have a ``m.room.create`` in its ``auth_events``, reject.
|
||||
|
||||
#. If type is ``m.room.aliases``:
|
||||
|
||||
a. If event has no ``state_key``, reject.
|
||||
b. If sender's domain doesn't matches ``state_key``, reject.
|
||||
c. Otherwise, allow.
|
||||
|
||||
#. If type is ``m.room.member``:
|
||||
|
||||
a. If no ``state_key`` key or ``membership`` key in ``content``, reject.
|
||||
|
||||
#. If ``membership`` is ``join``:
|
||||
|
||||
i. If the only previous event is an ``m.room.create``
|
||||
and the ``state_key`` is the creator, allow.
|
||||
|
||||
#. If the ``sender`` does not match ``state_key``, reject.
|
||||
|
||||
#. If the ``sender`` is banned, reject.
|
||||
|
||||
#. If the ``join_rule`` is ``invite`` then allow if membership state
|
||||
is ``invite`` or ``join``.
|
||||
|
||||
#. If the ``join_rule`` is ``public``, allow.
|
||||
|
||||
#. Otherwise, reject.
|
||||
|
||||
#. If ``membership`` is ``invite``:
|
||||
|
||||
i. If ``content`` has ``third_party_invite`` key:
|
||||
|
||||
#. If *target user* is banned, reject.
|
||||
|
||||
#. If ``content.third_party_invite`` does not have a
|
||||
``signed`` key, reject.
|
||||
|
||||
#. If ``signed`` does not have ``mxid`` and ``token`` keys, reject.
|
||||
|
||||
#. If ``mxid`` does not match ``state_key``, reject.
|
||||
|
||||
#. If there is no ``m.room.third_party_invite`` event in the
|
||||
current room state with ``state_key`` matching ``token``, reject.
|
||||
|
||||
#. If ``sender`` does not match ``sender`` of the
|
||||
``m.room.third_party_invite``, reject.
|
||||
|
||||
#. If any signature in ``signed`` matches any public key in the
|
||||
``m.room.third_party_invite`` event, allow. The public keys are
|
||||
in ``content`` of ``m.room.third_party_invite`` as:
|
||||
|
||||
#. A single public key in the ``public_key`` field.
|
||||
#. A list of public keys in the ``public_keys`` field.
|
||||
|
||||
#. Otherwise, reject.
|
||||
|
||||
#. If the ``sender``'s current membership state is not ``join``, reject.
|
||||
|
||||
#. If *target user*'s current membership state is ``join`` or ``ban``,
|
||||
reject.
|
||||
|
||||
#. If the ``sender``'s power level is greater than or equal to the *invite
|
||||
level*, allow.
|
||||
|
||||
#. Otherwise, reject.
|
||||
|
||||
#. If ``membership`` is ``leave``:
|
||||
|
||||
i. If the ``sender`` matches ``state_key``, allow if and only if that user's
|
||||
current membership state is ``invite`` or ``join``.
|
||||
|
||||
#. If the ``sender``'s current membership state is not ``join``, reject.
|
||||
|
||||
#. If the *target user*'s current membership state is ``ban``, and the
|
||||
``sender``'s power level is less than the *ban level*, reject.
|
||||
|
||||
#. If the ``sender``'s power level is greater than or equal to the *kick
|
||||
level*, and the *target user*'s power level is less than the
|
||||
``sender``'s power level, allow.
|
||||
|
||||
#. Otherwise, reject.
|
||||
|
||||
#. If ``membership`` is ``ban``:
|
||||
|
||||
i. If the ``sender``'s current membership state is not ``join``, reject.
|
||||
|
||||
#. If the ``sender``'s power level is greater than or equal to the *ban
|
||||
level*, and the *target user*'s power level is less than the
|
||||
``sender``'s power level, allow.
|
||||
|
||||
#. Otherwise, reject.
|
||||
|
||||
#. Otherwise, the membership is unknown. Reject.
|
||||
|
||||
#. If the ``sender``'s current membership state is not ``join``, reject.
|
||||
|
||||
#. If type is ``m.room.third_party_invite``:
|
||||
|
||||
a. Allow if and only if ``sender``'s current power level is greater than
|
||||
or equal to the *invite level*.
|
||||
|
||||
#. If the event type's *required power level* is greater than the ``sender``'s power
|
||||
level, reject.
|
||||
|
||||
#. If the event has a ``state_key`` that starts with an ``@`` and does not match
|
||||
the ``sender``, reject.
|
||||
|
||||
#. If type is ``m.room.power_levels``:
|
||||
|
||||
a. If ``users`` key in ``content`` is not a dictionary with keys that are
|
||||
valid user IDs with values that are integers (or a string that is an
|
||||
integer), reject.
|
||||
|
||||
#. If there is no previous ``m.room.power_levels`` event in the room, allow.
|
||||
|
||||
#. For each of the keys ``users_default``, ``events_default``,
|
||||
``state_default``, ``ban``, ``redact``, ``kick``, ``invite``, as well as
|
||||
each entry being changed under the ``events`` or ``users`` keys:
|
||||
|
||||
i. If the current value is higher than the ``sender``'s current power level,
|
||||
reject.
|
||||
|
||||
#. If the new value is higher than the ``sender``'s current power level,
|
||||
reject.
|
||||
|
||||
#. For each entry being changed under the ``users`` key, other than the
|
||||
``sender``'s own entry:
|
||||
|
||||
i. If the current value is equal to the ``sender``'s current power level,
|
||||
reject.
|
||||
|
||||
#. Otherwise, allow.
|
||||
|
||||
#. If type is ``m.room.redaction``:
|
||||
|
||||
a. If the ``sender``'s power level is greater than or equal to the *redact
|
||||
level*, allow.
|
||||
|
||||
#. If the domain of the ``event_id`` of the event being redacted is the same
|
||||
as the domain of the ``event_id`` of the ``m.room.redaction``, allow.
|
||||
|
||||
#. Otherwise, reject.
|
||||
|
||||
#. Otherwise, allow.
|
||||
|
||||
.. NOTE::
|
||||
|
||||
Some consequences of these rules:
|
||||
|
||||
* Unless you are a member of the room, the only permitted operations (apart
|
||||
from the intial create/join) are: joining a public room; accepting or
|
||||
rejecting an invitation to a room.
|
||||
|
||||
* To unban somebody, you must have power level greater than or equal to both
|
||||
the kick *and* ban levels, *and* greater than the target user's power
|
||||
level.
|
||||
|
||||
Event format
|
||||
~~~~~~~~~~~~
|
||||
|
||||
Events in version 1 rooms have the following structure:
|
||||
|
||||
{{definition_ss_pdu}}
|
||||
|
||||
|
||||
.. _`auth events selection`: ../../server_server/unstable.html#auth-events-selection
|
||||
.. _`Signing Events`: ../../server_server/unstable.html#signing-events
|
||||
|
|
121
specification/rooms/v3.rst
Normal file
121
specification/rooms/v3.rst
Normal file
|
@ -0,0 +1,121 @@
|
|||
.. Copyright 2018-2019 New Vector Ltd
|
||||
..
|
||||
.. 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.
|
||||
|
||||
Room Version 3
|
||||
==============
|
||||
|
||||
This room version builds on `version 2 <v2.html>`_ with an improved event format.
|
||||
|
||||
.. note:
|
||||
All requirements listed in this room version specification are scoped to rooms
|
||||
which actually use this room version. For example, a requirement of "all APIs must
|
||||
accept the new event format" does in fact apply to all APIs, but only so much as
|
||||
where the contextual room of the request is using this room version. Rooms using
|
||||
other room versions should not be affected by these sweeping requirements.
|
||||
|
||||
|
||||
Client considerations
|
||||
---------------------
|
||||
|
||||
This room version changes the format for event IDs sent to clients. Clients should be
|
||||
aware that these event IDs may contain slashes and other potentially problematic
|
||||
characters. Clients should be treating event IDs as opaque identifiers and should not
|
||||
be attempting to parse them into a usable form, just like with other room versions.
|
||||
|
||||
Clients should expect to see event IDs changed from the format of ``$randomstring:example.org``
|
||||
to something like ``$acR1l0raoZnm60CBwAVgqbZqoO/mYU81xysh1u7XcJk`` (note the lack of
|
||||
domain and the potentially problematic slash).
|
||||
|
||||
|
||||
Server implementation components
|
||||
--------------------------------
|
||||
|
||||
.. WARNING::
|
||||
The information contained in this section is strictly for server implementors.
|
||||
Applications which use the Client-Server API are generally unaffected by the
|
||||
intricacies contained here. The section above regarding client considerations
|
||||
is the resource that Client-Server API use cases should reference.
|
||||
|
||||
|
||||
Room version 3 uses the state resolution algorithm defined in `room version 2 <v2.html>`_,
|
||||
and the event format defined here.
|
||||
|
||||
Event IDs
|
||||
~~~~~~~~~
|
||||
|
||||
.. admonition:: Rationale
|
||||
|
||||
In other room versions (namely version 1 and 2) the event ID is a distinct field
|
||||
from the remainder of the event, which must be tracked as such. This leads to
|
||||
complications where servers receive multiple events with the same ID in either the
|
||||
same or different rooms where the server cannot easily keep track of which event it
|
||||
should be using. By removing the use of a dedicated event ID, servers are required
|
||||
to track the hashes on an event to determine its ID.
|
||||
|
||||
The event ID is the `reference hash`_ of the event encoded using `Unpadded Base64`_,
|
||||
prefixed with ``$``. A resulting event ID using this approach should look similar to
|
||||
``$CD66HAED5npg6074c6pDtLKalHjVfYb2q4Q3LZgrW6o``.
|
||||
|
||||
Event IDs should not be sent over federation to servers when the room uses
|
||||
this room version. On the receiving end of an event, the server should compute
|
||||
the relevant event ID for itself.
|
||||
|
||||
Additionally, the ``auth_events`` and ``prev_events`` have had a format change
|
||||
compared to other room versions to make it easier to handle. Instead of a tuple
|
||||
of values, they are now plain lists of events.
|
||||
|
||||
{{definition_ss_pdu_v3}}
|
||||
|
||||
Changes to APIs
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Due to the event ID being removed from the event, some APIs need to change. All
|
||||
APIs which currently accept an event ID must do so with the new format. Servers
|
||||
must append the calculated event ID to all events sent to clients where an event
|
||||
ID would normally be expected.
|
||||
|
||||
Because the format of events has changed, servers must be aware of the room version
|
||||
where the event resides so that the server may parse and handle the event. The
|
||||
federation API has taken this concern into consideration by ensuring that servers
|
||||
are aware of (or can find) the room version during a request.
|
||||
|
||||
Authorization rules for events
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The authorization rules for a given event have changed in this room version due
|
||||
to the change in event format:
|
||||
|
||||
* The event no longer needs to be signed by the domain of the event ID (as there
|
||||
is no domain in the event ID), but still needs to be signed by the sender's
|
||||
domain.
|
||||
|
||||
* In past room versions, redactions were only permitted to enter the DAG if the
|
||||
sender's domain matched the domain in the event ID being redacted, or the sender
|
||||
had appropriate permissions per the power levels. Due to servers now not being
|
||||
able to determine where an event came from during event authorization, redaction
|
||||
events are always accepted (provided the event is allowed by ``events`` and
|
||||
``events_default`` in the power levels). However, servers should not apply or send
|
||||
redactions to clients until both the redaction event and original event have been
|
||||
seen, and are valid. Servers should only apply redactions to events where the
|
||||
sender's domains match, or the sender of the redaction has the appropriate
|
||||
permissions per the power levels.
|
||||
|
||||
|
||||
The remaining rules are the same as `room version 1 <v1.html#authorization-rules>`_.
|
||||
|
||||
|
||||
.. _`Unpadded Base64`: ../../appendices.html#unpadded-base64
|
||||
.. _`Canonical JSON`: ../../appendices.html#canonical-json
|
||||
.. _`Signing Events`: ../../server_server/unstable.html#signing-events
|
||||
.. _`reference hash`: ../../server_server/unstable.html#reference-hashes
|
|
@ -281,6 +281,8 @@ Transactions are limited in size; they can have at most 50 PDUs and 100 EDUs.
|
|||
|
||||
{{transactions_ss_http_api}}
|
||||
|
||||
.. _`Persistent Data Unit schema`:
|
||||
|
||||
PDUs
|
||||
----
|
||||
|
||||
|
@ -327,7 +329,7 @@ following subset of the room state:
|
|||
``m.room.third_party_invite`` event with ``state_key`` matching
|
||||
``content.third_party_invite.signed.token``, if any.
|
||||
|
||||
{{definition_ss_pdu}}
|
||||
For a full schema of what a PDU looks like, see the `room version specification`_.
|
||||
|
||||
Checks performed on receipt of a PDU
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
@ -379,189 +381,9 @@ Authorization rules
|
|||
|
||||
The rules governing whether an event is authorized depends on a set of state. A
|
||||
given event is checked multiple times against different sets of state, as
|
||||
specified above. The types of state events that affect authorization are:
|
||||
|
||||
- ``m.room.create``
|
||||
- ``m.room.member``
|
||||
- ``m.room.join_rules``
|
||||
- ``m.room.power_levels``
|
||||
- ``m.room.third_party_invite``
|
||||
|
||||
The rules are as follows:
|
||||
|
||||
1. If type is ``m.room.create``:
|
||||
|
||||
a. If it has any previous events, reject.
|
||||
b. If the domain of the ``room_id`` does not match the domain of the
|
||||
``sender``, reject.
|
||||
c. If ``content.room_version`` is present and is not a recognised version,
|
||||
reject.
|
||||
d. If ``content`` has no ``creator`` field, reject.
|
||||
e. Otherwise, allow.
|
||||
|
||||
#. Reject if event has ``auth_events`` that:
|
||||
|
||||
a. have duplicate entries for a given ``type`` and ``state_key`` pair
|
||||
#. have entries whose ``type`` and ``state_key`` don't match those
|
||||
specified by the `auth events selection`_ algorithm described above.
|
||||
|
||||
#. If event does not have a ``m.room.create`` in its ``auth_events``, reject.
|
||||
|
||||
#. If type is ``m.room.aliases``:
|
||||
|
||||
a. If event has no ``state_key``, reject.
|
||||
b. If sender's domain doesn't matches ``state_key``, reject.
|
||||
c. Otherwise, allow.
|
||||
|
||||
#. If type is ``m.room.member``:
|
||||
|
||||
a. If no ``state_key`` key or ``membership`` key in ``content``, reject.
|
||||
|
||||
#. If ``membership`` is ``join``:
|
||||
|
||||
i. If the only previous event is an ``m.room.create``
|
||||
and the ``state_key`` is the creator, allow.
|
||||
|
||||
#. If the ``sender`` does not match ``state_key``, reject.
|
||||
|
||||
#. If the ``sender`` is banned, reject.
|
||||
|
||||
#. If the ``join_rule`` is ``invite`` then allow if membership state
|
||||
is ``invite`` or ``join``.
|
||||
|
||||
#. If the ``join_rule`` is ``public``, allow.
|
||||
|
||||
#. Otherwise, reject.
|
||||
|
||||
#. If ``membership`` is ``invite``:
|
||||
|
||||
i. If ``content`` has ``third_party_invite`` key:
|
||||
|
||||
#. If *target user* is banned, reject.
|
||||
|
||||
#. If ``content.third_party_invite`` does not have a
|
||||
``signed`` key, reject.
|
||||
|
||||
#. If ``signed`` does not have ``mxid`` and ``token`` keys, reject.
|
||||
|
||||
#. If ``mxid`` does not match ``state_key``, reject.
|
||||
|
||||
#. If there is no ``m.room.third_party_invite`` event in the
|
||||
current room state with ``state_key`` matching ``token``, reject.
|
||||
|
||||
#. If ``sender`` does not match ``sender`` of the
|
||||
``m.room.third_party_invite``, reject.
|
||||
|
||||
#. If any signature in ``signed`` matches any public key in the
|
||||
``m.room.third_party_invite`` event, allow. The public keys are
|
||||
in ``content`` of ``m.room.third_party_invite`` as:
|
||||
|
||||
#. A single public key in the ``public_key`` field.
|
||||
#. A list of public keys in the ``public_keys`` field.
|
||||
|
||||
#. Otherwise, reject.
|
||||
|
||||
#. If the ``sender``'s current membership state is not ``join``, reject.
|
||||
|
||||
#. If *target user*'s current membership state is ``join`` or ``ban``,
|
||||
reject.
|
||||
|
||||
#. If the ``sender``'s power level is greater than or equal to the *invite
|
||||
level*, allow.
|
||||
|
||||
#. Otherwise, reject.
|
||||
|
||||
#. If ``membership`` is ``leave``:
|
||||
|
||||
i. If the ``sender`` matches ``state_key``, allow if and only if that user's
|
||||
current membership state is ``invite`` or ``join``.
|
||||
|
||||
#. If the ``sender``'s current membership state is not ``join``, reject.
|
||||
|
||||
#. If the *target user*'s current membership state is ``ban``, and the
|
||||
``sender``'s power level is less than the *ban level*, reject.
|
||||
|
||||
#. If the ``sender``'s power level is greater than or equal to the *kick
|
||||
level*, and the *target user*'s power level is less than the
|
||||
``sender``'s power level, allow.
|
||||
|
||||
#. Otherwise, reject.
|
||||
|
||||
#. If ``membership`` is ``ban``:
|
||||
|
||||
i. If the ``sender``'s current membership state is not ``join``, reject.
|
||||
|
||||
#. If the ``sender``'s power level is greater than or equal to the *ban
|
||||
level*, and the *target user*'s power level is less than the
|
||||
``sender``'s power level, allow.
|
||||
|
||||
#. Otherwise, reject.
|
||||
|
||||
#. Otherwise, the membership is unknown. Reject.
|
||||
|
||||
#. If the ``sender``'s current membership state is not ``join``, reject.
|
||||
|
||||
#. If type is ``m.room.third_party_invite``:
|
||||
|
||||
a. Allow if and only if ``sender``'s current power level is greater than
|
||||
or equal to the *invite level*.
|
||||
|
||||
#. If the event type's *required power level* is greater than the ``sender``'s power
|
||||
level, reject.
|
||||
|
||||
#. If the event has a ``state_key`` that starts with an ``@`` and does not match
|
||||
the ``sender``, reject.
|
||||
|
||||
#. If type is ``m.room.power_levels``:
|
||||
|
||||
a. If ``users`` key in ``content`` is not a dictionary with keys that are
|
||||
valid user IDs with values that are integers (or a string that is an
|
||||
integer), reject.
|
||||
|
||||
#. If there is no previous ``m.room.power_levels`` event in the room, allow.
|
||||
|
||||
#. For each of the keys ``users_default``, ``events_default``,
|
||||
``state_default``, ``ban``, ``redact``, ``kick``, ``invite``, as well as
|
||||
each entry being changed under the ``events`` or ``users`` keys:
|
||||
|
||||
i. If the current value is higher than the ``sender``'s current power level,
|
||||
reject.
|
||||
|
||||
#. If the new value is higher than the ``sender``'s current power level,
|
||||
reject.
|
||||
|
||||
#. For each entry being changed under the ``users`` key, other than the
|
||||
``sender``'s own entry:
|
||||
|
||||
i. If the current value is equal to the ``sender``'s current power level,
|
||||
reject.
|
||||
|
||||
#. Otherwise, allow.
|
||||
|
||||
#. If type is ``m.room.redaction``:
|
||||
|
||||
a. If the ``sender``'s power level is greater than or equal to the *redact
|
||||
level*, allow.
|
||||
|
||||
#. If the domain of the ``event_id`` of the event being redacted is the same
|
||||
as the domain of the ``event_id`` of the ``m.room.redaction``, allow.
|
||||
|
||||
#. Otherwise, reject.
|
||||
|
||||
#. Otherwise, allow.
|
||||
|
||||
.. NOTE::
|
||||
|
||||
Some consequences of these rules:
|
||||
|
||||
* Unless you are a member of the room, the only permitted operations (apart
|
||||
from the intial create/join) are: joining a public room; accepting or
|
||||
rejecting an invitation to a room.
|
||||
|
||||
* To unban somebody, you must have power level greater than or equal to both
|
||||
the kick *and* ban levels, *and* greater than the target user's power
|
||||
level.
|
||||
|
||||
specified above. Each room version can have a different algorithm for how the
|
||||
rules work, and which rules are applied. For more detailed information, please
|
||||
see the `room version specification`_.
|
||||
|
||||
Rejection
|
||||
+++++++++
|
||||
|
@ -1230,6 +1052,24 @@ 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.
|
||||
|
||||
.. _`reference hashes`:
|
||||
|
||||
Calculating the reference hash for an event
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The *reference hash* of an event covers the essential fields of an event,
|
||||
including content hashes. It is calculated as follows.
|
||||
|
||||
1. The event is put through the redaction algorithm.
|
||||
|
||||
2. The ``signatures``, ``age_ts``, and ``unsigned`` properties are removed
|
||||
from the event, if present.
|
||||
|
||||
3. The event is converted into `Canonical JSON`_.
|
||||
|
||||
4. A sha256 hash is calculed on the resulting JSON object.
|
||||
|
||||
|
||||
Calculating the content hash for an event
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
|
|
@ -34,6 +34,10 @@ targets:
|
|||
files:
|
||||
- rooms/v2.rst
|
||||
version_label: v2
|
||||
rooms@v3: # this is translated to be rooms/v3.html
|
||||
files:
|
||||
- rooms/v3.rst
|
||||
version_label: v3
|
||||
appendices:
|
||||
files:
|
||||
- appendices.rst
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue