Merge remote matrix-org/master

pull/977/head
Travis Ralston 6 years ago
commit 219b27b182

@ -18,15 +18,17 @@ info:
host: localhost:8090 host: localhost:8090
schemes: schemes:
- https - https
- http
basePath: /_matrix/identity/api/v1 basePath: /_matrix/identity/api/v1
consumes:
- application/json
produces: produces:
- application/json - application/json
paths: paths:
"/3pid/getValidated3pid": "/3pid/getValidated3pid":
get: get:
summary: Check whether ownership of a 3pid was validated. summary: Check whether ownership of a 3pid was validated.
description: A client can check whether ownership of a 3pid was validated description: |-
Determines if a given 3pid has been validated by a user.
operationId: getValidated3pid operationId: getValidated3pid
parameters: parameters:
- in: query - in: query
@ -61,7 +63,9 @@ paths:
description: The address of the 3pid being looked up. description: The address of the 3pid being looked up.
validated_at: validated_at:
type: integer type: integer
description: Timestamp indicating the time that the 3pid was validated. description: |-
Timestamp, in milliseconds, indicating the time that the 3pid
was validated.
required: ['medium', 'address', 'validated_at'] required: ['medium', 'address', 'validated_at']
400: 400:
description: |- description: |-
@ -78,7 +82,7 @@ paths:
schema: schema:
$ref: "../client-server/definitions/errors/error.yaml" $ref: "../client-server/definitions/errors/error.yaml"
404: 404:
description: The Session ID or client secret were not found description: The Session ID or client secret were not found.
examples: examples:
application/json: { application/json: {
"errcode": "M_NO_VALID_SESSION", "errcode": "M_NO_VALID_SESSION",
@ -95,7 +99,7 @@ paths:
Future calls to ``/lookup`` for any of the session\'s 3pids will return Future calls to ``/lookup`` for any of the session\'s 3pids will return
this association. this association.
Note: for backwards compatibility with older versions of this Note: for backwards compatibility with previous drafts of this
specification, the parameters may also be specified as specification, the parameters may also be specified as
``application/x-form-www-urlencoded`` data. However, this usage is ``application/x-form-www-urlencoded`` data. However, this usage is
deprecated. deprecated.
@ -132,7 +136,6 @@ paths:
"not_before": 1428825849161, "not_before": 1428825849161,
"not_after": 4582425849161, "not_after": 4582425849161,
"ts": 1428825849161, "ts": 1428825849161,
"signatures": { "signatures": {
"matrix.org": { "matrix.org": {
"ed25519:0": "ENiU2YORYUJgE6WBMitU0mppbQjidDLanAusj8XS2nVRHPu+0t42OKA/r6zV6i2MzUbNQ3c3MiLScJuSsOiVDQ" "ed25519:0": "ENiU2YORYUJgE6WBMitU0mppbQjidDLanAusj8XS2nVRHPu+0t42OKA/r6zV6i2MzUbNQ3c3MiLScJuSsOiVDQ"
@ -162,7 +165,10 @@ paths:
description: The unix timestamp at which the association was verified. description: The unix timestamp at which the association was verified.
signatures: signatures:
type: object type: object
description: The signatures of the verifying identity services which show that the association should be trusted, if you trust the verifying identity services. description: |-
The signatures of the verifying identity services which show that the
association should be trusted, if you trust the verifying identity
services.
$ref: "../../schemas/server-signatures.yaml" $ref: "../../schemas/server-signatures.yaml"
required: required:
- address - address

@ -18,8 +18,9 @@ info:
host: localhost:8090 host: localhost:8090
schemes: schemes:
- https - https
- http
basePath: /_matrix/identity/api/v1 basePath: /_matrix/identity/api/v1
consumes:
- application/json
produces: produces:
- application/json - application/json
paths: paths:
@ -34,13 +35,13 @@ paths:
that that user was able to read the email for that email address, and that that user was able to read the email for that email address, and
so we validate ownership of the email address. so we validate ownership of the email address.
Note that Home Servers offer APIs that proxy this API, adding Note that homeservers offer APIs that proxy this API, adding
additional behaviour on top, for example, additional behaviour on top, for example,
``/register/email/requestToken`` is designed specifically for use when ``/register/email/requestToken`` is designed specifically for use when
registering an account and therefore will inform the user if the email registering an account and therefore will inform the user if the email
address given is already registered on the server. address given is already registered on the server.
Note: for backwards compatibility with older versions of this Note: for backwards compatibility with previous drafts of this
specification, the parameters may also be specified as specification, the parameters may also be specified as
``application/x-form-www-urlencoded`` data. However, this usage is ``application/x-form-www-urlencoded`` data. However, this usage is
deprecated. deprecated.
@ -58,7 +59,7 @@ paths:
properties: properties:
client_secret: client_secret:
type: string type: string
description: A unique string used to identify the validation attempt description: A unique string used to identify the validation attempt.
email: email:
type: string type: string
description: The email address to validate. description: The email address to validate.
@ -119,7 +120,7 @@ paths:
associate the email address with any Matrix user ID. Specifically, associate the email address with any Matrix user ID. Specifically,
calls to ``/lookup`` will not show a binding. calls to ``/lookup`` will not show a binding.
Note: for backwards compatibility with older versions of this Note: for backwards compatibility with previous drafts of this
specification, the parameters may also be specified as specification, the parameters may also be specified as
``application/x-form-www-urlencoded`` data. However, this usage is ``application/x-form-www-urlencoded`` data. However, this usage is
deprecated. deprecated.

@ -18,8 +18,9 @@ info:
host: localhost:8090 host: localhost:8090
schemes: schemes:
- https - https
- http
basePath: /_matrix/identity/api/v1 basePath: /_matrix/identity/api/v1
consumes:
- application/json
produces: produces:
- application/json - application/json
paths: paths:
@ -29,7 +30,7 @@ paths:
description: |- description: |-
Sign invitation details. Sign invitation details.
The identity server will look up ``token`` which was stored in a call The identity service will look up ``token`` which was stored in a call
to ``store-invite``, and fetch the sender of the invite. to ``store-invite``, and fetch the sender of the invite.
operationId: blindlySignStuff operationId: blindlySignStuff
parameters: parameters:
@ -48,14 +49,14 @@ paths:
description: The Matrix user ID of the user accepting the invitation. description: The Matrix user ID of the user accepting the invitation.
token: token:
type: string type: string
description: Token from the call to ``store-invite`` description: The token from the call to ``store-invite``.
private_key: private_key:
type: string type: string
description: The private key, encoded as `Unpadded base64`_. description: The private key, encoded as `Unpadded base64`_.
required: ["mxid", "token", "private_key"] required: ["mxid", "token", "private_key"]
responses: responses:
200: 200:
description: The signedjson of the mxid, sender, and token. description: The signed JSON of the mxid, sender, and token.
schema: schema:
type: object type: object
properties: properties:
@ -85,7 +86,7 @@ paths:
"token": "abc123" "token": "abc123"
} }
404: 404:
description: Token was not found. description: The token was not found.
examples: examples:
application/json: { application/json: {
"errcode": "M_UNRECOGNIZED", "errcode": "M_UNRECOGNIZED",

@ -1,6 +1,7 @@
# Copyright 2016 OpenMarket Ltd # Copyright 2016 OpenMarket Ltd
# Copyright 2017 Kamax.io # Copyright 2017 Kamax.io
# Copyright 2017 New Vector Ltd # Copyright 2017 New Vector Ltd
# Copyright 2018 New Vector Ltd
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
@ -20,8 +21,9 @@ info:
host: localhost:8090 host: localhost:8090
schemes: schemes:
- https - https
- http
basePath: /_matrix/identity/api/v1 basePath: /_matrix/identity/api/v1
consumes:
- application/json
produces: produces:
- application/json - application/json
paths: paths:
@ -46,7 +48,7 @@ paths:
responses: responses:
200: 200:
description: description:
The association for that 3pid, or the empty object if no association is known. The association for that 3pid, or an empty object if no association is known.
examples: examples:
application/json: { application/json: {
"address": "louise@bobs.burgers", "address": "louise@bobs.burgers",
@ -66,10 +68,10 @@ paths:
properties: properties:
address: address:
type: string type: string
description: The 3pid address of the user being looked up. description: The 3pid address of the user being looked up, matching the address requested.
medium: medium:
type: string type: string
description: The literal string "email". description: A medium from the `3PID Types`_ Appendix, matching the medium requested.
mxid: mxid:
type: string type: string
description: The Matrix user ID associated with the 3pid. description: The Matrix user ID associated with the 3pid.
@ -126,7 +128,9 @@ paths:
#- type: 3PID Address #- type: 3PID Address
- type: string - type: string
- type: string - type: string
description: an array of arrays containing the `3PID Types`_ with the ``medium`` in first position and the ``address`` in second position. description: |-
An array of arrays containing the `3PID Types`_ with the ``medium``
in first position and the ``address`` in second position.
required: required:
- "threepids" - "threepids"
responses: responses:
@ -157,6 +161,9 @@ paths:
- type: string - type: string
- type: string - type: string
- type: string - type: string
description: an array of array containing the `3PID Types`_ with the ``medium`` in first position, the ``address`` in second position and Matrix ID in third position. description: |-
An array of array containing the `3PID Types`_ with the ``medium``
in first position, the ``address`` in second position and Matrix user
ID in third position.
required: required:
- "threepids" - "threepids"

@ -18,8 +18,9 @@ info:
host: localhost:8090 host: localhost:8090
schemes: schemes:
- https - https
- http
basePath: /_matrix/identity/api/v1 basePath: /_matrix/identity/api/v1
consumes:
- application/json
produces: produces:
- application/json - application/json
paths: paths:
@ -34,13 +35,13 @@ paths:
indicates that that user was able to read the SMS for that phone indicates that that user was able to read the SMS for that phone
number, and so we validate ownership of the phone number. number, and so we validate ownership of the phone number.
Note that Home Servers offer APIs that proxy this API, adding Note that homeservers offer APIs that proxy this API, adding
additional behaviour on top, for example, additional behaviour on top, for example,
``/register/msisdn/requestToken`` is designed specifically for use when ``/register/msisdn/requestToken`` is designed specifically for use when
registering an account and therefore will inform the user if the phone registering an account and therefore will inform the user if the phone
number given is already registered on the server. number given is already registered on the server.
Note: for backwards compatibility with older versions of this Note: for backwards compatibility with previous drafts of this
specification, the parameters may also be specified as specification, the parameters may also be specified as
``application/x-form-www-urlencoded`` data. However, this usage is ``application/x-form-www-urlencoded`` data. However, this usage is
deprecated. deprecated.
@ -106,6 +107,8 @@ paths:
- ``M_INVALID_ADDRESS``: The phone number provided was invalid. - ``M_INVALID_ADDRESS``: The phone number provided was invalid.
- ``M_SEND_ERROR``: The validation SMS could not be sent. - ``M_SEND_ERROR``: The validation SMS could not be sent.
- ``M_DESTINATION_REJECTED``: The identity service cannot deliver an
SMS to the provided country or region.
examples: examples:
application/json: { application/json: {
"errcode": "M_INVALID_ADDRESS", "errcode": "M_INVALID_ADDRESS",
@ -125,7 +128,7 @@ paths:
associate the phone number address with any Matrix user associate the phone number address with any Matrix user
ID. Specifically, calls to ``/lookup`` will not show a binding. ID. Specifically, calls to ``/lookup`` will not show a binding.
Note: for backwards compatibility with older versions of this Note: for backwards compatibility with previous drafts of this
specification, the parameters may also be specified as specification, the parameters may also be specified as
``application/x-form-www-urlencoded`` data. However, this usage is ``application/x-form-www-urlencoded`` data. However, this usage is
deprecated. deprecated.

@ -1,4 +1,5 @@
# Copyright 2018 Kamax Sàrl # Copyright 2018 Kamax Sàrl
# Copyright 2018 New Vector Ltd
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
@ -14,7 +15,7 @@
swagger: "2.0" swagger: "2.0"
info: info:
title: "Matrix Client-Identity Versions API" title: "Matrix Identity Service Ping API"
version: "1.0.0" version: "1.0.0"
host: localhost:8090 host: localhost:8090
schemes: schemes:
@ -25,19 +26,19 @@ produces:
paths: paths:
"/api/v1": "/api/v1":
get: get:
summary: Checks that an Identity server is available at this API endpopint. summary: Checks that an Identity Service is available at this API endpoint.
description: |- description: |-
Checks that an Identity server is available at this API endpopint. Checks that an Identity Service is available at this API endpoint.
To discover that an Identity server is available at a specific URL, To discover that an Identity Service is available at a specific URL,
this endpoint can be queried and will return an empty object. this endpoint can be queried and will return an empty object.
This is primarly used for auto-discovery and health check purposes This is primarly used for auto-discovery and health check purposes
by entities acting as a client for the Identity server. by entities acting as a client for the Identity Service.
operationId: ping operationId: ping
responses: responses:
200: 200:
description: An Identity server is ready to serve requests. description: An Identity Service is ready to serve requests.
examples: examples:
application/json: {} application/json: {}
schema: schema:

@ -18,8 +18,9 @@ info:
host: localhost:8090 host: localhost:8090
schemes: schemes:
- https - https
- http
basePath: /_matrix/identity/api/v1 basePath: /_matrix/identity/api/v1
consumes:
- application/json
produces: produces:
- application/json - application/json
paths: paths:

@ -18,16 +18,17 @@ info:
host: localhost:8090 host: localhost:8090
schemes: schemes:
- https - https
- http
basePath: /_matrix/identity/api/v1 basePath: /_matrix/identity/api/v1
consumes:
- application/json
produces: produces:
- application/json - application/json
paths: paths:
"/store-invite": "/store-invite":
post: post:
summary: Store pending invitations to a user\'s 3pid. summary: Store pending invitations to a user's 3pid.
description: |- description: |-
Store pending invitations to a user\'s 3pid. Store pending invitations to a user's 3pid.
In addition to the request parameters specified below, an arbitrary In addition to the request parameters specified below, an arbitrary
number of other parameters may also be specified. These may be used in number of other parameters may also be specified. These may be used in
@ -47,6 +48,8 @@ paths:
Also, the generated ephemeral public key will be listed as valid on Also, the generated ephemeral public key will be listed as valid on
requests to ``/_matrix/identity/api/v1/pubkey/ephemeral/isvalid``. requests to ``/_matrix/identity/api/v1/pubkey/ephemeral/isvalid``.
Currently, invites may only be issued for 3pids of the ``email`` medium.
operationId: storeInvite operationId: storeInvite
parameters: parameters:
- in: body - in: body
@ -84,7 +87,7 @@ paths:
description: The generated token. description: The generated token.
public_keys: public_keys:
type: array type: array
description: A list of [server\'s long-term public key, generated ephemeral public key]. description: A list of [server's long-term public key, generated ephemeral public key].
items: items:
type: string type: string
display_name: display_name:
@ -111,7 +114,7 @@ paths:
application/json: { application/json: {
"errcode": "M_THREEPID_IN_USE", "errcode": "M_THREEPID_IN_USE",
"error": "Binding already known", "error": "Binding already known",
"mxid": mxid "mxid": "@alice:example.com"
} }
schema: schema:
$ref: "../client-server/definitions/errors/error.yaml" $ref: "../client-server/definitions/errors/error.yaml"

@ -23,7 +23,8 @@ allOf:
hashes: hashes:
type: object type: object
title: Event Hash title: Event Hash
description: Hashes of the PDU, following the algorithm specified in `Signing Events`_. description: |-
Content hashes of the PDU, following the algorithm specified in `Signing Events`_.
example: { example: {
"sha256": "thishashcoversallfieldsincasethisisredacted" "sha256": "thishashcoversallfieldsincasethisisredacted"
} }

@ -55,8 +55,8 @@ properties:
prev_events: prev_events:
type: array type: array
description: |- description: |-
Event IDs and hashes of the most recent events in the room that the homeserver was aware Event IDs and reference hashes for the most recent events in the room
of when it made this event. that the homeserver was aware of when it made this event.
items: items:
type: array type: array
maxItems: 2 maxItems: 2
@ -86,7 +86,7 @@ properties:
auth_events: auth_events:
type: array type: array
description: |- description: |-
An event reference list containing the authorization events that would Event IDs and reference hashes for the authorization events that would
allow this event to be in the room. allow this event to be in the room.
items: items:
type: array type: array

@ -0,0 +1 @@
Update all event examples to be accurate representations of their associated events.

@ -0,0 +1,30 @@
[tool.towncrier]
filename = "../identity_service.rst"
directory = "newsfragments"
issue_format = "`#{issue} <https://github.com/matrix-org/matrix-doc/issues/{issue}>`_"
title_format = "{version}"
[[tool.towncrier.type]]
directory = "breaking"
name = "Breaking Changes"
showcontent = true
[[tool.towncrier.type]]
directory = "deprecation"
name = "Deprecations"
showcontent = true
[[tool.towncrier.type]]
directory = "new"
name = "New Endpoints"
showcontent = true
[[tool.towncrier.type]]
directory = "feature"
name = "Backwards Compatible Changes"
showcontent = true
[[tool.towncrier.type]]
directory = "clarification"
name = "Spec Clarifications"
showcontent = true

@ -0,0 +1,30 @@
[tool.towncrier]
filename = "../server_server.rst"
directory = "newsfragments"
issue_format = "`#{issue} <https://github.com/matrix-org/matrix-doc/issues/{issue}>`_"
title_format = "{version}"
[[tool.towncrier.type]]
directory = "breaking"
name = "Breaking Changes"
showcontent = true
[[tool.towncrier.type]]
directory = "deprecation"
name = "Deprecations"
showcontent = true
[[tool.towncrier.type]]
directory = "new"
name = "New Endpoints"
showcontent = true
[[tool.towncrier.type]]
directory = "feature"
name = "Backwards Compatible Changes"
showcontent = true
[[tool.towncrier.type]]
directory = "clarification"
name = "Spec Clarifications"
showcontent = true

@ -44,16 +44,51 @@ except ImportError as e:
raise raise
def load_file(path):
print("Loading reference: %s" % path)
if not path.startswith("file://"):
raise Exception("Bad ref: %s" % (path,))
path = path[len("file://"):]
with open(path, "r") as f:
if path.endswith(".json"):
return json.load(f)
else:
# We have to assume it's YAML because some of the YAML examples
# do not have file extensions.
return yaml.load(f)
def resolve_references(path, schema):
if isinstance(schema, dict):
# do $ref first
if '$ref' in schema:
value = schema['$ref']
path = os.path.abspath(os.path.join(os.path.dirname(path), value))
ref = load_file("file://" + path)
result = resolve_references(path, ref)
del schema['$ref']
else:
result = {}
for key, value in schema.items():
result[key] = resolve_references(path, value)
return result
elif isinstance(schema, list):
return [resolve_references(path, value) for value in schema]
else:
return schema
def check_example_file(examplepath, schemapath): def check_example_file(examplepath, schemapath):
with open(examplepath) as f: with open(examplepath) as f:
example = yaml.load(f) example = resolve_references(examplepath, json.load(f))
with open(schemapath) as f: with open(schemapath) as f:
schema = yaml.load(f) schema = yaml.load(f)
fileurl = "file://" + os.path.abspath(schemapath) fileurl = "file://" + os.path.abspath(schemapath)
schema["id"] = fileurl schema["id"] = fileurl
resolver = jsonschema.RefResolver(schemapath, schema, handlers={"file": load_yaml}) resolver = jsonschema.RefResolver(schemapath, schema, handlers={"file": load_file})
print ("Checking schema for: %r %r" % (examplepath, schemapath)) print ("Checking schema for: %r %r" % (examplepath, schemapath))
try: try:
@ -71,6 +106,10 @@ def check_example_dir(exampledir, schemadir):
if filename.startswith("."): if filename.startswith("."):
# Skip over any vim .swp files. # Skip over any vim .swp files.
continue continue
cwd = os.path.basename(os.path.dirname(os.path.join(root, filename)))
if cwd == "core":
# Skip checking the underlying definitions
continue
examplepath = os.path.join(root, filename) examplepath = os.path.join(root, filename)
schemapath = examplepath.replace(exampledir, schemadir) schemapath = examplepath.replace(exampledir, schemadir)
if schemapath.find("#") >= 0: if schemapath.find("#") >= 0:
@ -85,14 +124,6 @@ def check_example_dir(exampledir, schemadir):
raise ValueError("Error validating examples") raise ValueError("Error validating examples")
def load_yaml(path):
if not path.startswith("file:///"):
raise Exception("Bad ref: %s" % (path,))
path = path[len("file://"):]
with open(path, "r") as f:
return yaml.load(f)
if __name__ == '__main__': if __name__ == '__main__':
try: try:
check_example_dir("examples", "schema") check_example_dir("examples", "schema")

@ -0,0 +1,6 @@
{
"content": {
"key": "value"
},
"type": "org.example.custom.event"
}

@ -0,0 +1,4 @@
{
"$ref": "event.json",
"room_id": "!jEsUZKDJdhlrceRyVU:domain.com"
}

@ -0,0 +1,10 @@
{
"$ref": "event.json",
"event_id": "$143273582443PhrSn:domain.com",
"room_id": "!jEsUZKDJdhlrceRyVU:domain.com",
"sender": "@example:domain.com",
"origin_server_ts": 1432735824653,
"unsigned": {
"age": 1234
}
}

@ -0,0 +1,4 @@
{
"$ref": "room_event.json",
"state_key": "ArbitraryString"
}

@ -1,5 +1,6 @@
{ {
"age": 242352, "$ref": "core/room_event.json",
"type": "m.call.answer",
"content": { "content": {
"version" : 0, "version" : 0,
"call_id": "12345", "call_id": "12345",
@ -8,10 +9,5 @@
"type" : "answer", "type" : "answer",
"sdp" : "v=0\r\no=- 6584580628695956864 2 IN IP4 127.0.0.1[...]" "sdp" : "v=0\r\no=- 6584580628695956864 2 IN IP4 127.0.0.1[...]"
} }
}, }
"origin_server_ts": 1431961217939,
"event_id": "$WLGTSEFSEF:localhost",
"type": "m.call.answer",
"room_id": "!Cuyf34gef24t:localhost",
"sender": "@example:localhost"
} }

@ -1,5 +1,6 @@
{ {
"age": 242352, "$ref": "core/room_event.json",
"type": "m.call.candidates",
"content": { "content": {
"version" : 0, "version" : 0,
"call_id": "12345", "call_id": "12345",
@ -10,10 +11,5 @@
"candidate": "candidate:863018703 1 udp 2122260223 10.9.64.156 43670 typ host generation 0" "candidate": "candidate:863018703 1 udp 2122260223 10.9.64.156 43670 typ host generation 0"
} }
] ]
}, }
"origin_server_ts": 1431961217939,
"event_id": "$WLGTSEFSEF:localhost",
"type": "m.call.candidates",
"room_id": "!Cuyf34gef24t:localhost",
"sender": "@example:localhost"
} }

@ -1,12 +1,8 @@
{ {
"age": 242352, "$ref": "core/room_event.json",
"type": "m.call.hangup",
"content": { "content": {
"version" : 0, "version" : 0,
"call_id": "12345" "call_id": "12345"
}, }
"origin_server_ts": 1431961217939,
"event_id": "$WLGTSEFSEF:localhost",
"type": "m.call.hangup",
"room_id": "!Cuyf34gef24t:localhost",
"sender": "@example:localhost"
} }

@ -1,5 +1,6 @@
{ {
"age": 242352, "$ref": "core/room_event.json",
"type": "m.call.invite",
"content": { "content": {
"version" : 0, "version" : 0,
"call_id": "12345", "call_id": "12345",
@ -8,10 +9,5 @@
"type" : "offer", "type" : "offer",
"sdp" : "v=0\r\no=- 6584580628695956864 2 IN IP4 127.0.0.1[...]" "sdp" : "v=0\r\no=- 6584580628695956864 2 IN IP4 127.0.0.1[...]"
} }
}, }
"origin_server_ts": 1431961217939,
"event_id": "$WLGTSEFSEF:localhost",
"type": "m.call.invite",
"room_id": "!Cuyf34gef24t:localhost",
"sender": "@example:localhost"
} }

@ -1,4 +1,5 @@
{ {
"$ref": "core/event.json",
"type": "m.direct", "type": "m.direct",
"content": { "content": {
"@bob:example.com": [ "@bob:example.com": [

@ -1,4 +1,5 @@
{ {
"$ref": "core/event.json",
"type": "m.ignored_user_list", "type": "m.ignored_user_list",
"content": { "content": {
"ignored_users": { "ignored_users": {

@ -1,10 +1,11 @@
{ {
"$ref": "core/event.json",
"sender": "@example:localhost",
"type": "m.presence",
"content": { "content": {
"avatar_url": "mxc://localhost:wefuiwegh8742w", "avatar_url": "mxc://localhost:wefuiwegh8742w",
"last_active_ago": 2478593, "last_active_ago": 2478593,
"presence": "online", "presence": "online",
"currently_active": false "currently_active": false
}, }
"sender": "@example:localhost",
"type": "m.presence"
} }

@ -1,6 +1,6 @@
{ {
"$ref": "core/room_edu.json",
"type": "m.receipt", "type": "m.receipt",
"room_id": "!KpjVgQyZpzBwvMBsnT:matrix.org",
"content": { "content": {
"$1435641916114394fHBLK:matrix.org": { "$1435641916114394fHBLK:matrix.org": {
"m.read": { "m.read": {

@ -1,12 +1,8 @@
{ {
"age": 242352, "$ref": "core/state_event.json",
"content": { "state_key": "domain.com",
"aliases": ["#somewhere:localhost", "#another:localhost"]
},
"state_key": "localhost",
"origin_server_ts": 1431961217939,
"event_id": "$WLGTSEFSEF:localhost",
"type": "m.room.aliases", "type": "m.room.aliases",
"room_id": "!Cuyf34gef24t:localhost", "content": {
"sender": "@example:localhost" "aliases": ["#somewhere:domain.com", "#another:domain.com"]
}
} }

@ -1,5 +1,7 @@
{ {
"age": 242352, "$ref": "core/state_event.json",
"type": "m.room.avatar",
"state_key": "",
"content": { "content": {
"info": { "info": {
"h": 398, "h": 398,
@ -7,12 +9,6 @@
"mimetype": "image/jpeg", "mimetype": "image/jpeg",
"size": 31037 "size": 31037
}, },
"url": "mxc://localhost/JWEIFJgwEIhweiWJE" "url": "mxc://domain.com/JWEIFJgwEIhweiWJE"
}, }
"origin_server_ts": 1431961217939,
"event_id": "$WLGTSEFSEF:localhost",
"type": "m.room.avatar",
"state_key": "",
"room_id": "!Cuyf34gef24t:localhost",
"sender": "@example:localhost"
} }

@ -1,12 +1,8 @@
{ {
"age": 242352, "$ref": "core/state_event.json",
"type": "m.room.canonical_alias",
"state_key": "",
"content": { "content": {
"alias": "#somewhere:localhost" "alias": "#somewhere:localhost"
}, }
"state_key": "",
"origin_server_ts": 1431961217939,
"event_id": "$WLGTSEFSEF:localhost",
"type": "m.room.canonical_alias",
"room_id": "!Cuyf34gef24t:localhost",
"sender": "@example:localhost"
} }

@ -1,12 +1,10 @@
{ {
"age": 242352, "$ref": "core/state_event.json",
"content": {
"creator": "@example:localhost"
},
"state_key": "",
"origin_server_ts": 1431961217939,
"event_id": "$WLGTSEFSEF:localhost",
"type": "m.room.create", "type": "m.room.create",
"room_id": "!Cuyf34gef24t:localhost", "state_key": "",
"sender": "@example:localhost" "content": {
"creator": "@example:domain.com",
"room_version": "1",
"m.federate": true
}
} }

@ -1,14 +1,11 @@
{ {
"$ref": "core/room_event.json",
"type": "m.room.encrypted",
"content": { "content": {
"algorithm": "m.megolm.v1.aes-sha2", "algorithm": "m.megolm.v1.aes-sha2",
"ciphertext": "AwgAEnACgAkLmt6qF84IK++J7UDH2Za1YVchHyprqTqsg...", "ciphertext": "AwgAEnACgAkLmt6qF84IK++J7UDH2Za1YVchHyprqTqsg...",
"device_id": "RJYKSTBOIE", "device_id": "RJYKSTBOIE",
"sender_key": "IlRMeOPX2e0MurIyfWEucYBRVOEEUMrOHqn/8mLqMjA", "sender_key": "IlRMeOPX2e0MurIyfWEucYBRVOEEUMrOHqn/8mLqMjA",
"session_id": "X3lUlvLELLYxeTx4yOVu6UDpasGEVO0Jbu+QFnm0cKQ" "session_id": "X3lUlvLELLYxeTx4yOVu6UDpasGEVO0Jbu+QFnm0cKQ"
}, }
"event_id": "$WLGTSEFSEF:localhost",
"room_id": "!Cuyf34gef24t:localhost",
"origin_server_ts": 1476648761524,
"sender": "@example:localhost",
"type": "m.room.encrypted"
} }

@ -1,6 +1,6 @@
{ {
"$ref": "core/room_event.json",
"type": "m.room.encrypted", "type": "m.room.encrypted",
"sender": "@example:localhost",
"content": { "content": {
"algorithm": "m.olm.v1.curve25519-aes-sha2", "algorithm": "m.olm.v1.curve25519-aes-sha2",
"sender_key": "Szl29ksW/L8yZGWAX+8dY1XyFi+i5wm+DRhTGkbMiwU", "sender_key": "Szl29ksW/L8yZGWAX+8dY1XyFi+i5wm+DRhTGkbMiwU",

@ -1,13 +1,10 @@
{ {
"$ref": "core/state_event.json",
"type": "m.room.encryption",
"state_key": "",
"content": { "content": {
"algorithm": "m.megolm.v1.aes-sha2", "algorithm": "m.megolm.v1.aes-sha2",
"rotation_period_ms": 604800000, "rotation_period_ms": 604800000,
"rotation_period_msgs": 100 "rotation_period_msgs": 100
}, }
"event_id": "$WLGTSEFJJKJ:localhost",
"origin_server_ts": 1476648761524,
"sender": "@example:localhost",
"room_id": "!Cuyf34gef24t:localhost",
"state_key": "",
"type": "m.room.encryption"
} }

@ -1,12 +1,8 @@
{ {
"age": 242353, "$ref": "core/state_event.json",
"type": "m.room.guest_access",
"state_key": "",
"content": { "content": {
"guest_access": "can_join" "guest_access": "can_join"
}, }
"state_key": "",
"origin_server_ts": 1431961217938,
"event_id": "$WLGTSEFSEG:localhost",
"type": "m.room.guest_access",
"room_id": "!Cuyf34gef24u:localhost",
"sender": "@example:localhost"
} }

@ -1,12 +1,8 @@
{ {
"age": 242352, "$ref": "core/state_event.json",
"type": "m.room.history_visibility",
"state_key": "",
"content": { "content": {
"history_visibility": "shared" "history_visibility": "shared"
}, }
"state_key": "",
"origin_server_ts": 1431961217939,
"event_id": "$WLGTSEFSEF:localhost",
"type": "m.room.history_visibility",
"room_id": "!Cuyf34gef24t:localhost",
"sender": "@example:localhost"
} }

@ -1,12 +1,8 @@
{ {
"age": 242352, "$ref": "core/state_event.json",
"type": "m.room.join_rules",
"state_key": "",
"content": { "content": {
"join_rule": "public" "join_rule": "public"
}, }
"state_key": "",
"origin_server_ts": 1431961217939,
"event_id": "$WLGTSEFSEF:localhost",
"type": "m.room.join_rules",
"room_id": "!Cuyf34gef24t:localhost",
"sender": "@example:localhost"
} }

@ -1,14 +1,10 @@
{ {
"age": 242352, "$ref": "core/state_event.json",
"state_key": "@alice:domain.com",
"type": "m.room.member",
"content": { "content": {
"membership": "join", "membership": "join",
"avatar_url": "mxc://localhost/SEsfnsuifSDFSSEF#auto", "avatar_url": "mxc://domain.com/SEsfnsuifSDFSSEF#auto",
"displayname": "Alice Margatroid" "displayname": "Alice Margatroid"
}, }
"state_key": "@alice:localhost",
"origin_server_ts": 1431961217939,
"event_id": "$WLGTSEFSEF:localhost",
"type": "m.room.member",
"room_id": "!Cuyf34gef24t:localhost",
"sender": "@example:localhost"
} }

@ -1,11 +1,12 @@
{ {
"age": 242352, "$ref": "m.room.member",
"content": { "content": {
"membership": "invite", "membership": "invite",
"avatar_url": "mxc://localhost/SEsfnsuifSDFSSEF#auto", "avatar_url": "mxc://domain.com/SEsfnsuifSDFSSEF#auto",
"displayname": "Alice Margatroid" "displayname": "Alice Margatroid"
}, },
"unsigned": { "unsigned": {
"age": 1234,
"invite_room_state": [ "invite_room_state": [
{ {
"type": "m.room.name", "type": "m.room.name",
@ -22,11 +23,5 @@
} }
} }
] ]
}, }
"state_key": "@alice:localhost",
"origin_server_ts": 1431961217939,
"event_id": "$WLGTSEFSEF:localhost",
"type": "m.room.member",
"room_id": "!Cuyf34gef24t:localhost",
"sender": "@example:localhost"
} }

@ -1,13 +1,13 @@
{ {
"age": 242352, "$ref": "m.room.member",
"content": { "content": {
"membership": "invite", "membership": "invite",
"avatar_url": "mxc://localhost/SEsfnsuifSDFSSEF#auto", "avatar_url": "mxc://domain.com/SEsfnsuifSDFSSEF#auto",
"displayname": "Alice Margatroid", "displayname": "Alice Margatroid",
"third_party_invite": { "third_party_invite": {
"display_name": "alice", "display_name": "alice",
"signed": { "signed": {
"mxid": "@alice:localhost", "mxid": "@alice:domain.com",
"signatures": { "signatures": {
"magic.forest": { "magic.forest": {
"ed25519:3": "fQpGIW1Snz+pwLZu6sTy2aHy/DYWWTspTJRPyNp0PKkymfIsNffysMl6ObMMFdIJhk6g6pwlIqZ54rxo8SLmAg" "ed25519:3": "fQpGIW1Snz+pwLZu6sTy2aHy/DYWWTspTJRPyNp0PKkymfIsNffysMl6ObMMFdIJhk6g6pwlIqZ54rxo8SLmAg"
@ -16,11 +16,5 @@
"token": "abc123" "token": "abc123"
} }
} }
}, }
"state_key": "@alice:localhost",
"origin_server_ts": 1431961217939,
"event_id": "$WLGTSEFSEF:localhost",
"type": "m.room.member",
"room_id": "!Cuyf34gef24t:localhost",
"sender": "@example:localhost"
} }

@ -1,18 +1,14 @@
{ {
"age": 146, "$ref": "core/room_event.json",
"type": "m.room.message",
"content": { "content": {
"body": "Bee Gees - Stayin' Alive", "body": "Bee Gees - Stayin' Alive",
"url": "mxc://localhost/ffed755USFFxlgbQYZGtryd", "url": "mxc://domain.com/ffed755USFFxlgbQYZGtryd",
"info": { "info": {
"duration": 2140786, "duration": 2140786,
"size": 1563685, "size": 1563685,
"mimetype": "audio/mpeg" "mimetype": "audio/mpeg"
}, },
"msgtype": "m.audio" "msgtype": "m.audio"
}, }
"event_id": "$143273582443PhrSn:localhost",
"origin_server_ts": 1432735824653,
"room_id": "!jEsUZKDJdhlrceRyVU:localhost",
"type": "m.room.message",
"sender": "@example:localhost"
} }

@ -1,14 +1,10 @@
{ {
"age": 242352, "$ref": "core/room_event.json",
"type": "m.room.message",
"content": { "content": {
"body": "thinks this is an example emote", "body": "thinks this is an example emote",
"msgtype": "m.emote", "msgtype": "m.emote",
"format": "org.matrix.custom.html", "format": "org.matrix.custom.html",
"formatted_body": "thinks <b>this</b> is an example emote" "formatted_body": "thinks <b>this</b> is an example emote"
}, }
"origin_server_ts": 1431961217939,
"event_id": "$WLGTSEFSEF:localhost",
"type": "m.room.message",
"room_id": "!Cuyf34gef24t:localhost",
"sender": "@example:localhost"
} }

@ -1,5 +1,6 @@
{ {
"age": 146, "$ref": "core/room_event.json",
"type": "m.room.message",
"content": { "content": {
"body": "something-important.doc", "body": "something-important.doc",
"filename": "something-important.doc", "filename": "something-important.doc",
@ -8,11 +9,6 @@
"size": 46144 "size": 46144
}, },
"msgtype": "m.file", "msgtype": "m.file",
"url": "mxc://localhost/FHyPlCeYUSFFxlgbQYZmoEoe" "url": "mxc://domain.com/FHyPlCeYUSFFxlgbQYZmoEoe"
}, }
"event_id": "$143273582443PhrSn:localhost",
"origin_server_ts": 1432735824653,
"room_id": "!jEsUZKDJdhlrceRyVU:localhost",
"type": "m.room.message",
"sender": "@example:localhost"
} }

@ -1,5 +1,6 @@
{ {
"age": 242352, "$ref": "core/room_event.json",
"type": "m.room.message",
"content": { "content": {
"body": "filename.jpg", "body": "filename.jpg",
"info": { "info": {
@ -8,12 +9,7 @@
"mimetype": "image/jpeg", "mimetype": "image/jpeg",
"size": 31037 "size": 31037
}, },
"url": "mxc://localhost/JWEIFJgwEIhweiWJE", "url": "mxc://domain.com/JWEIFJgwEIhweiWJE",
"msgtype": "m.image" "msgtype": "m.image"
}, }
"origin_server_ts": 1431961217939,
"event_id": "$WLGTSEFSEF:localhost",
"type": "m.room.message",
"room_id": "!Cuyf34gef24t:localhost",
"sender": "@example:localhost"
} }

@ -1,10 +1,11 @@
{ {
"age": 146, "$ref": "core/room_event.json",
"type": "m.room.message",
"content": { "content": {
"body": "Big Ben, London, UK", "body": "Big Ben, London, UK",
"geo_uri": "geo:51.5008,0.1247", "geo_uri": "geo:51.5008,0.1247",
"info": { "info": {
"thumbnail_url": "mxc://localhost/FHyPlCeYUSFFxlgbQYZmoEoe", "thumbnail_url": "mxc://domain.com/FHyPlCeYUSFFxlgbQYZmoEoe",
"thumbnail_info": { "thumbnail_info": {
"mimetype": "image/jpeg", "mimetype": "image/jpeg",
"size": 46144, "size": 46144,
@ -13,10 +14,5 @@
} }
}, },
"msgtype": "m.location" "msgtype": "m.location"
}, }
"event_id": "$143273582443PhrSn:localhost",
"origin_server_ts": 1432735824653,
"room_id": "!jEsUZKDJdhlrceRyVU:localhost",
"type": "m.room.message",
"sender": "@example:localhost"
} }

@ -1,14 +1,10 @@
{ {
"age": 242352, "$ref": "core/room_event.json",
"type": "m.room.message",
"content": { "content": {
"body": "This is an example notice", "body": "This is an example notice",
"msgtype": "m.notice", "msgtype": "m.notice",
"format": "org.matrix.custom.html", "format": "org.matrix.custom.html",
"formatted_body": "This is an <strong>example</strong> notice" "formatted_body": "This is an <strong>example</strong> notice"
}, }
"origin_server_ts": 1431961217939,
"event_id": "$WLGTSEFSEF:localhost",
"type": "m.room.message",
"room_id": "!Cuyf34gef24t:localhost",
"sender": "@example:localhost"
} }

@ -1,14 +1,10 @@
{ {
"age": 242352, "$ref": "core/room_event.json",
"type": "m.room.message",
"content": { "content": {
"body": "This is an example text message", "body": "This is an example text message",
"msgtype": "m.text", "msgtype": "m.text",
"format": "org.matrix.custom.html", "format": "org.matrix.custom.html",
"formatted_body": "<b>This is an example text message</b>" "formatted_body": "<b>This is an example text message</b>"
}, }
"origin_server_ts": 1431961217939,
"event_id": "$WLGTSEFSEF:localhost",
"type": "m.room.message",
"room_id": "!Cuyf34gef24t:localhost",
"sender": "@example:localhost"
} }

@ -1,10 +1,11 @@
{ {
"age": 146, "$ref": "core/room_event.json",
"type": "m.room.message",
"content": { "content": {
"body": "Gangnam Style", "body": "Gangnam Style",
"url": "mxc://localhost/a526eYUSFFxlgbQYZmo442", "url": "mxc://domain.com/a526eYUSFFxlgbQYZmo442",
"info": { "info": {
"thumbnail_url": "mxc://localhost/FHyPlCeYUSFFxlgbQYZmoEoe", "thumbnail_url": "mxc://domain.com/FHyPlCeYUSFFxlgbQYZmoEoe",
"thumbnail_info": { "thumbnail_info": {
"mimetype": "image/jpeg", "mimetype": "image/jpeg",
"size": 46144, "size": 46144,
@ -18,10 +19,5 @@
"mimetype": "video/mp4" "mimetype": "video/mp4"
}, },
"msgtype": "m.video" "msgtype": "m.video"
}, }
"event_id": "$143273582443PhrSn:localhost",
"origin_server_ts": 1432735824653,
"room_id": "!jEsUZKDJdhlrceRyVU:localhost",
"type": "m.room.message",
"sender": "@example:localhost"
} }

@ -1,12 +1,8 @@
{ {
"age": 242352, "$ref": "core/room_event.json",
"type": "m.room.message.feedback",
"content": { "content": {
"type": "delivered", "type": "delivered",
"target_event_id": "$WEIGFHFW:localhost" "target_event_id": "$WEIGFHFW:localhost"
}, }
"origin_server_ts": 1431961217939,
"event_id": "$WLGTSEFSEF:localhost",
"type": "m.room.message.feedback",
"room_id": "!Cuyf34gef24t:localhost",
"sender": "@example:localhost"
} }

@ -1,12 +1,8 @@
{ {
"age": 242352, "$ref": "core/state_event.json",
"type": "m.room.name",
"state_key": "",
"content": { "content": {
"name": "The room name" "name": "The room name"
}, }
"state_key": "",
"origin_server_ts": 1431961217939,
"event_id": "$WLGTSEFSEF:localhost",
"type": "m.room.name",
"room_id": "!Cuyf34gef24t:localhost",
"sender": "@example:localhost"
} }

@ -1,12 +1,8 @@
{ {
"age": 242352, "$ref": "core/state_event.json",
"content": {
"pinned": ["$someevent:localhost"]
},
"state_key": "",
"origin_server_ts": 1431961217939,
"event_id": "$WLGTSEFSEF:localhost",
"type": "m.room.pinned_events", "type": "m.room.pinned_events",
"room_id": "!Cuyf34gef24t:localhost", "state_key": "",
"sender": "@example:localhost" "content": {
"pinned": ["$someevent:domain.com"]
}
} }

@ -1,5 +1,7 @@
{ {
"age": 242352, "$ref": "core/state_event.json",
"type": "m.room.power_levels",
"state_key": "",
"content": { "content": {
"ban": 50, "ban": 50,
"events": { "events": {
@ -18,11 +20,5 @@
"notifications": { "notifications": {
"room": 20 "room": 20
} }
}, }
"state_key": "",
"origin_server_ts": 1431961217939,
"event_id": "$WLGTSEFSEF:localhost",
"type": "m.room.power_levels",
"room_id": "!Cuyf34gef24t:localhost",
"sender": "@example:localhost"
} }

@ -1,14 +1,8 @@
{ {
"unsigned": { "$ref": "core/room_event.json",
"age": 242352
},
"content": {
"reason": "Spamming"
},
"origin_server_ts": 1431961217939,
"event_id": "$WLGTSEFSEF:localhost",
"type": "m.room.redaction", "type": "m.room.redaction",
"room_id": "!Cuyf34gef24t:localhost",
"redacts": "$fukweghifu23:localhost", "redacts": "$fukweghifu23:localhost",
"sender": "@example:localhost" "content": {
"reason": "Spamming"
}
} }

@ -1,5 +1,7 @@
{ {
"age": 242352, "$ref": "core/state_event.json",
"type": "m.room.third_party_invite",
"state_key": "pc98",
"content": { "content": {
"display_name": "Alice Margatroid", "display_name": "Alice Margatroid",
"key_validity_url": "https://magic.forest/verifykey", "key_validity_url": "https://magic.forest/verifykey",
@ -8,11 +10,5 @@
"public_key": "def456", "public_key": "def456",
"key_validity_url": "https://magic.forest/verifykey" "key_validity_url": "https://magic.forest/verifykey"
}] }]
}, }
"state_key": "pc98",
"origin_server_ts": 1431961217939,
"event_id": "$WLGTSEFSEF:localhost",
"type": "m.room.third_party_invite",
"room_id": "!Cuyf34gef24t:localhost",
"sender": "@example:localhost"
} }

@ -1,12 +1,8 @@
{ {
"age": 242352, "$ref": "core/state_event.json",
"type": "m.room.topic",
"state_key": "",
"content": { "content": {
"topic": "A room topic" "topic": "A room topic"
}, }
"state_key": "",
"origin_server_ts": 1431961217939,
"event_id": "$WLGTSEFSEF:localhost",
"type": "m.room.topic",
"room_id": "!Cuyf34gef24t:localhost",
"sender": "@example:localhost"
} }

@ -1,9 +1,10 @@
{ {
"$ref": "core/event.json",
"type": "m.room_key",
"content": { "content": {
"algorithm": "m.megolm.v1.aes-sha2", "algorithm": "m.megolm.v1.aes-sha2",
"room_id": "!Cuyf34gef24t:localhost", "room_id": "!Cuyf34gef24t:localhost",
"session_id": "X3lUlvLELLYxeTx4yOVu6UDpasGEVO0Jbu+QFnm0cKQ", "session_id": "X3lUlvLELLYxeTx4yOVu6UDpasGEVO0Jbu+QFnm0cKQ",
"session_key": "AgAAAADxKHa9uFxcXzwYoNueL5Xqi69IkD4sni8LlfJL7qNBEY..." "session_key": "AgAAAADxKHa9uFxcXzwYoNueL5Xqi69IkD4sni8LlfJL7qNBEY..."
}, }
"type": "m.room_key"
} }

@ -1,5 +1,6 @@
{ {
"age": 242352, "$ref": "core/room_event.json",
"type": "m.sticker",
"content": { "content": {
"body": "Landing", "body": "Landing",
"info": { "info": {
@ -16,10 +17,5 @@
"size": 73602 "size": 73602
}, },
"url": "mxc://matrix.org/sHhqkFCvSkFwtmvtETOtKnLP" "url": "mxc://matrix.org/sHhqkFCvSkFwtmvtETOtKnLP"
}, }
"origin_server_ts": 1431961217939,
"event_id": "$WLGTSEFSEF:localhost",
"type": "m.sticker",
"room_id": "!Cuyf34gef24t:localhost",
"sender": "@example:localhost"
} }

@ -1,4 +1,5 @@
{ {
"$ref": "core/event.json",
"type": "m.tag", "type": "m.tag",
"content": { "content": {
"tags": { "tags": {

@ -1,6 +1,6 @@
{ {
"$ref": "core/room_edu.json",
"type": "m.typing", "type": "m.typing",
"room_id": "!z0mnsuiwhifuhwwfw:matrix.org",
"content": { "content": {
"user_ids": ["@alice:matrix.org", "@bob:example.com"] "user_ids": ["@alice:matrix.org", "@bob:example.com"]
} }

@ -525,6 +525,10 @@ if __name__ == '__main__':
"--push_gateway_release", "-p", action="store", default="unstable", "--push_gateway_release", "-p", action="store", default="unstable",
help="The push gateway release tag to generate, e.g. r1.2" help="The push gateway release tag to generate, e.g. r1.2"
) )
parser.add_argument(
"--identity_release", "-i", action="store", default="unstable",
help="The identity service release tag to generate, e.g. r1.2"
)
parser.add_argument( parser.add_argument(
"--list_targets", action="store_true", "--list_targets", action="store_true",
help="Do not update the specification. Instead print a list of targets.", help="Do not update the specification. Instead print a list of targets.",
@ -543,13 +547,13 @@ if __name__ == '__main__':
substitutions = { substitutions = {
"%CLIENT_RELEASE_LABEL%": args.client_release, "%CLIENT_RELEASE_LABEL%": args.client_release,
# we hardcode a major version of r0. This ends up in the # we hardcode the major versions. This ends up in the example
# example API URLs. When we have released a new major version, # API URLs. When we have released a new major version, we'll
# we'll have to bump it. # have to bump them.
"%CLIENT_MAJOR_VERSION%": "r0", "%CLIENT_MAJOR_VERSION%": "r0",
"%SERVER_RELEASE_LABEL%": args.server_release, "%SERVER_RELEASE_LABEL%": args.server_release,
"%SERVER_MAJOR_VERSION%": extract_major(args.server_release),
"%APPSERVICE_RELEASE_LABEL%": args.appservice_release, "%APPSERVICE_RELEASE_LABEL%": args.appservice_release,
"%IDENTITY_RELEASE_LABEL%": args.identity_release,
"%PUSH_GATEWAY_RELEASE_LABEL%": args.push_gateway_release, "%PUSH_GATEWAY_RELEASE_LABEL%": args.push_gateway_release,
} }

@ -37,6 +37,14 @@ class MatrixSections(Sections):
changelogs = self.units.get("changelogs") changelogs = self.units.get("changelogs")
return changelogs["push_gateway"] return changelogs["push_gateway"]
def render_identity_service_changelog(self):
changelogs = self.units.get("changelogs")
return changelogs["identity_service"]
def render_server_server_changelog(self):
changelogs = self.units.get("changelogs")
return changelogs["server_server"]
def render_application_service_changelog(self): def render_application_service_changelog(self):
changelogs = self.units.get("changelogs") changelogs = self.units.get("changelogs")
return changelogs["application_service"] return changelogs["application_service"]

@ -754,6 +754,7 @@ class MatrixUnits(Units):
def load_apis(self, substitutions): def load_apis(self, substitutions):
cs_ver = substitutions.get("%CLIENT_RELEASE_LABEL%", "unstable") cs_ver = substitutions.get("%CLIENT_RELEASE_LABEL%", "unstable")
fed_ver = substitutions.get("%SERVER_RELEASE_LABEL%", "unstable") fed_ver = substitutions.get("%SERVER_RELEASE_LABEL%", "unstable")
is_ver = substitutions.get("%IDENTITY_RELEASE_LABEL%", "unstable")
as_ver = substitutions.get("%APPSERVICE_RELEASE_LABEL%", "unstable") as_ver = substitutions.get("%APPSERVICE_RELEASE_LABEL%", "unstable")
push_gw_ver = substitutions.get("%PUSH_GATEWAY_RELEASE_LABEL%", "unstable") push_gw_ver = substitutions.get("%PUSH_GATEWAY_RELEASE_LABEL%", "unstable")
@ -772,7 +773,7 @@ class MatrixUnits(Units):
as_ver, as_ver,
"Privileged server plugins", "Privileged server plugins",
), TypeTableRow( ), TypeTableRow(
"`Identity Service API <identity_service/unstable.html>`_", "`Identity Service API <identity_service/"+is_ver+".html>`_",
"unstable", "unstable",
"Mapping of third party IDs to Matrix IDs", "Mapping of third party IDs to Matrix IDs",
), TypeTableRow( ), TypeTableRow(
@ -794,7 +795,7 @@ class MatrixUnits(Units):
logger.info("Reading event example: %s" % filepath) logger.info("Reading event example: %s" % filepath)
try: try:
with open(filepath, "r", encoding="utf-8") as f: with open(filepath, "r", encoding="utf-8") as f:
example = json.load(f) example = resolve_references(filepath, json.load(f))
examples[filename] = examples.get(filename, []) examples[filename] = examples.get(filename, [])
examples[filename].append(example) examples[filename].append(example)
if filename != event_name: if filename != event_name:

@ -1,6 +1,7 @@
.. Copyright 2016 OpenMarket Ltd .. Copyright 2016 OpenMarket Ltd
.. Copyright 2017 Kamax.io .. Copyright 2017 Kamax.io
.. Copyright 2017 New Vector Ltd .. Copyright 2017 New Vector Ltd
.. Copyright 2018 New Vector Ltd
.. ..
.. Licensed under the Apache License, Version 2.0 (the "License"); .. Licensed under the Apache License, Version 2.0 (the "License");
.. you may not use this file except in compliance with the License. .. you may not use this file except in compliance with the License.
@ -28,13 +29,27 @@ practice has only been applied specifically to email addresses and phone numbers
.. contents:: Table of Contents .. contents:: Table of Contents
.. sectnum:: .. sectnum::
Specification version Changelog
--------------------- ---------
.. topic:: Version: %IDENTITY_RELEASE_LABEL%
{{identity_service_changelog}}
This version of the specification is generated from This version of the specification is generated from
`matrix-doc <https://github.com/matrix-org/matrix-doc>`_ as of Git commit `matrix-doc <https://github.com/matrix-org/matrix-doc>`_ as of Git commit
`{{git_version}} <https://github.com/matrix-org/matrix-doc/tree/{{git_rev}}>`_. `{{git_version}} <https://github.com/matrix-org/matrix-doc/tree/{{git_rev}}>`_.
For the full historical changelog, see
https://github.com/matrix-org/matrix-doc/blob/master/changelogs/identity_service.rst
Other versions of this specification
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The following other versions are also available, in reverse chronological order:
- `HEAD <https://matrix.org/docs/spec/identity_service/unstable.html>`_: Includes all changes since the latest versioned release.
General principles General principles
------------------ ------------------
@ -56,7 +71,7 @@ is left as an exercise for the client.
3PID types are described in `3PID Types`_ Appendix. 3PID types are described in `3PID Types`_ Appendix.
API Standards API standards
------------- -------------
The mandatory baseline for identity service communication in Matrix is exchanging The mandatory baseline for identity service communication in Matrix is exchanging
@ -136,6 +151,22 @@ should allow a 3pid to be mapped to a Matrix user identity, but not in the other
direction (i.e. one should not be able to get all 3pids associated with a Matrix direction (i.e. one should not be able to get all 3pids associated with a Matrix
user ID, or get all 3pids associated with a 3pid). user ID, or get all 3pids associated with a 3pid).
Web browser clients
-------------------
It is realistic to expect that some clients will be written to be run within a web
browser or similar environment. In these cases, the identity service should respond to
pre-flight requests and supply Cross-Origin Resource Sharing (CORS) headers on all
requests.
When a client approaches the server with a pre-flight (OPTIONS) request, the server
should respond with the CORS headers for that route. The recommended CORS headers
to be returned by servers on all requests are::
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept, Authorization
Status check Status check
------------ ------------
@ -146,25 +177,24 @@ Key management
An identity service has some long-term public-private keypairs. These are named An identity service has some long-term public-private keypairs. These are named
in a scheme ``algorithm:identifier``, e.g. ``ed25519:0``. When signing an in a scheme ``algorithm:identifier``, e.g. ``ed25519:0``. When signing an
association, the Matrix standard JSON signing format is used, as specified in association, the standard `Signing JSON`_ algorithm applies.
the server-server API specification under the heading "Signing Events".
In the event of key compromise, the identity service may revoke any of its keys. In the event of key compromise, the identity service may revoke any of its keys.
An HTTP API is offered to get public keys, and check whether a particular key is An HTTP API is offered to get public keys, and check whether a particular key is
valid. valid.
The identity server may also keep track of some short-term public-private The identity service may also keep track of some short-term public-private
keypairs, which may have different usage and lifetime characteristics than the keypairs, which may have different usage and lifetime characteristics than the
service's long-term keys. service's long-term keys.
{{pubkey_is_http_api}} {{pubkey_is_http_api}}
Association Lookup Association lookup
------------------ ------------------
{{lookup_is_http_api}} {{lookup_is_http_api}}
Establishing Associations Establishing associations
------------------------- -------------------------
The flow for creating an association is session-based. The flow for creating an association is session-based.
@ -183,6 +213,12 @@ session, within a 24 hour period since its most recent modification. Any
attempts to perform these actions after the expiry will be rejected, and a new attempts to perform these actions after the expiry will be rejected, and a new
session should be created and used instead. session should be created and used instead.
To start a session, the client makes a request to the appropriate ``/requestToken``
endpoint. The user then receives a validation token which should be provided
to the client. The client then provides the token to the appropriate ``/submitToken``
endpoint, completing the session. At this point, the client should ``/bind`` the
third party identifier or leave it for another entity to bind.
Email associations Email associations
~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~
@ -198,53 +234,31 @@ General
{{associations_is_http_api}} {{associations_is_http_api}}
Invitation Storage Invitation storage
------------------ ------------------
An identity service can store pending invitations to a user's 3pid, which will An identity service can store pending invitations to a user's 3pid, which will
be retrieved and can be either notified on or look up when the 3pid is be retrieved and can be either notified on or look up when the 3pid is
associated with a Matrix user ID. associated with a Matrix user ID.
At a later point, if the owner of that particular 3pid binds it with a Matrix user ID, the identity server will attempt to make an HTTP POST to the Matrix user's homeserver which looks roughly as below:: At a later point, if the owner of that particular 3pid binds it with a Matrix user
ID, the identity service will attempt to make an HTTP POST to the Matrix user's
POST https://bar.com:8448/_matrix/federation/v1/3pid/onbind homeserver via the `/3pid/onbind`_ endpoint. The request MUST be signed with a
Content-Type: application/json long-term private key for the identity service.
{
"medium": "email",
"address": "foo@bar.baz",
"mxid": "@alice:example.tld",
"invites": [
{
"medium": "email",
"address": "foo@bar.baz",
"mxid": "@alice:example.tld",
"room_id": "!something:example.tld",
"sender": "@bob:example.tld",
"signed": {
"mxid": "@alice:example.tld",
"signatures": {
"vector.im": {
"ed25519:0": "somesignature"
}
},
"token": "sometoken"
}
}
]
}
Where the signature is produced using a long-term private key.
{{store_invite_is_http_api}} {{store_invite_is_http_api}}
Ephemeral invitation signing Ephemeral invitation signing
---------------------------- ----------------------------
To aid clients who may not be able to perform crypto themselves, the identity service offers some crypto functionality to help in accepting invitations. To aid clients who may not be able to perform crypto themselves, the identity
This is less secure than the client doing it itself, but may be useful where this isn't possible. service offers some crypto functionality to help in accepting invitations.
This is less secure than the client doing it itself, but may be useful where
this isn't possible.
{{invitation_signing_is_http_api}} {{invitation_signing_is_http_api}}
.. _`Unpadded Base64`: ../appendices.html#unpadded-base64 .. _`Unpadded Base64`: ../appendices.html#unpadded-base64
.. _`3PID Types`: ../appendices.html#pid-types .. _`3PID Types`: ../appendices.html#pid-types
.. _`Signing JSON`: ../appendices.html#signing-json
.. _`/3pid/onbind`: ../server_server/unstable.html#put-matrix-federation-v1-3pid-onbind

@ -64,13 +64,27 @@ request.
.. contents:: Table of Contents .. contents:: Table of Contents
.. sectnum:: .. sectnum::
Specification version Changelog
--------------------- ---------
.. topic:: Version: %SERVER_RELEASE_LABEL%
{{server_server_changelog}}
This version of the specification is generated from This version of the specification is generated from
`matrix-doc <https://github.com/matrix-org/matrix-doc>`_ as of Git commit `matrix-doc <https://github.com/matrix-org/matrix-doc>`_ as of Git commit
`{{git_version}} <https://github.com/matrix-org/matrix-doc/tree/{{git_rev}}>`_. `{{git_version}} <https://github.com/matrix-org/matrix-doc/tree/{{git_rev}}>`_.
For the full historical changelog, see
https://github.com/matrix-org/matrix-doc/blob/master/changelogs/server_server.rst
Other versions of this specification
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The following other versions are also available, in reverse chronological order:
- `HEAD <https://matrix.org/docs/spec/server_server/unstable.html>`_: Includes all changes since the latest versioned release.
Server discovery Server discovery
---------------- ----------------
@ -112,7 +126,7 @@ Server implementation
{{version_ss_http_api}} {{version_ss_http_api}}
Retrieving Server Keys Retrieving server keys
~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~
.. NOTE:: .. NOTE::
@ -978,152 +992,127 @@ Signing Events
Signing events is complicated by the fact that servers can choose to redact Signing events is complicated by the fact that servers can choose to redact
non-essential parts of an event. non-essential parts of an event.
Before signing the event, the ``unsigned`` and ``signature`` members are Adding hashes and signatures to outgoing events
removed, it is encoded as `Canonical JSON`_, and then hashed using SHA-256. The ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
resulting hash is then stored in the event JSON in a ``hash`` object under a
``sha256`` key.
.. code:: python
def hash_event(event_json_object):
# Keys under "unsigned" can be modified by other servers. Before signing the event, the *content hash* of the event is calculated as
# They are useful for conveying information like the age of an described below. The hash is encoded using `Unpadded Base64`_ and stored in the
# event that will change in transit. event object, in a ``hashes`` object, under a ``sha256`` key.
# Since they can be modifed we need to exclude them from the hash.
unsigned = event_json_object.pop("unsigned", None)
# Signatures will depend on the current value of the "hashes" key.
# We cannot add new hashes without invalidating existing signatures.
signatures = event_json_object.pop("signatures", None)
# The "hashes" key might contain multiple algorithms if we decide to
# migrate away from SHA-2. We don't want to include an existing hash
# output in our hash so we exclude the "hashes" dict from the hash.
hashes = event_json_object.pop("hashes", {})
# Encode the JSON using a canonical encoding so that we get the same
# bytes on every server for the same JSON object.
event_json_bytes = encode_canonical_json(event_json_bytes)
# Add the base64 encoded bytes of the hash to the "hashes" dict. The event object is then *redacted*, following the `redaction
hashes["sha256"] = encode_base64(sha256(event_json_bytes).digest()) algorithm`_. Finally it is signed as described in `Signing JSON`_, using the
server's signing key (see also `Retrieving server keys`_).
# Add the "hashes" dict back the event JSON under a "hashes" key. The signature is then copied back to the original event object.
event_json_object["hashes"] = hashes
if unsigned is not None:
event_json_object["unsigned"] = unsigned
return event_json_object
The event is then stripped of all non-essential keys both at the top level and See `Persistent Data Unit schema`_ for an example of a signed event.
within the ``content`` object. Any top-level keys not in the following list
MUST be removed:
.. code::
auth_events Validating hashes and signatures on received events
depth ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
event_id When a server receives an event over federation from another server, the
hashes receiving server should check the hashes and signatures on that event.
membership
origin
origin_server_ts
prev_events
prev_state
room_id
sender
signatures
state_key
type
A new ``content`` object is constructed for the resulting event that contains
only the essential keys of the original ``content`` object. If the original
event lacked a ``content`` object at all, a new empty JSON object is created
for it.
The keys that are considered essential for the ``content`` object depend on the
the ``type`` of the event. These are:
.. code:: First the signature is checked. The event is redacted following the `redaction
algorithm`_, and the resultant object is checked for a signature from the
originating server, following the algorithm described in `Checking for a signature`_.
Note that this step should succeed whether we have been sent the full event or
a redacted copy.
type is "m.room.aliases": If the signature is found to be valid, the expected content hash is calculated
aliases as described below. The content hash in the ``hashes`` property of the received
event is base64-decoded, and the two are compared for equality.
type is "m.room.create": If the hash check fails, then it is assumed that this is because we have only
creator 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.
type is "m.room.history_visibility": Calculating the content hash for an event
history_visibility ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
type is "m.room.join_rules": The *content hash* of an event covers the complete event including the
join_rule *unredacted* contents. It is calculated as follows.
type is "m.room.member": First, any existing ``unsigned``, ``signature``, and ``hashes`` members are
membership removed. The resulting object is then encoded as `Canonical JSON`_, and the
JSON is hashed using SHA-256.
type is "m.room.power_levels":
ban
events
events_default
kick
redact
state_default
users
users_default
The resulting stripped object with the new ``content`` object and the original Example code
``hashes`` key is then signed using the JSON signing algorithm outlined below: ~~~~~~~~~~~~
.. code:: python .. code:: python
def sign_event(event_json_object, name, key): def hash_and_sign_event(event_object, signing_key, signing_name):
# First we need to hash the event object.
# Make sure the event has a "hashes" key. content_hash = compute_content_hash(event_object)
if "hashes" not in event_json_object: event_object["hashes"] = {"sha256": encode_unpadded_base64(content_hash)}
event_json_object = hash_event(event_json_object)
# Strip all the keys that would be removed if the event was redacted. # Strip all the keys that would be removed if the event was redacted.
# The hashes are not stripped and cover all the keys in the event. # The hashes are not stripped and cover all the keys in the event.
# This means that we can tell if any of the non-essential keys are # This means that we can tell if any of the non-essential keys are
# modified or removed. # modified or removed.
stripped_json_object = strip_non_essential_keys(event_json_object) stripped_object = strip_non_essential_keys(event_object)
# Sign the stripped JSON object. The signature only covers the # Sign the stripped JSON object. The signature only covers the
# essential keys and the hashes. This means that we can check the # essential keys and the hashes. This means that we can check the
# signature even if the event is redacted. # signature even if the event is redacted.
signed_json_object = sign_json(stripped_json_object) signed_object = sign_json(stripped_object, signing_key, signing_name)
# Copy the signatures from the stripped event to the original event. # Copy the signatures from the stripped event to the original event.
event_json_object["signatures"] = signed_json_oject["signatures"] event_object["signatures"] = signed_object["signatures"]
return event_json_object
def compute_content_hash(event_object):
# take a copy of the event before we remove any keys.
event_object = dict(event_object)
Servers can then transmit the entire event or the event with the non-essential # Keys under "unsigned" can be modified by other servers.
keys removed. If the entire event is present, receiving servers can then check # They are useful for conveying information like the age of an
the event by computing the SHA-256 of the event, excluding the ``hash`` object. # event that will change in transit.
If the keys have been redacted, then the ``hash`` object is included when # Since they can be modifed we need to exclude them from the hash.
calculating the SHA-256 hash instead. event_object.pop("unsigned", None)
New hash functions can be introduced by adding additional keys to the ``hash`` # Signatures will depend on the current value of the "hashes" key.
object. Since the ``hash`` object cannot be redacted a server shouldn't allow # We cannot add new hashes without invalidating existing signatures.
too many hashes to be listed, otherwise a server might embed illict data within event_object.pop("signatures", None)
the ``hash`` object. For similar reasons a server shouldn't allow hash values
that are too long. # The "hashes" key might contain multiple algorithms if we decide to
# migrate away from SHA-2. We don't want to include an existing hash
# output in our hash so we exclude the "hashes" dict from the hash.
event_object.pop("hashes", None)
# Encode the JSON using a canonical encoding so that we get the same
# bytes on every server for the same JSON object.
event_json_bytes = encode_canonical_json(event_object)
return hashlib.sha256(event_json_bytes)
.. TODO .. TODO
[[TODO(markjh): We might want to specify a maximum number of keys for the
[[TODO(markjh): Since the ``hash`` object cannot be redacted a server
shouldn't allow too many hashes to be listed, otherwise a server might embed
illict data within the ``hash`` object.
We might want to specify a maximum number of keys for the
``hash`` and we might want to specify the maximum output size of a hash]] ``hash`` and we might want to specify the maximum output size of a hash]]
[[TODO(markjh) We might want to allow the server to omit the output of well [[TODO(markjh) We might want to allow the server to omit the output of well
known hash functions like SHA-256 when none of the keys have been redacted]] known hash functions like SHA-256 when none of the keys have been redacted]]
.. |/query/directory| replace:: ``/query/directory`` .. |/query/directory| replace:: ``/query/directory``
.. _/query/directory: #get-matrix-federation-v1-query-directory .. _/query/directory: #get-matrix-federation-v1-query-directory
.. _`Invitation storage`: ../identity_service/unstable.html#invitation-storage .. _`Invitation storage`: ../identity_service/%IDENTITY_RELEASE_LABEL%.html#invitation-storage
.. _`Identity Service API`: ../identity_service/unstable.html .. _`Identity Service API`: ../identity_service/%IDENTITY_RELEASE_LABEL%.html
.. _`Client-Server API`: ../client_server/%CLIENT_RELEASE_LABEL%.html .. _`Client-Server API`: ../client_server/%CLIENT_RELEASE_LABEL%.html
.. _`Inviting to a room`: #inviting-to-a-room .. _`Inviting to a room`: #inviting-to-a-room
.. _`Canonical JSON`: ../appendices.html#canonical-json .. _`Canonical JSON`: ../appendices.html#canonical-json
.. _`Unpadded Base64`: ../appendices.html#unpadded-base64 .. _`Unpadded Base64`: ../appendices.html#unpadded-base64
.. _`Server ACLs`: ../client_server/unstable.html#module-server-acls .. _`Server ACLs`: ../client_server/unstable.html#module-server-acls
.. _`redaction algorithm`: ../client_server/unstable.html#redactions
.. _`Signing JSON`: ../appendices.html#signing-json
.. _`Checking for a signature`: ../appendices.html#checking-for-a-signature
.. _`Device Management module`: ../client-server/%CLIENT_RELEASE_LABEL%.html#device-management .. _`Device Management module`: ../client-server/%CLIENT_RELEASE_LABEL%.html#device-management
.. _`End-to-End Encryption module`: ../client-server/%CLIENT_RELEASE_LABEL%.html#end-to-end-encryption .. _`End-to-End Encryption module`: ../client-server/%CLIENT_RELEASE_LABEL%.html#end-to-end-encryption

Loading…
Cancel
Save