move stuff from synapse/doc
parent
1b730ab616
commit
c7d0039c26
@ -0,0 +1,149 @@
|
||||
API Efficiency
|
||||
==============
|
||||
|
||||
A simple implementation of presence messaging has the ability to cause a large
|
||||
amount of Internet traffic relating to presence updates. In order to minimise
|
||||
the impact of such a feature, the following observations can be made:
|
||||
|
||||
* There is no point in a Home Server polling status for peers in a user's
|
||||
presence list if the user has no clients connected that care about it.
|
||||
|
||||
* It is highly likely that most presence subscriptions will be symmetric - a
|
||||
given user watching another is likely to in turn be watched by that user.
|
||||
|
||||
* It is likely that most subscription pairings will be between users who share
|
||||
at least one Room in common, and so their Home Servers are actively
|
||||
exchanging message PDUs or transactions relating to that Room.
|
||||
|
||||
* Presence update messages do not need realtime guarantees. It is acceptable to
|
||||
delay delivery of updates for some small amount of time (10 seconds to a
|
||||
minute).
|
||||
|
||||
The general model of presence information is that of a HS registering its
|
||||
interest in receiving presence status updates from other HSes, which then
|
||||
promise to send them when required. Rather than actively polling for the
|
||||
current state all the time, HSes can rely on their relative stability to only
|
||||
push updates when required.
|
||||
|
||||
A Home Server should not rely on the longterm validity of this presence
|
||||
information, however, as this would not cover such cases as a user's server
|
||||
crashing and thus failing to inform their peers that users it used to host are
|
||||
no longer available online. Therefore, each promise of future updates should
|
||||
carry with a timeout value (whether explicit in the message, or implicit as some
|
||||
defined default in the protocol), after which the receiving HS should consider
|
||||
the information potentially stale and request it again.
|
||||
|
||||
However, because of the likelihood that two home servers are exchanging messages
|
||||
relating to chat traffic in a room common to both of them, the ongoing receipt
|
||||
of these messages can be taken by each server as an implicit notification that
|
||||
the sending server is still up and running, and therefore that no status changes
|
||||
have happened; because if they had the server would have sent them. A second,
|
||||
larger timeout should be applied to this implicit inference however, to protect
|
||||
against implementation bugs or other reasons that the presence state cache may
|
||||
become invalid; eventually the HS should re-enquire the current state of users
|
||||
and update them with its own.
|
||||
|
||||
The following workflows can therefore be used to handle presence updates:
|
||||
|
||||
1 When a user first appears online their HS sends a message to each other HS
|
||||
containing at least one user to be watched; each message carrying both a
|
||||
notification of the sender's new online status, and a request to obtain and
|
||||
watch the target users' presence information. This message implicitly
|
||||
promises the sending HS will now push updates to the target HSes.
|
||||
|
||||
2 The target HSes then respond a single message each, containing the current
|
||||
status of the requested user(s). These messages too implicitly promise the
|
||||
target HSes will themselves push updates to the sending HS.
|
||||
|
||||
As these messages arrive at the sending user's HS they can be pushed to the
|
||||
user's client(s), possibly batched again to ensure not too many small
|
||||
messages which add extra protocol overheads.
|
||||
|
||||
At this point, all the user's clients now have the current presence status
|
||||
information for this moment in time, and have promised to send each other
|
||||
updates in future.
|
||||
|
||||
3 The HS maintains two watchdog timers per peer HS it is exchanging presence
|
||||
information with. The first timer should have a relatively small expiry
|
||||
(perhaps 1 minute), and the second timer should have a much longer time
|
||||
(perhaps 1 hour).
|
||||
|
||||
4 Any time any kind of message is received from a peer HS, the short-term
|
||||
presence timer associated with it is reset.
|
||||
|
||||
5 Whenever either of these timers expires, an HS should push a status reminder
|
||||
to the target HS whose timer has now expired, and request again from that
|
||||
server the status of the subscribed users.
|
||||
|
||||
6 On receipt of one of these presence status reminders, an HS can reset both
|
||||
of its presence watchdog timers.
|
||||
|
||||
To avoid bursts of traffic, implementations should attempt to stagger the expiry
|
||||
of the longer-term watchdog timers for different peer HSes.
|
||||
|
||||
When individual users actively change their status (either by explicit requests
|
||||
from clients, or inferred changes due to idle timers or client timeouts), the HS
|
||||
should batch up any status changes for some reasonable amount of time (10
|
||||
seconds to a minute). This allows for reduced protocol overheads in the case of
|
||||
multiple messages needing to be sent to the same peer HS; as is the likely
|
||||
scenario in many cases, such as a given human user having multiple user
|
||||
accounts.
|
||||
|
||||
|
||||
API Requirements
|
||||
================
|
||||
|
||||
The data model presented here puts the following requirements on the APIs:
|
||||
|
||||
Client-Server
|
||||
-------------
|
||||
|
||||
Requests that a client can make to its Home Server
|
||||
|
||||
* get/set current presence state
|
||||
Basic enumeration + ability to set a custom piece of text
|
||||
|
||||
* report per-device idle time
|
||||
After some (configurable?) idle time the device should send a single message
|
||||
to set the idle duration. The HS can then infer a "start of idle" instant and
|
||||
use that to keep the device idleness up to date. At some later point the
|
||||
device can cancel this idleness.
|
||||
|
||||
* report per-device type
|
||||
Inform the server that this device is a "mobile" device, or perhaps some
|
||||
other to-be-defined category of reduced capability that could be presented to
|
||||
other users.
|
||||
|
||||
* start/stop presence polling for my presence list
|
||||
It is likely that these messages could be implicitly inferred by other
|
||||
messages, though having explicit control is always useful.
|
||||
|
||||
* get my presence list
|
||||
[implicit poll start?]
|
||||
It is possible that the HS doesn't yet have current presence information when
|
||||
the client requests this. There should be a "don't know" type too.
|
||||
|
||||
* add/remove a user to my presence list
|
||||
|
||||
Server-Server
|
||||
-------------
|
||||
|
||||
Requests that Home Servers make to others
|
||||
|
||||
* request permission to add a user to presence list
|
||||
|
||||
* allow/deny a request to add to a presence list
|
||||
|
||||
* perform a combined presence state push and subscription request
|
||||
For each sending user ID, the message contains their new status.
|
||||
For each receiving user ID, the message should contain an indication on
|
||||
whether the sending server is also interested in receiving status from that
|
||||
user; either as an immediate update response now, or as a promise to send
|
||||
future updates.
|
||||
|
||||
Server to Client
|
||||
----------------
|
||||
|
||||
[[TODO(paul): There also needs to be some way for a user's HS to push status
|
||||
updates of the presence list to clients, but the general server-client event
|
||||
model currently lacks a space to do that.]]
|
@ -0,0 +1,232 @@
|
||||
========
|
||||
Profiles
|
||||
========
|
||||
|
||||
A description of Synapse user profile metadata support.
|
||||
|
||||
|
||||
Overview
|
||||
========
|
||||
|
||||
Internally within Synapse users are referred to by an opaque ID, which consists
|
||||
of some opaque localpart combined with the domain name of their home server.
|
||||
Obviously this does not yield a very nice user experience; users would like to
|
||||
see readable names for other users that are in some way meaningful to them.
|
||||
Additionally, users like to be able to publish "profile" details to inform other
|
||||
users of other information about them.
|
||||
|
||||
It is also conceivable that since we are attempting to provide a
|
||||
worldwide-applicable messaging system, that users may wish to present different
|
||||
subsets of information in their profile to different other people, from a
|
||||
privacy and permissions perspective.
|
||||
|
||||
A Profile consists of a display name, an (optional?) avatar picture, and a set
|
||||
of other metadata fields that the user may wish to publish (email address, phone
|
||||
numbers, website URLs, etc...). We put no requirements on the display name other
|
||||
than it being a valid Unicode string. Since it is likely that users will end up
|
||||
having multiple accounts (perhaps by necessity of being hosted in multiple
|
||||
places, perhaps by choice of wanting multiple distinct identifies), it would be
|
||||
useful that a metadata field type exists that can refer to another Synapse User
|
||||
ID, so that clients and HSes can make use of this information.
|
||||
|
||||
Metadata Fields
|
||||
---------------
|
||||
|
||||
[[TODO(paul): Likely this list is incomplete; more fields can be defined as we
|
||||
think of them. At the very least, any sort of supported ID for the 3rd Party ID
|
||||
servers should be accounted for here.]]
|
||||
|
||||
* Synapse Directory Server username(s)
|
||||
|
||||
* Email address
|
||||
|
||||
* Phone number - classify "home"/"work"/"mobile"/custom?
|
||||
|
||||
* Twitter/Facebook/Google+/... social networks
|
||||
|
||||
* Location - keep this deliberately vague to allow people to choose how
|
||||
granular it is
|
||||
|
||||
* "Bio" information - date of birth, etc...
|
||||
|
||||
* Synapse User ID of another account
|
||||
|
||||
* Web URL
|
||||
|
||||
* Freeform description text
|
||||
|
||||
|
||||
Visibility Permissions
|
||||
======================
|
||||
|
||||
A home server implementation could offer the ability to set permissions on
|
||||
limited visibility of those fields. When another user requests access to the
|
||||
target user's profile, their own identity should form part of that request. The
|
||||
HS implementation can then decide which fields to make available to the
|
||||
requestor.
|
||||
|
||||
A particular detail of implementation could allow the user to create one or more
|
||||
ACLs; where each list is granted permission to see a given set of non-public
|
||||
fields (compare to Google+ Circles) and contains a set of other people allowed
|
||||
to use it. By giving these ACLs strong identities within the HS, they can be
|
||||
referenced in communications with it, granting other users who encounter these
|
||||
the "ACL Token" to use the details in that ACL.
|
||||
|
||||
If we further allow an ACL Token to be present on Room join requests or stored
|
||||
by 3PID servers, then users of these ACLs gain the extra convenience of not
|
||||
having to manually curate people in the access list; anyone in the room or with
|
||||
knowledge of the 3rd Party ID is automatically granted access. Every HS and
|
||||
client implementation would have to be aware of the existence of these ACL
|
||||
Token, and include them in requests if present, but not every HS implementation
|
||||
needs to actually provide the full permissions model. This can be used as a
|
||||
distinguishing feature among competing implementations. However, servers MUST
|
||||
NOT serve profile information from a cache if there is a chance that its limited
|
||||
understanding could lead to information leakage.
|
||||
|
||||
|
||||
Client Concerns of Multiple Accounts
|
||||
====================================
|
||||
|
||||
Because a given person may want to have multiple Synapse User accounts, client
|
||||
implementations should allow the use of multiple accounts simultaneously
|
||||
(especially in the field of mobile phone clients, which generally don't support
|
||||
running distinct instances of the same application). Where features like address
|
||||
books, presence lists or rooms are presented, the client UI should remember to
|
||||
make distinct with user account is in use for each.
|
||||
|
||||
|
||||
Directory Servers
|
||||
=================
|
||||
|
||||
Directory Servers can provide a forward mapping from human-readable names to
|
||||
User IDs. These can provide a service similar to giving domain-namespaced names
|
||||
for Rooms; in this case they can provide a way for a user to reference their
|
||||
User ID in some external form (e.g. that can be printed on a business card).
|
||||
|
||||
The format for Synapse user name will consist of a localpart specific to the
|
||||
directory server, and the domain name of that directory server:
|
||||
|
||||
@localname:some.domain.name
|
||||
|
||||
The localname is separated from the domain name using a colon, so as to ensure
|
||||
the localname can still contain periods, as users may want this for similarity
|
||||
to email addresses or the like, which typically can contain them. The format is
|
||||
also visually quite distinct from email addresses, phone numbers, etc... so
|
||||
hopefully reasonably "self-describing" when written on e.g. a business card
|
||||
without surrounding context.
|
||||
|
||||
[[TODO(paul): we might have to think about this one - too close to email?
|
||||
Twitter? Also it suggests a format scheme for room names of
|
||||
#localname:domain.name, which I quite like]]
|
||||
|
||||
Directory server administrators should be able to make some kind of policy
|
||||
decision on how these are allocated. Servers within some "closed" domain (such
|
||||
as company-specific ones) may wish to verify the validity of a mapping using
|
||||
their own internal mechanisms; "public" naming servers can operate on a FCFS
|
||||
basis. There are overlapping concerns here with the idea of the 3rd party
|
||||
identity servers as well, though in this specific case we are creating a new
|
||||
namespace to allocate names into.
|
||||
|
||||
It would also be nice from a user experience perspective if the profile that a
|
||||
given name links to can also declare that name as part of its metadata.
|
||||
Furthermore as a security and consistency perspective it would be nice if each
|
||||
end (the directory server and the user's home server) check the validity of the
|
||||
mapping in some way. This needs investigation from a security perspective to
|
||||
ensure against spoofing.
|
||||
|
||||
One such model may be that the user starts by declaring their intent to use a
|
||||
given user name link to their home server, which then contacts the directory
|
||||
service. At some point later (maybe immediately for "public open FCFS servers",
|
||||
maybe after some kind of human intervention for verification) the DS decides to
|
||||
honour this link, and includes it in its served output. It should also tell the
|
||||
HS of this fact, so that the HS can present this as fact when requested for the
|
||||
profile information. For efficiency, it may further wish to provide the HS with
|
||||
a cryptographically-signed certificate as proof, so the HS serving the profile
|
||||
can provide that too when asked, avoiding requesting HSes from constantly having
|
||||
to contact the DS to verify this mapping. (Note: This is similar to the security
|
||||
model often applied in DNS to verify PTR <-> A bidirectional mappings).
|
||||
|
||||
|
||||
Identity Servers
|
||||
================
|
||||
|
||||
The identity servers should support the concept of pointing a 3PID being able to
|
||||
store an ACL Token as well as the main User ID. It is however, beyond scope to
|
||||
do any kind of verification that any third-party IDs that the profile is
|
||||
claiming match up to the 3PID mappings.
|
||||
|
||||
|
||||
User Interface and Expectations Concerns
|
||||
========================================
|
||||
|
||||
Given the weak "security" of some parts of this model as compared to what users
|
||||
might expect, some care should be taken on how it is presented to users,
|
||||
specifically in the naming or other wording of user interface components.
|
||||
|
||||
Most notably mere knowledge of an ACL Pointer is enough to read the information
|
||||
stored in it. It is possible that Home or Identity Servers could leak this
|
||||
information, allowing others to see it. This is a security-vs-convenience
|
||||
balancing choice on behalf of the user who would choose, or not, to make use of
|
||||
such a feature to publish their information.
|
||||
|
||||
Additionally, unless some form of strong end-to-end user-based encryption is
|
||||
used, a user of ACLs for information privacy has to trust other home servers not
|
||||
to lie about the identify of the user requesting access to the Profile.
|
||||
|
||||
|
||||
API Requirements
|
||||
================
|
||||
|
||||
The data model presented here puts the following requirements on the APIs:
|
||||
|
||||
Client-Server
|
||||
-------------
|
||||
|
||||
Requests that a client can make to its Home Server
|
||||
|
||||
* get/set my Display Name
|
||||
This should return/take a simple "text/plain" field
|
||||
|
||||
* get/set my Avatar URL
|
||||
The avatar image data itself is not stored by this API; we'll just store a
|
||||
URL to let the clients fetch it. Optionally HSes could integrate this with
|
||||
their generic content attacmhent storage service, allowing a user to set
|
||||
upload their profile Avatar and update the URL to point to it.
|
||||
|
||||
* get/add/remove my metadata fields
|
||||
Also we need to actually define types of metadata
|
||||
|
||||
* get another user's Display Name / Avatar / metadata fields
|
||||
|
||||
[[TODO(paul): At some later stage we should consider the API for:
|
||||
|
||||
* get/set ACL permissions on my metadata fields
|
||||
|
||||
* manage my ACL tokens
|
||||
]]
|
||||
|
||||
Server-Server
|
||||
-------------
|
||||
|
||||
Requests that Home Servers make to others
|
||||
|
||||
* get a user's Display Name / Avatar
|
||||
|
||||
* get a user's full profile - name/avatar + MD fields
|
||||
This request must allow for specifying the User ID of the requesting user,
|
||||
for permissions purposes. It also needs to take into account any ACL Tokens
|
||||
the requestor has.
|
||||
|
||||
* push a change of Display Name to observers (overlaps with the presence API)
|
||||
|
||||
Room Event PDU Types
|
||||
--------------------
|
||||
|
||||
Events that are pushed from Home Servers to other Home Servers or clients.
|
||||
|
||||
* user Display Name change
|
||||
|
||||
* user Avatar change
|
||||
[[TODO(paul): should the avatar image itself be stored in all the room
|
||||
histories? maybe this event should just be a hint to clients that they should
|
||||
re-fetch the avatar image]]
|
@ -0,0 +1,64 @@
|
||||
PUT /send/abc/ HTTP/1.1
|
||||
Host: ...
|
||||
Content-Length: ...
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"origin": "localhost:5000",
|
||||
"pdus": [
|
||||
{
|
||||
"content": {},
|
||||
"context": "tng",
|
||||
"depth": 12,
|
||||
"is_state": false,
|
||||
"origin": "localhost:5000",
|
||||
"pdu_id": 1404381396854,
|
||||
"pdu_type": "feedback",
|
||||
"prev_pdus": [
|
||||
[
|
||||
"1404381395883",
|
||||
"localhost:6000"
|
||||
]
|
||||
],
|
||||
"ts": 1404381427581
|
||||
}
|
||||
],
|
||||
"prev_ids": [
|
||||
"1404381396852"
|
||||
],
|
||||
"ts": 1404381427823
|
||||
}
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
...
|
||||
|
||||
======================================
|
||||
|
||||
GET /pull/-1/ HTTP/1.1
|
||||
Host: ...
|
||||
Content-Length: 0
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Content-Length: ...
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
origin: ...,
|
||||
prev_ids: ...,
|
||||
data: [
|
||||
{
|
||||
data_id: ...,
|
||||
prev_pdus: [...],
|
||||
depth: ...,
|
||||
ts: ...,
|
||||
context: ...,
|
||||
origin: ...,
|
||||
content: {
|
||||
...
|
||||
}
|
||||
},
|
||||
...,
|
||||
]
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,113 @@
|
||||
==================
|
||||
Room Join Workflow
|
||||
==================
|
||||
|
||||
An outline of the workflows required when a user joins a room.
|
||||
|
||||
Discovery
|
||||
=========
|
||||
|
||||
To join a room, a user has to discover the room by some mechanism in order to
|
||||
obtain the (opaque) Room ID and a candidate list of likely home servers that
|
||||
contain it.
|
||||
|
||||
Sending an Invitation
|
||||
---------------------
|
||||
|
||||
The most direct way a user discovers the existence of a room is from a
|
||||
invitation from some other user who is a member of that room.
|
||||
|
||||
The inviter's HS sets the membership status of the invitee to "invited" in the
|
||||
"m.members" state key by sending a state update PDU. The HS then broadcasts this
|
||||
PDU among the existing members in the usual way. An invitation message is also
|
||||
sent to the invited user, containing the Room ID and the PDU ID of this
|
||||
invitation state change and potentially a list of some other home servers to use
|
||||
to accept the invite. The user's client can then choose to display it in some
|
||||
way to alert the user.
|
||||
|
||||
[[TODO(paul): At present, no API has been designed or described to actually send
|
||||
that invite to the invited user. Likely it will be some facet of the larger
|
||||
user-user API required for presence, profile management, etc...]]
|
||||
|
||||
Directory Service
|
||||
-----------------
|
||||
|
||||
Alternatively, the user may discover the channel via a directory service; either
|
||||
by performing a name lookup, or some kind of browse or search acitivty. However
|
||||
this is performed, the end result is that the user's home server requests the
|
||||
Room ID and candidate list from the directory service.
|
||||
|
||||
[[TODO(paul): At present, no API has been designed or described for this
|
||||
directory service]]
|
||||
|
||||
|
||||
Joining
|
||||
=======
|
||||
|
||||
Once the ID and home servers are obtained, the user can then actually join the
|
||||
room.
|
||||
|
||||
Accepting an Invite
|
||||
-------------------
|
||||
|
||||
If a user has received and accepted an invitation to join a room, the invitee's
|
||||
home server can now send an invite acceptance message to a chosen candidate
|
||||
server from the list given in the invitation, citing also the PDU ID of the
|
||||
invitation as "proof" of their invite. (This is required as due to late message
|
||||
propagation it could be the case that the acceptance is received before the
|
||||
invite by some servers). If this message is allowed by the candidate server, it
|
||||
generates a new PDU that updates the invitee's membership status to "joined",
|
||||
referring back to the acceptance PDU, and broadcasts that as a state change in
|
||||
the usual way. The newly-invited user is now a full member of the room, and
|
||||
state propagation proceeds as usual.
|
||||
|
||||
Joining a Public Room
|
||||
---------------------
|
||||
|
||||
If a user has discovered the existence of a room they wish to join but does not
|
||||
have an active invitation, they can request to join it directly by sending a
|
||||
join message to a candidate server on the list provided by the directory
|
||||
service. As this list may be out of date, the HS should be prepared to retry
|
||||
other candidates if the chosen one is no longer aware of the room, because it
|
||||
has no users as members in it.
|
||||
|
||||
Once a candidate server that is aware of the room has been found, it can
|
||||
broadcast an update PDU to add the member into the "m.members" key setting their
|
||||
state directly to "joined" (i.e. bypassing the two-phase invite semantics),
|
||||
remembering to include the new user's HS in that list.
|
||||
|
||||
Knocking on a Semi-Public Room
|
||||
------------------------------
|
||||
|
||||
If a user requests to join a room but the join mode of the room is "knock", the
|
||||
join is not immediately allowed. Instead, if the user wishes to proceed, they
|
||||
can instead post a "knock" message, which informs other members of the room that
|
||||
the would-be joiner wishes to become a member and sets their membership value to
|
||||
"knocked". If any of them wish to accept this, they can then send an invitation
|
||||
in the usual way described above. Knowing that the user has already knocked and
|
||||
expressed an interest in joining, the invited user's home server should
|
||||
immediately accept that invitation on the user's behalf, and go on to join the
|
||||
room in the usual way.
|
||||
|
||||
[[NOTE(Erik): Though this may confuse users who expect 'X has joined' to
|
||||
actually be a user initiated action, i.e. they may expect that 'X' is actually
|
||||
looking at synapse right now?]]
|
||||
|
||||
[[NOTE(paul): Yes, a fair point maybe we should suggest HSes don't do that, and
|
||||
just offer an invite to the user as normal]]
|
||||
|
||||
Private and Non-Existent Rooms
|
||||
------------------------------
|
||||
|
||||
If a user requests to join a room but the room is either unknown by the home
|
||||
server receiving the request, or is known by the join mode is "invite" and the
|
||||
user has not been invited, the server must respond that the room does not exist.
|
||||
This is to prevent leaking information about the existence and identity of
|
||||
private rooms.
|
||||
|
||||
|
||||
Outstanding Questions
|
||||
=====================
|
||||
|
||||
* Do invitations or knocks time out and expire at some point? If so when? Time
|
||||
is hard in distributed systems.
|
@ -0,0 +1,274 @@
|
||||
===========
|
||||
Rooms Model
|
||||
===========
|
||||
|
||||
A description of the general data model used to implement Rooms, and the
|
||||
user-level visible effects and implications.
|
||||
|
||||
|
||||
Overview
|
||||
========
|
||||
|
||||
"Rooms" in Synapse are shared messaging channels over which all the participant
|
||||
users can exchange messages. Rooms have an opaque persistent identify, a
|
||||
globally-replicated set of state (consisting principly of a membership set of
|
||||
users, and other management and miscellaneous metadata), and a message history.
|
||||
|
||||
|
||||
Room Identity and Naming
|
||||
========================
|
||||
|
||||
Rooms can be arbitrarily created by any user on any home server; at which point
|
||||
the home server will sign the message that creates the channel, and the
|
||||
fingerprint of this signature becomes the strong persistent identify of the
|
||||
room. This now identifies the room to any home server in the network regardless
|
||||
of its original origin. This allows the identify of the room to outlive any
|
||||
particular server. Subject to appropriate permissions [to be discussed later],
|
||||
any current member of a room can invite others to join it, can post messages
|
||||
that become part of its history, and can change the persistent state of the room
|
||||
(including its current set of permissions).
|
||||
|
||||
Home servers can provide a directory service, allowing a lookup from a
|
||||
convenient human-readable form of room label to a room ID. This mapping is
|
||||
scoped to the particular home server domain and so simply represents that server
|
||||
administrator's opinion of what room should take that label; it does not have to
|
||||
be globally replicated and does not form part of the stored state of that room.
|
||||
|
||||
This room name takes the form
|
||||
|
||||
#localname:some.domain.name
|
||||
|
||||
for similarity and consistency with user names on directories.
|
||||
|
||||
To join a room (and therefore to be allowed to inspect past history, post new
|
||||
messages to it, and read its state), a user must become aware of the room's
|
||||
fingerprint ID. There are two mechanisms to allow this:
|
||||
|
||||
* An invite message from someone else in the room
|
||||
|
||||
* A referral from a room directory service
|
||||
|
||||
As room IDs are opaque and ephemeral, they can serve as a mechanism to create
|
||||
"ad-hoc" rooms deliberately unnamed, for small group-chats or even private
|
||||
one-to-one message exchange.
|
||||
|
||||
|
||||
Stored State and Permissions
|
||||
============================
|
||||
|
||||
Every room has a globally-replicated set of stored state. This state is a set of
|
||||
key/value or key/subkey/value pairs. The value of every (sub)key is a
|
||||
JSON-representable object. The main key of a piece of stored state establishes
|
||||
its meaning; some keys store sub-keys to allow a sub-structure within them [more
|
||||
detail below]. Some keys have special meaning to Synapse, as they relate to
|
||||
management details of the room itself, storing such details as user membership,
|
||||
and permissions of users to alter the state of the room itself. Other keys may
|
||||
store information to present to users, which the system does not directly rely
|
||||
on. The key space itself is namespaced, allowing 3rd party extensions, subject
|
||||
to suitable permission.
|
||||
|
||||
Permission management is based on the concept of "power-levels". Every user
|
||||
within a room has an integer assigned, being their "power-level" within that
|
||||
room. Along with its actual data value, each key (or subkey) also stores the
|
||||
minimum power-level a user must have in order to write to that key, the
|
||||
power-level of the last user who actually did write to it, and the PDU ID of
|
||||
that state change.
|
||||
|
||||
To be accepted as valid, a change must NOT:
|
||||
|
||||
* Be made by a user having a power-level lower than required to write to the
|
||||
state key
|
||||
|
||||
* Alter the required power-level for that state key to a value higher than the
|
||||
user has
|
||||
|
||||
* Increase that user's own power-level
|
||||
|
||||
* Grant any other user a power-level higher than the level of the user making
|
||||
the change
|
||||
|
||||
[[TODO(paul): consider if relaxations should be allowed; e.g. is the current
|
||||
outright-winner allowed to raise their own level, to allow for "inflation"?]]
|
||||
|
||||
|
||||
Room State Keys
|
||||
===============
|
||||
|
||||
[[TODO(paul): if this list gets too big it might become necessary to move it
|
||||
into its own doc]]
|
||||
|
||||
The following keys have special semantics or meaning to Synapse itself:
|
||||
|
||||
m.member (has subkeys)
|
||||
Stores a sub-key for every Synapse User ID which is currently a member of
|
||||
this room. Its value gives the membership type ("knocked", "invited",
|
||||
"joined").
|
||||
|
||||
m.power_levels
|
||||
Stores a mapping from Synapse User IDs to their power-level in the room. If
|
||||
they are not present in this mapping, the default applies.
|
||||
|
||||
The reason to store this as a single value rather than a value with subkeys
|
||||
is that updates to it are atomic; allowing a number of colliding-edit
|
||||
problems to be avoided.
|
||||
|
||||
m.default_level
|
||||
Gives the default power-level for members of the room that do not have one
|
||||
specified in their membership key.
|
||||
|
||||
m.invite_level
|
||||
If set, gives the minimum power-level required for members to invite others
|
||||
to join, or to accept knock requests from non-members requesting access. If
|
||||
absent, then invites are not allowed. An invitation involves setting their
|
||||
membership type to "invited", in addition to sending the invite message.
|
||||
|
||||
m.join_rules
|
||||
Encodes the rules on how non-members can join the room. Has the following
|
||||
possibilities:
|
||||
"public" - a non-member can join the room directly
|
||||
"knock" - a non-member cannot join the room, but can post a single "knock"
|
||||
message requesting access, which existing members may approve or deny
|
||||
"invite" - non-members cannot join the room without an invite from an
|
||||
existing member
|
||||
"private" - nobody who is not in the 'may_join' list or already a member
|
||||
may join by any mechanism
|
||||
|
||||
In any of the first three modes, existing members with sufficient permission
|
||||
can send invites to non-members if allowed by the "m.invite_level" key. A
|
||||
"private" room is not allowed to have the "m.invite_level" set.
|
||||
|
||||
A client may use the value of this key to hint at the user interface
|
||||
expectations to provide; in particular, a private chat with one other use
|
||||
might warrant specific handling in the client.
|
||||
|
||||
m.may_join
|
||||
A list of User IDs that are always allowed to join the room, regardless of any
|
||||
of the prevailing join rules and invite levels. These apply even to private
|
||||
rooms. These are stored in a single list with normal update-powerlevel
|
||||
permissions applied; users cannot arbitrarily remove themselves from the list.
|
||||
|
||||
m.add_state_level
|
||||
The power-level required for a user to be able to add new state keys.
|
||||
|
||||
m.public_history
|
||||
If set and true, anyone can request the history of the room, without needing
|
||||
to be a member of the room.
|
||||
|
||||
m.archive_servers
|
||||
For "public" rooms with public history, gives a list of home servers that
|
||||
should be included in message distribution to the room, even if no users on
|
||||
that server are present. These ensure that a public room can still persist
|
||||
even if no users are currently members of it. This list should be consulted by
|
||||
the dirctory servers as the candidate list they respond with.
|
||||
|
||||
The following keys are provided by Synapse for user benefit, but their value is
|
||||
not otherwise used by Synapse.
|
||||
|
||||
m.name
|
||||
Stores a short human-readable name for the room, such that clients can display
|
||||
to a user to assist in identifying which room is which.
|
||||
|
||||
This name specifically is not the strong ID used by the message transport
|
||||
system to refer to the room, because it may be changed from time to time.
|
||||
|
||||
m.topic
|
||||
Stores the current human-readable topic
|
||||
|
||||
|
||||
Room Creation Templates
|
||||
=======================
|
||||
|
||||
A client (or maybe home server?) could offer a few templates for the creation of
|
||||
new rooms. For example, for a simple private one-to-one chat the channel could
|
||||
assign the creator a power-level of 1, requiring a level of 1 to invite, and
|
||||
needing an invite before members can join. An invite is then sent to the other
|
||||
party, and if accepted and the other user joins, the creator's power-level can
|
||||
now be reduced to 0. This now leaves a room with two participants in it being
|
||||
unable to add more.
|
||||
|
||||
|
||||
Rooms that Continue History
|
||||
===========================
|
||||
|
||||
An option that could be considered for room creation, is that when a new room is
|
||||
created the creator could specify a PDU ID into an existing room, as the history
|
||||
continuation point. This would be stored as an extra piece of meta-data on the
|
||||
initial PDU of the room's creation. (It does not appear in the normal previous
|
||||
PDU linkage).
|
||||
|
||||
This would allow users in rooms to "fork" a room, if it is considered that the
|
||||
conversations in the room no longer fit its original purpose, and wish to
|
||||
diverge. Existing permissions on the original room would continue to apply of
|
||||
course, for viewing that history. If both rooms are considered "public" we might
|
||||
also want to define a message to post into the original room to represent this
|
||||
fork point, and give a reference to the new room.
|
||||
|
||||
|
||||
User Direct Message Rooms
|
||||
=========================
|
||||
|
||||
There is no need to build a mechanism for directly sending messages between
|
||||
users, because a room can handle this ability. To allow direct user-to-user chat
|
||||
messaging we simply need to be able to create rooms with specific set of
|
||||
permissions to allow this direct messaging.
|
||||
|
||||
Between any given pair of user IDs that wish to exchange private messages, there
|
||||
will exist a single shared Room, created lazily by either side. These rooms will
|
||||
need a certain amount of special handling in both home servers and display on
|
||||
clients, but as much as possible should be treated by the lower layers of code
|
||||
the same as other rooms.
|
||||
|
||||
Specially, a client would likely offer a special menu choice associated with
|
||||
another user (in room member lists, presence list, etc..) as "direct chat". That
|
||||
would perform all the necessary steps to create the private chat room. Receiving
|
||||
clients should display these in a special way too as the room name is not
|
||||
important; instead it should distinguish them on the Display Name of the other
|
||||
party.
|
||||
|
||||
Home Servers will need a client-API option to request setting up a new user-user
|
||||
chat room, which will then need special handling within the server. It will
|
||||
create a new room with the following
|
||||
|
||||
m.member: the proposing user
|
||||
m.join_rules: "private"
|
||||
m.may_join: both users
|
||||
m.power_levels: empty
|
||||
m.default_level: 0
|
||||
m.add_state_level: 0
|
||||
m.public_history: False
|
||||
|
||||
Having created the room, it can send an invite message to the other user in the
|
||||
normal way - the room permissions state that no users can be set to the invited
|
||||
state, but because they're in the may_join list then they'd be allowed to join
|
||||
anyway.
|
||||
|
||||
In this arrangement there is now a room with both users may join but neither has
|
||||
the power to invite any others. Both users now have the confidence that (at
|
||||
least within the messaging system itself) their messages remain private and
|
||||
cannot later be provably leaked to a third party. They can freely set the topic
|
||||
or name if they choose and add or edit any other state of the room. The update
|
||||
powerlevel of each of these fixed properties should be 1, to lock out the users
|
||||
from being able to alter them.
|
||||
|
||||
|
||||
Anti-Glare
|
||||
==========
|
||||
|
||||
There exists the possibility of a race condition if two users who have no chat
|
||||
history with each other simultaneously create a room and invite the other to it.
|
||||
This is called a "glare" situation. There are two possible ideas for how to
|
||||
resolve this:
|
||||
|
||||
* Each Home Server should persist the mapping of (user ID pair) to room ID, so
|
||||
that duplicate requests can be suppressed. On receipt of a room creation
|
||||
request that the HS thinks there already exists a room for, the invitation to
|
||||
join can be rejected if:
|
||||
a) the HS believes the sending user is already a member of the room (and
|
||||
maybe their HS has forgotten this fact), or
|
||||
b) the proposed room has a lexicographically-higher ID than the existing
|
||||
room (to resolve true race condition conflicts)
|
||||
|
||||
* The room ID for a private 1:1 chat has a special form, determined by
|
||||
concatenting the User IDs of both members in a deterministic order, such that
|
||||
it doesn't matter which side creates it first; the HSes can just ignore
|
||||
(or merge?) received PDUs that create the room twice.
|
@ -0,0 +1,108 @@
|
||||
======================
|
||||
Third Party Identities
|
||||
======================
|
||||
|
||||
A description of how email addresses, mobile phone numbers and other third
|
||||
party identifiers can be used to authenticate and discover users in Matrix.
|
||||
|
||||
|
||||
Overview
|
||||
========
|
||||
|
||||
New users need to authenticate their account. An email or SMS text message can
|
||||
be a convenient form of authentication. Users already have email addresses
|
||||
and phone numbers for contacts in their address book. They want to communicate
|
||||
with those contacts in Matrix without manually exchanging a Matrix User ID with
|
||||
them.
|
||||
|
||||
Third Party IDs
|
||||
---------------
|
||||
|
||||
[[TODO(markjh): Describe the format of a 3PID]]
|
||||
|
||||
|
||||
Third Party ID Associations
|
||||
---------------------------
|
||||
|
||||
An Associaton is a binding between a Matrix User ID and a Third Party ID (3PID).
|
||||
Each 3PID can be associated with one Matrix User ID at a time.
|
||||
|
||||
[[TODO(markjh): JSON format of the association.]]
|
||||
|
||||
Verification
|
||||
------------
|
||||
|
||||
An Assocation must be verified by a trusted Verification Server. Email
|
||||
addresses and phone numbers can be verified by sending a token to the address
|
||||
which a client can supply to the verifier to confirm ownership.
|
||||
|
||||
An email Verification Server may be capable of verifying all email 3PIDs or may
|
||||
be restricted to verifying addresses for a particular domain. A phone number
|
||||
Verification Server may be capable of verifying all phone numbers or may be
|
||||
restricted to verifying numbers for a given country or phone prefix.
|
||||
|
||||
Verification Servers fulfil a similar role to Certificate Authorities in PKI so
|
||||
a similar level of vetting should be required before clients trust their
|
||||
signatures.
|
||||
|
||||
A Verification Server may wish to check for existing Associations for a 3PID
|
||||
before creating a new Association.
|
||||
|
||||
Discovery
|
||||
---------
|
||||
|
||||
Users can discover Associations using a trusted Identity Server. Each
|
||||
Association will be signed by the Identity Server. An Identity Server may store
|
||||
the entire space of Associations or may delegate to other Identity Servers when
|
||||
looking up Associations.
|
||||
|
||||
Each Association returned from an Identity Server must be signed by a
|
||||
Verification Server. Clients should check these signatures.
|
||||
|
||||
Identity Servers fulfil a similar role to DNS servers.
|
||||
|
||||
Privacy
|
||||
-------
|
||||
|
||||
A User may publish the association between their phone number and Matrix User ID
|
||||
on the Identity Server without publishing the number in their Profile hosted on
|
||||
their Home Server.
|
||||
|
||||
Identity Servers should refrain from publishing reverse mappings and should
|
||||
take steps, such as rate limiting, to prevent attackers enumerating the space of
|
||||
mappings.
|
||||
|
||||
Federation
|
||||
==========
|
||||
|
||||
Delegation
|
||||
----------
|
||||
|
||||
Verification Servers could delegate signing to another server by issuing
|
||||
certificate to that server allowing it to verify and sign a subset of 3PID on
|
||||
its behalf. It would be necessary to provide a language for describing which
|
||||
subset of 3PIDs that server had authority to validate. Alternatively it could
|
||||
delegate the verification step to another server but sign the resulting
|
||||
association itself.
|
||||
|
||||
The 3PID space will have a heirachical structure like DNS so Identity Servers
|
||||
can delegate lookups to other servers. An Identity Server should be prepared
|
||||
to host or delegate any valid association within the subset of the 3PIDs it is
|
||||
resonsible for.
|
||||
|
||||
Multiple Root Verification Servers
|
||||
----------------------------------
|
||||
|
||||
There can be multiple root Verification Servers and an Association could be
|
||||
signed by multiple servers if different clients trust different subsets of
|
||||
the verification servers.
|
||||
|
||||
Multiple Root Identity Servers
|
||||
------------------------------
|
||||
|
||||
There can be be multiple root Identity Servers. Clients will add each
|
||||
Association to all root Identity Servers.
|
||||
|
||||
[[TODO(markjh): Describe how clients find the list of root Identity Servers]]
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue