From 494b7b98430ae90c5d011d400bb6ecfbca1e2dbe Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Mon, 22 Dec 2014 15:39:11 +0000 Subject: [PATCH 01/95] Add fundamental client use cases. --- drafts/client_server_use_cases.log | 303 ----------------------------- drafts/use_cases.log | 220 +++++++++++++++++++++ 2 files changed, 220 insertions(+), 303 deletions(-) delete mode 100644 drafts/client_server_use_cases.log create mode 100644 drafts/use_cases.log diff --git a/drafts/client_server_use_cases.log b/drafts/client_server_use_cases.log deleted file mode 100644 index df6b0c6f..00000000 --- a/drafts/client_server_use_cases.log +++ /dev/null @@ -1,303 +0,0 @@ -#1: Lightweight IM client (no perm storage) -#2: Mobile IM client (perm storage) -#3: MIDI client -#4: Animatrix client -#5: Unity object trees -#6: Forum -#7: Social Network ("Walls", PMs, groups) -#8: Minecraft-clone -#9: Bug Tracking Software -#10: Global 'Like' widget, which links through to a room. - -================ - -#1: Lightweight IM client (no perm storage) -------------------------------------------- -Description: - An IM client (think web client) with no way of persisting data beyond - a session (the instance a person is using the app). -Features: - Recent activity, Room screen (member list, etc), User page, just like - the web client. -Actions: - - Send a one-to-one message to someone. - - Accept an invite. - - Populate recent activity (all rooms joined with latest message + room names/aliases, rooms invited to + room names/aliases) - - Populate scrollback if click on room - - Populate member list if click on room + get presence updates for them - - Populate room name / topic if click on room - - Create an empty room. - - Join a room from an alias. - - ---- - -Action: - Send a one-to-one message to someone. -How: - Enter their username and hit Message. Taken to room page with invited user. - History displays that I've invited someone / joined the room. Enter a message - and hit send. Message appears in window. - -:: - - Client Server - -- @user:domain --> - <--- room ID, ACK-- - <-historical msgs-- - -- msg,room ID ---> - <--- ACK ---------- - ---- - -Action: - Accept an invite. -How: - Get list of invites. Click one of them to 'accept' it. May or may not want - room content. - -:: - - Client Server - ---- req invites -> - <--- [inv,inv] ---- - ---- accept inv --> - <--- ACK ---------- - <--- room content-- (optional) - ---- - -Action: - Populate recent activity (all rooms joined with latest message + room names/aliases, rooms invited to + room names/aliases) -How: - Request joined rooms with latest message and room name. Request rooms invited to. Possibly extra info like # joined members. - -:: - - Client Server - ---- req sync ----> - <---joined rooms--- {msg,name,alias,#members?} - <---invited rooms-- {name,alias} - ---- - -Action: - Populate scrollback if click on room. -How: - Request scrollback for room. - -:: - - Client Server - ---- room id -----> - <--- scrollback --- - ---- - -Action: - Populate member list if click on room + get presence updates for them. -How: - Click on room. Member list with names/presence/pics appears. May not want - pic. - -:: - - Client Server - ---- req mem list -> - <--- members ------- {name,pic,presence} - - monitor presence-> - ... - <- presence change-- - <- presence change-- - ... - -- stop presence ---> - ---- - -Action: - Populate room name / topic if click on room. -How: - Click on room. Room name and topic with aliases appears. May not want topic - (eg screen size). - -:: - - Client Server - ---- req room info-> - <--- room info ----- {name,topic,aliases} - ---- - -Action: - Create an empty room. -How: - Type in room config (desired name, public/private, etc). Hit Create. Room is - created. Possibly get room info. - -:: - - Client Server - ---- mkroom{config}-> - <--ACK{room_id}------ - <-- room info ------- (optional) - ---- - -Action: - Join a room from an alias. -How: - Type in alias. Hit Join. Room is joined. Possibly get room info. - -:: - - Client Server - -- join{alias} -----> - <--ACK{room_id}------ - <--room info--------- (optional) - - -=========================== - -#2: Mobile IM client (perm storage) ------------------------------------ -Description: - An IM client (think android/ios) which persists data on a database. -Features: - Recent activity, Room screen (member list, etc), User page, just like - the web client. -Actions: - - Send a one-to-one message to someone. - - Accept a stored invite. - - Populate recent activity (all rooms joined with latest message + room names/aliases, rooms invited to + room names/aliases) - - Populate scrollback if click on room - - Populate member list if click on room + get presence updates for them - - Populate room name / topic if click on room - - Create an empty room. - - Join a room from an alias. - - ---- - -Action: - Send a one-to-one message to someone (single room). -How: - Enter their username and hit Message. Taken to room page with invited user if no room exists, - else takes to existing room. History displays that I've invited someone or scrollback. Enter - a message and hit send. Message appears in window. - -:: - - Client Server - -- @user:domain --> - <--- room ID, ACK-- - <-historical msgs-- (optional; not if existing room) - -- msg,room ID ---> - <--- ACK ---------- - ---- - -Action: - Accept a stored invite. -How: - Send invite to server. Get room content (or NO-OP if already joined). - -:: - - Client Server - ---- accept inv --> - <--- ACK ---------- - <--- room content-- (optional) - ---- - -Action: - Populate recent activity (all rooms joined with latest message + room names/aliases, rooms invited to + room names/aliases) - incrementally. -How: - Request recent activity diff. Get updated msg/name/#members for changed values only. - -:: - - Client Server - - req sync{token}-> - <---diff{rooms}---- {msg,name,alias,#members?} - ---- - -Action: - Populate scrollback if click on room. -How: - Request scrollback for room. Either a diff or a page of scrollback - depending on cached data. - -:: - - Client Server - -room id{latest event}-> {max msgs} - <--- scrollback -------- {fresh/incremental flag} - ---- - -Action: - Populate member list if click on room + get presence updates for them. -How: - Click on room. Member list with names/presence/pics appears. May not want - pic. - -:: - - Client Server - ---- req mem list -> - <--- members ------- {name,pic,presence} - - monitor presence-> - ... - <- presence change-- - <- presence change-- - ... - -- stop presence ---> - ---- - -Action: - Populate room name / topic if click on room. -How: - Click on room. Room name and topic with aliases appears. May not want topic - (eg screen size). Display cached info until updated. - -:: - - Client Server - ---- req room info-> - <--- room info ----- {name,topic,aliases} - ---- - -Action: - Create an empty room. -How: - Type in room config (desired name, public/private, etc). Hit Create. Room is - created. Possibly get room info. - -:: - - Client Server - ---- mkroom{config}-> - <--ACK{room_id}------ - <-- room info ------- (optional) - ---- - -Action: - Join a room from an alias. -How: - Type in alias. Hit Join. Room is joined. Possibly get room info. - -:: - - Client Server - -- join{alias} -----> - <--ACK{room_id}------ - <--room info--------- (optional) - - - diff --git a/drafts/use_cases.log b/drafts/use_cases.log new file mode 100644 index 00000000..5e7b93bc --- /dev/null +++ b/drafts/use_cases.log @@ -0,0 +1,220 @@ +General UI/UX requirements: +=========================== +- Live updates +- No flicker: + * Sending message (local echo) + * Receiving images (encoding w/h) + * Scrollback + * Resolving display names (from user ID) +- Fast startup times +- Fast "opening room" times (esp. when clicking through from a notification) + + +#1 Web client UI +================ + +Model:: + + Rooms ----< Messages + - name - type (call/image) + - topic + +Home Screen + What's visible: + - Recent chats ordered by timestamp of latest event (with # users) + - Your own display name, user ID and avatar url + - A list of public rooms (with # users and alias + room name + room topic) + What you can do: + - Create a room (public/private, with alias) + - Join a room from alias + - Message a user (with user ID) + - Leave a recent room + - Open a room + +Chat Screen + What's visible: + - Enough scrollback to fill a "screen full" of content. + - Each message: timestamp, user ID, display name at the time the message was + sent, avatar URL at the time the message was sent, whether it was a bing message + or not. + - User list: for each user: presence, current avatar url in the room, current + display name in the room, power level, ordered by when they were last speaking. + - Recents list: (same as Home Screen) + - Room name + - Room topic + - Typing notifications + - Desktop/Push Notifications for messages + What you can do: + - Invite a user + - Kick a user + - Ban/Unban a user + - Leave the room + - Send a message (image/text/emote) + - Change someone's power level + - Change your own display name + - Accept an incoming call + - Make an outgoing call + - Get older messages by scrolling up (scrollback) + - Redact a message + - Resend a message which was not sent + Message sending: + - Immediate local echo + - Queue up messages which haven't been sent yet + - Reordering local echo to where it actually happened + VoIP: + - One entry in your display for a call (which may contain duration, type, status) + - Glare resolution + Scrollback: + - Display in reverse chronological order by the originating server's timestamp + - Terminates at the start of the room (which then makes it impossible to request + more scrollback) + Local storage: + - Driven by desire for fast startup times and minimal network traffic + - Display messages from storage and from the network without any gaps in messages. + - Persist scrollback if possible: Scrollback from storage first then from the + network. + Notifications: + - Receive notifications for rooms you're interested in (explicitly or from a default) + - Maybe per device. + - Maybe depending on presence (e.g. idle) + - Maybe depending on message volume + - Maybe depending on room config options. + Message contents: + - images + - video + - rich text + - audio + - location + - vcards (potentially) + +User screen + What's visible: + - Display name + - Avatar + - User ID + What you can do: + - Start a chat with the user + + +#2 Bug tracking UI +================== + +Model:: + + Projects ----< Issues ---< Comments + - key - summary - user + - name - ID - message + SYN SYN-52 Fix it nooow! + +Landing page + What's visible: + - Issues assigned to me + - Issues I'm watching + - Recent activity on other issues (not refined to me) + - List of projects + What you can do: + - View an issue + - Create an issue + - Sort issues + - View a user + - View a project + - Search for issues (by name, time, priority, description contents, reporter, etc...) + +Issue page + What's visible: + - Summary of issue + - Issue key + - Project affected + - Description + - Comments + - Priority, labels, type, purpose, etc.. + - Reporter/assignee + - Creation and last updated times + - History of issue changes + What you can do: + - Comment on issue + - Change issue info (labels, type, purpose, etc..) + - Open/Close/Resolve the issue + - Edit the issue + - Watch/Unwatch the issue + + +#3 Forum UI +=========== + +Model:: + + Forum ----< Boards ----< Threads ----< Messages + - Matrix - Dev - HALP! - please halp! + +Main page + What's visible: + - Categories (containing boards) + - Boards (with names and # posts and tagline and latest post) + What you can do: + - View a board + - View the latest message on a board + +Board page + What's visible: + - Threads (titles, OP, latest post date+author, # replies, # upvotes, whether + the OP contains an image or hyperlink (small icon on title)) + - Whether the thread is answered (with link to the answer) + - Pagination for posts within a thread (1,2,3,4,5...10) + - Pagination for threads within a board + - List of threads in chronological order + - Stickied threads + What you can do: + - View a user + - View a thread on a particular page + - View the latest message on a thread + - View older threads (pagination) + - Search the board + +Thread page + What's visible: + - Messages in chronological order + - For each message: author, timestamp, # posts by author, avatar, registration + date, status message, message contents, # views of message + What you can do: + - Upvote the message + - Flag the message for a mod + - Reply to the message + - Subscribe to thread or message's RSS feed + - Go to previous/next thread + + +#4 Google+ community +==================== + +Model:: + + Community -----< Categories ----< Posts ---< Comments + Kerbal SP Mods, Help Text Text + (no title!) + +Communities page + What's visible: + - List of communities + - For each community: # users, # posts, group pic, title + What you can do: + - Join a community + - View a community + +Community Page + What's visible: + - Title, pic + - List of categories + - List of members with avatars (+ total #) + - Most recent posts with comments (most recent comment if >1) + What you can do: + - Join the group + - Post a post (with voting and options) + - Report abuse + - View member + - Expand comments + - Infinite scrolling + - Add a comment to a post + - Share a post + - +1 a post + From 3fb8eb8cf7421b273b419f146388114cf72d41e7 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Mon, 22 Dec 2014 15:41:06 +0000 Subject: [PATCH 02/95] rstify --- drafts/{use_cases.log => use_cases.rst} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename drafts/{use_cases.log => use_cases.rst} (100%) diff --git a/drafts/use_cases.log b/drafts/use_cases.rst similarity index 100% rename from drafts/use_cases.log rename to drafts/use_cases.rst From 2f1d6cc8e2d43e2bf74fc01f68c86b3943cca591 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Mon, 22 Dec 2014 16:50:55 +0000 Subject: [PATCH 03/95] Add data models. --- drafts/data_flows.rst | 97 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 drafts/data_flows.rst diff --git a/drafts/data_flows.rst b/drafts/data_flows.rst new file mode 100644 index 00000000..d8d52d01 --- /dev/null +++ b/drafts/data_flows.rst @@ -0,0 +1,97 @@ +Mapping model use cases to matrix models (Room, Message, etc) +============================================================= + +To think about: + - Do we want to support the idea of forking off new rooms from existing ones? This + and forums could benefit from it. + +Bug tracking UI +--------------- +:: + + Projects => Rooms + Issues => Message Events + Comments => Message Events (relates_to key) + +Projects: + - Unlikely that there will be 100,000s of issues, so having to pull in all the issues for a project is okay. + - Permissions are usually per project and this Just Works. + - New issues come in automatically and Just Work. + - Can have read-only members + +Issues: + - Don't really want 1 Room per Issue, else you can have thousands of Rooms PER PROJECT, hence choice for + Issues as Messages. Don't need to join a room for each issue. + - Idea of issue owner is clear (sender of the message) + - Updating issues requires an additional event similar to comments (with ``relates_to``)? Could possibly + be state events? Don't really want all the history if say the priority was changed 1000 times, just want + the current state of the key. + +Comments: + - Additional event with ``relates_to`` key. + + +Forum +----- +:: + + Forum => Room (with pointers to Board Rooms) + Boards => Room (with pointers to Thread Rooms) + Threads => Room + Messages => Message Events + +Forum: + - Contains 10s of Boards. + - Contains special Message Events which point to different rooms f.e Board. + +Boards: + - Contains 100s of Threads. + - Contains special Message Events which point to different rooms f.e. Thread. + +Threads: + - Contains 100s of Messages. + +Can't do this nicely with the current Federation API because you have loads of +Rooms and what does posting a message look like? Creating a thread is done by..? +The user who is posting cannot create the thread because otherwise they would be +the room creator and have ultimate privileges. So it has to be created by a bot +of some kind which ties into auth (Application services?). To follow a board, +you need a bot to join the Board Room and then watch it for changes... + +Fundamental problem with forums is that there is only 1 PDU graph per room and +you either have to pull in lots of graphs separately or one graph and filter it +separately to get to the desired sub set of data. You have to subscribe into a +lot of graphs if you subscribe to a board... If you have the entire board... +good luck scrollbacking a particular thread. + + +Google+ Community +----------------- +:: + + Community => Room (with pointers to Category Rooms) + Category => Room + Post => Message Events + Comment => Message Events (relates_to key) + +Community: + - Contains 10s of categories. + - Contains special Message Events which point to different rooms f.e Category. + - Moderators of the community are mods in this room. They are in charge of making + new categories and the subsequent rooms. Can get a bit funky if a mod creates a + category room without the same permissions as the community room... but another + mod can always delete the pointer to the buggy category room and make a new one. + - Do we want to support the idea of forking off new rooms from existing ones? This + and forums could benefit from it. + +Category: + - Contains 1000s of posts. + - Same permissions as the community room. How to enforce? Fork off the community + room? + +Posts: + - Contains 10s of comments. + +This is similar to forums but you can more reasonably say "screw it, pull in the +entire community of posts." + From eb9a8c39e72b2753426061a1de22867a2b860626 Mon Sep 17 00:00:00 2001 From: Kegsay Date: Tue, 23 Dec 2014 12:04:05 +0000 Subject: [PATCH 04/95] Update data_flows.rst Add some data flows for IM. --- drafts/data_flows.rst | 95 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) diff --git a/drafts/data_flows.rst b/drafts/data_flows.rst index d8d52d01..b1e2cd32 100644 --- a/drafts/data_flows.rst +++ b/drafts/data_flows.rst @@ -1,3 +1,98 @@ +Data flows for use cases +======================== + +:: + + <- Data from server to client + -> Data from client to server + +Instant Messaging +----------------- + +Without storage +~~~~~~~~~~~~~~~ + +:: + + Home screen + Data required on load: + <- For each room the user is joined: Name, topic, # members, last message, room ID, aliases + Data required when new message arrives for a room: + <- Room ID, message content, sender (user ID, display name, avatar url) + Data required when someone invites you to a room: + <- Room ID, sender (user ID, display name, avatar url), Room Name, Room Topic + Data required when you leave a room on another device: + <- Room ID + Data required when you join a room on another device: + <- Name, topic, # members, last message, room ID, aliases + Data required when your profile info changes on another device: + <- new profile info e.g. avatar, display name, etc. + + Creating a room + -> Invitee list of user IDs, public/private, name of room, alias of room, topic of room + <- Room ID + + Joining a room (and dumped into chat screen on success) + -> Room ID / Room alias + <- Room ID, Room aliases (plural), Name, topic, member list (f.e. member: user ID, + avatar, presence, display name, power level, whether they are typing), enough + messages to fill screen (and whether there are more) + + Chat Screen + Data required when member name changes: + <- new name, room ID, user ID, when in the context of the room did this occur + Data required when the room name changes: + <- new name, room ID, old room name? + Invite a user: + -> user ID, room ID + <- display name / avatar of user invited (if known) + Kick a user: + -> user ID, room ID + <- what message it came after + Leave a room: + -> room ID + <- what message it came after + + Send a message + -> Message content, room ID, message sequencing (eg sending my 1st, 2nd, 3rd msg) + <- actual content sent (if server mods it), what message it comes after (to correctly + display the local echo) + + Place a call (receive a call is just reverse) + <- turn servers + -> SDP offer + -> Ice candidates (1 by 1; trickling) + <- SDP answer + <- Ice candidates + + Scrolling back (infinite scrolling) + -> Identifier for the earliest message, # requested messages + <- requested messages (f.e change in display name, what the old name was), whether + there are more. + + +With storage +~~~~~~~~~~~~ +:: + + Home Screen + On Load + -> Identifier which tells the server the client's current state (which rooms it is aware + of, which messages it has, what display names for users, etc..) + <- A delta from the client's current state to the current state on the server (e.g. the + new rooms, the *latest* message if different, the changed display names, the new + invites, etc). f.e Room: Whether the cache of the room that you have has been replaced + with this new state. + + Pre-load optimisation (not essential for this screen) + -> Number of desired messages f.e room to cache + <- f.e Room: the delta OR the entire state + + +Bug Tracking +------------ + + Mapping model use cases to matrix models (Room, Message, etc) ============================================================= From f92ac373c41396dc8e728503f33e73a6d122b2f5 Mon Sep 17 00:00:00 2001 From: Kegsay Date: Tue, 23 Dec 2014 14:01:30 +0000 Subject: [PATCH 05/95] Update data_flows.rst Add bug tracking. --- drafts/data_flows.rst | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/drafts/data_flows.rst b/drafts/data_flows.rst index b1e2cd32..4e8cd09a 100644 --- a/drafts/data_flows.rst +++ b/drafts/data_flows.rst @@ -91,6 +91,36 @@ With storage Bug Tracking ------------ +:: + + Landing Page + On Load + <- Issues assigned to me, Issues I'm watching, Recent activity on other issues includes + comments, list of projects + + Search for an issue (assume text) + -> Search string + <- List of paginated issues + Request page 2: + -> Page number requested + <- Page of paginated issues + + Issue Page + On Load + -> Issue ID and Project ID (equiv to Room) + <- Issue contents e.g. priority, resolution state, etc. All comments e.g. user ID, + comment text, timestamp. Entire issue history e.g. changes in priority + + Post a comment + -> Issue ID, comment content, Project ID (equiv to Room) + <- actual content sent (if modded), what comment it comes after + + Set issue priority + -> Issue ID, Project ID, desired priority + <- What action in the history it came after + + Someone else sets issue priority + <- Issue ID, Project ID, new priority, where in the history Mapping model use cases to matrix models (Room, Message, etc) From 8e3e67fa397ede2468a4922275dd164f66abdd8d Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Tue, 23 Dec 2014 14:06:10 +0000 Subject: [PATCH 06/95] Add stub object model --- drafts/object_model.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 drafts/object_model.rst diff --git a/drafts/object_model.rst b/drafts/object_model.rst new file mode 100644 index 00000000..9c558e35 --- /dev/null +++ b/drafts/object_model.rst @@ -0,0 +1 @@ +. From fc3b135cbb46903076e9b2ee05774b95ff419846 Mon Sep 17 00:00:00 2001 From: Mark Haines Date: Tue, 23 Dec 2014 14:50:34 +0000 Subject: [PATCH 07/95] Ascii art for object model --- drafts/object_model.rst | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/drafts/object_model.rst b/drafts/object_model.rst index 9c558e35..1ae60b2c 100644 --- a/drafts/object_model.rst +++ b/drafts/object_model.rst @@ -1 +1,28 @@ -. + + + + +:: + + +---------------+ + | Room | + | "Room-ID" | + | {State} | +----------------------+ + | Name------|-------->| Event m.room.name | + | Topic | | "Name" | + | [Aliases] | +----------------------+ +-------------+ + | [Members]-|---+ +----------------------+ <----| Start Token | + | [Messages] | | | Event m.room.member | +-------------+ + | | | | +---->| "invite/join/ban" | + +---------------+ | "User-ID" | + | | +----------------------+ + | | +----------------------+ + | | Message | Event m.room.message | + | +-------------->| {content} |<--+ + | +----------------------+ | + | Comment +----------------------+ | + +------------------>| Event m.room.message | | + | {content} | | + | "relates-to"-------|---+ +-------------+ + +----------------------+ <----| End Token | + +-------------+ From 40424eded94deb43e086b5385b6d38e8e5a26578 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Wed, 24 Dec 2014 20:23:39 +0000 Subject: [PATCH 08/95] a few forgotten things --- drafts/use_cases.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drafts/use_cases.rst b/drafts/use_cases.rst index 5e7b93bc..85725be9 100644 --- a/drafts/use_cases.rst +++ b/drafts/use_cases.rst @@ -8,6 +8,7 @@ General UI/UX requirements: * Resolving display names (from user ID) - Fast startup times - Fast "opening room" times (esp. when clicking through from a notification) +- Low latency file transfer. #1 Web client UI @@ -84,6 +85,7 @@ Chat Screen - video - rich text - audio + - arbitrary files - location - vcards (potentially) From 1a92548b9c46c3fac5f49e775cfe3f30382519b0 Mon Sep 17 00:00:00 2001 From: Kegsay Date: Mon, 29 Dec 2014 16:46:48 +0000 Subject: [PATCH 09/95] Add general API design Several missing sections: VoIP, presence, typing, but many core ordering and event mapping issues are addressed. --- drafts/general_api.rst | 210 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 210 insertions(+) create mode 100644 drafts/general_api.rst diff --git a/drafts/general_api.rst b/drafts/general_api.rst new file mode 100644 index 00000000..8a0cd092 --- /dev/null +++ b/drafts/general_api.rst @@ -0,0 +1,210 @@ +Instant Messaging +================= + +TODO: + - EDU stuff (presence/typing) + - Concept of a session (for announcing of presence, and scoping of action IDs) + - The actual filter APIs themselves + - VoIP + +Global ``/initialSync`` API +--------------------------- +Inputs: + - A way of identifying the user (e.g. access token, user ID, etc) +Outputs: + - For each room the user is joined: Name, topic, # members, last message, room ID, aliases +What data flows does it address: + - Home screen: data required on load. + +Event Stream API +---------------- +Inputs: + - Position in the stream + - Filter to apply: which event types, which room IDs, whether to get out-of-order events, which users + to get presence/profile updates for + - User ID + - Device ID +Outputs: + - 0-N events the client hasn't seen. + - New position in the stream. +State Events Ordering Notes: + - Home servers may receive state events over federation that are superceded by state events previously + sent to the client. The home server *cannot* send these events to the client else they would end up + erroneously clobbering the superceding state event. + - As a result, the home server reserves the right to omit sending state events which are known to be + superceded already. + - This may result in missed *state* events. However, the state of the room will always be eventually + consistent. +Message Events Ordering Notes: + - Home servers may receive message events over federation that happened a long time ago. The client + may or may not be interested in these message events. + - For clients which do not persist scrollback for a room, this is not a problem as they only care + about the recent messages. + - For clients which do persist scrollback for a room, they need to know about the message event and + where to insert it so that scrollback remains consistent and doesn't omit messages. + - Clients can specify an input parameter stating that they wish to receive these out-of-order events. + - The event, when it comes down the stream, will indicate which event it comes after. +What data flows does it address: + - Home Screen: Data required when new message arrives for a room + - Home Screen: Data required when someone invites you to a room + - Home Screen: Data required when you leave a room on another device + - Home Screen: Data required when you join a room on another device + - Home Screen: Data required when your profile info changes on another device + - Chat Screen: Data required when member name changes + - Chat Screen: Data required when the room name changes + - Chat Screen: Data required when a new message arrives + +Room Creation +------------- +Inputs: + - Invitee list of user IDs, public/private, name of room, alias of room, topic of room +Output: + - Room ID +Notes: + - This is a special case of joining a room. See the notes on joining a room. +What data flows does it address: + - Home Screen: Creating a room + +Joining a room +-------------- +Inputs: + - Room ID / alias + - Optional filter (which events to return, whether the returned events should come down + the event stream) +Outputs: + - Room ID, Room aliases (plural), Name, topic, member list (f.e. member: user ID, + avatar, presence, display name, power level, whether they are typing), enough + messages to fill screen (and whether there are more) +Notes: + - How do you return room information? In response to the join, or from the event stream? + - The events returned need to be filterable. Different clients for the same user may want + different information (e.g. the client performing the join may jump to the chat screen and + therefore want some messages, whereas the client not performing the join just needs to be + aware of the new room). + - As a result, the join response should return events *instead of* to the event stream, depending + on the client. +Mapping messages to the event stream: + - Once you join a room, you will start getting message events for it. How do you know when + you started getting events for this room? You need to know so you can provide a token when + scrolling back. You cannot currently infer this from the join event itself, as individual + events do not have tokens (only chunks do). + - This token can be provided as a separate server-generated event, or an annotation on the join + event itself. + - We propose that a server-generated event is sent down the event stream to all clients, rather + than annotating the join event. The server-generated event works nicely for Application + Services where an entity subscribes to a room without a join event. +What data flows does it address: + - Home Screen: Joining a room + +Scrolling back (infinite scrolling) +----------------------------------- +Inputs: + - Identifier for the earliest event + - # requested events + - filter to apply + - flag to say if the home server should do a backfill over federation +Outputs: + - requested events (f.e change in display name, what the old name was), + - whether there are more events on the local HS / over federation. + - new identifier for the earliest event +What data flows does it address: + - Chat Screen: Scrolling back (infinite scrolling) + +Action APIs +----------- +The following APIs are "action APIs". This is defined to be a request which alters the state of +a room you are already joined to. + +When you perform an action in a room, you immediately want to display the local echo. The client +can receive the response to the action either directly or from the event stream. The order in which +you receive these responses is undefined. As a result, clients MUST be able to handle all possible +orderings:: + + 1 2a 3 + START ----> REQUEST SENT ---> RESPONSE TO REQUEST RECEIVED --------> GOT BOTH + | ^ + | 2b | + +----------> APPEARS IN EVENT STREAM -------------------+ + + 1: Can display local echo at this point. + 2a: The request has been successfully processed and can be displayed as Sent. + 2b/3: The request has been successfully processed and the client knows its position in the event stream. + +When a client sends a request, they can include an "action ID" so that they can match up the event in +the event stream to the request which they made. This ID is created by the client, and MUST be a +monotonically increasing integer for that client. This ID serves as a transaction ID for idempotency as +well as a sequence ID for ordering actions performed in parallel by that client. Events for actions +performed by a client in that client's event stream will include the action ID the client submitted +when making the request. The action ID will *not* appear in other client's event streams. + +Action IDs are optional and are only needed by clients that retransmit their requests, or display local +echo, or allow the submission of multiple requests in parallel. An example of a client which may not need +the use of action IDs includes bots which operate using basic request/responses in a synchronous fashion. + +Inviting a user +~~~~~~~~~~~~~~~ +Inputs: + - User ID + - Room ID + - Action ID (optional) +Outputs: + - Display name / avatar of user invited (if known) +What data flows does it address: + - Chat Screen: Invite a user + +Kicking a user +~~~~~~~~~~~~~~ +Inputs: + - User ID + - Room ID + - Action ID (optional) +Outputs: + - None. +What data flows does it address: + - Chat Screen: Kick a user + +Leaving a room +~~~~~~~~~~~~~~ +Inputs: + - Room ID + - A way of identifying the user (user ID, access token) + - Action ID (optional) +Outputs: + - None. +What data flows does it address: + - Chat Screen: Leave a room + +Send a message +~~~~~~~~~~~~~~ +Inputs: + - Room ID + - Message contents + - Action ID (optional) +Outputs: + - Actual content sent (if server modified it) + - When in the stream this action happened. (to correctly display local echo) +What data flows does it address: + - Chat Screen: Send a Message + +VoIP +---- +WIPWIPWIPWIPWIPWIPWIPWIPWIPWIPWIPWIP + +Placing a call (initial) +~~~~~~~~~~~~~~~~~~~~~~~~ +Inputs: + - WIP +Outputs: + - WIP +What data flows does it address: + - WIP + +Placing a call (candidates) +~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Inputs: + - WIP +Outputs: + - WIP +What data flows does it address: + - WIP + From 12382bbd858753d481e09eca528e017e003399d7 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Tue, 30 Dec 2014 10:00:33 +0000 Subject: [PATCH 10/95] Add back in the use case bullet points --- drafts/use_cases.rst | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drafts/use_cases.rst b/drafts/use_cases.rst index 85725be9..999d5340 100644 --- a/drafts/use_cases.rst +++ b/drafts/use_cases.rst @@ -10,6 +10,18 @@ General UI/UX requirements: - Fast "opening room" times (esp. when clicking through from a notification) - Low latency file transfer. +Use cases +--------- +- #1: Lightweight IM client (no perm storage)perm +- #2: Mobile IM client (perm storage)perm +- #3: MIDI client +- #4: Animatrix client +- #5: Unity object trees +- #6: Forum +- #7: Social Network ("Walls", PMs, groups)PMs-#8: Minecraft-clone +- #9: Bug Tracking Software +- #10: Global 'Like' widget, which links through to a room. + #1 Web client UI ================ From fe052ea4f89743cacd561dec4d1287b0f663e45a Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Tue, 30 Dec 2014 10:04:23 +0000 Subject: [PATCH 11/95] Match numbering used in the document --- drafts/use_cases.rst | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/drafts/use_cases.rst b/drafts/use_cases.rst index 999d5340..5bd6de33 100644 --- a/drafts/use_cases.rst +++ b/drafts/use_cases.rst @@ -12,15 +12,17 @@ General UI/UX requirements: Use cases --------- -- #1: Lightweight IM client (no perm storage)perm -- #2: Mobile IM client (perm storage)perm -- #3: MIDI client -- #4: Animatrix client -- #5: Unity object trees -- #6: Forum -- #7: Social Network ("Walls", PMs, groups)PMs-#8: Minecraft-clone -- #9: Bug Tracking Software -- #10: Global 'Like' widget, which links through to a room. +- #1: Lightweight IM client (no perm storage) - e.g. Web client +- #2: Bug tracking software +- #3: Forum +- #4: Google + style communities +- #5: Mobile IM client (perm storage) +- #6: MIDI client +- #7: Animatrix client +- #8: Unity object trees +- #9: Social Network ("Walls", PMs, groups) +- #10: Minecraft-clone +- #11: Global 'Like' widget, which links through to a room. #1 Web client UI From 65348aa578d4deb990f3cc8c4e6e1562a564d9ee Mon Sep 17 00:00:00 2001 From: Kegsay Date: Tue, 30 Dec 2014 15:37:26 +0000 Subject: [PATCH 12/95] Add more info on sessions, filtering and updates/replies to --- drafts/general_api.rst | 160 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 154 insertions(+), 6 deletions(-) diff --git a/drafts/general_api.rst b/drafts/general_api.rst index 8a0cd092..effa5e6f 100644 --- a/drafts/general_api.rst +++ b/drafts/general_api.rst @@ -1,16 +1,25 @@ Instant Messaging ================= - -TODO: - - EDU stuff (presence/typing) - - Concept of a session (for announcing of presence, and scoping of action IDs) - - The actual filter APIs themselves - - VoIP + +Filter API +---------- +Inputs: + - Which event types (incl wildcards) + - Which room IDs + - Which user IDs (for profile/presence) +Outputs: + - An opaque token which represents the inputs +Notes: + - The token may expire, in which case you would need to request another one. + - The token could be as simple as a concatenation of the requested filters with a delimiter between them. + - Omitting the token on APIs results in ALL THE THINGS coming down. + - Clients should remember which token they need to use for which API. Global ``/initialSync`` API --------------------------- Inputs: - A way of identifying the user (e.g. access token, user ID, etc) + - Filter to apply Outputs: - For each room the user is joined: Name, topic, # members, last message, room ID, aliases What data flows does it address: @@ -186,6 +195,145 @@ Outputs: What data flows does it address: - Chat Screen: Send a Message +Sessions +-------- +A session is a group of requests sent within a short amount of time by the same client. Starting +a session is known as going "online". Its purpose is to wrap up the expiry of presence and +typing notifications into a clearer scope. A session starts when the client makes any request. +A session ends when the client doesn't make a request for a particular amount of time (times out). +A session can also end when explicitly hitting a particular endpoint. This is known as going "offline". + +When a session starts, a session ID is sent in response to the first request the client makes. This +session ID should be sent in *all* subsequent requests. If the server expires a session and the client +uses an old session ID, the server should fail the request with the old session ID and send a new +session ID in response for the client to use. If the client receives a new session ID mid-session, +it must re-establish its typing status and presence status, as they are linked to the session ID. + +Presence +~~~~~~~~ +When a session starts, the home server can treat the user as "online". When the session ends, the home +server can treat the user as "offline". + +Inputs: + - Presence state (online, offline, away, busy, do not disturb, etc) +Outputs: + - None. +Notes: + - TODO: Handle multiple devices. + + +Typing +~~~~~~ +When in a session, a user can send a request stating that they are typing in a room. They are no longer +typing when either the session ends or they explicitly send another request to say they are no longer +typing. + +Inputs: + - Room ID + - Whether you are typing or not. +Output: + - None. +Notes: + - Typing will time out when the session ends. + +Action IDs +~~~~~~~~~~ +Action IDs are scoped per session. The first action ID for a session should be 0. For each subsequent +action request, the ID should be incremented by 1. It should be reset to 0 when a new session starts. + +If the client sends an action request with a stale session ID, the home server MUST fail the request +and start a new session. The request needs to be failed in order to avoid edge cases with incrementing +action IDs. + +Updates (Events) +---------------- +Events may update other events. This is represented by the ``updates`` key. This is a key which +contains the event ID for the event it relates to. Events that relate to other events are referred to +as "Child Events". The event being related to is referred to as "Parent Events". Child events cannot +stand alone as a separate entity; they require the parent event in order to make sense. + +Bundling +~~~~~~~~ +Events that relate to another event should come down inside that event. That is, the top-level event +should come down with all the child events at the same time. This is called a "bundle" and it is +represented as an array of events inside the top-level event.There are some issues with this however: + +- Scrollback: Should you be told about child events for which you do not know the parent event? + Conclusion: No you shouldn't be told about child events. You will receive them when you scroll back + to the parent event. +- Pagination of child events: You don't necessarily want to have 1000000s of child events with the + parent event. We can't reasonably paginate child events because we require all the child events + in order to display the event correctly. Comments on a message should be done via another technique, + such as ``in_reply_to`. +- Do you allow child events to relate to other child events? There is no technical reason why we + cannot nest child events, however we can't think of any use cases for it. The behaviour would be + to get the child events recursively from the top-level event. + +Main use cases for ``updates``: + - Call signalling (child events are ICE candidates, answer to the offer, and termination) + - *Local* Delivery/Read receipts : "Local" means they are not shared with other users on the same home + server or via federation but *are* shared between clients for the same user; useful for push + notifications, read count markers, etc. This is done to avoid the ``n^2`` problem for sending + receipts, where the vast majority of traffic tends towards sending more receipts. + - s/foo/bar/ style message edits + +Clients *always* need to know how to apply the deltas because clients may receive the events separately +down the event stream. Combining event updates server-side does not make client implementation simpler, +as the client still needs to know how to combine the events. + +In reply to (Events) +-------------------- +Events may be in response to other events, e.g. comments. This is represented by the ``in_reply_to`` +key. This differs from the ``updates`` key as they *do not update the event itself*, and are *not required* +in order to display the parent event. Crucially, the child events can be paginated, whereas ``updates`` child events cannot +be paginated. + +Bundling +~~~~~~~~ +Child events can be optionally bundled with the parent event, depending on your display mechanism. The +number of child events which can be bundled should be limited to prevent events becoming too large. This +limit should be set by the client. If the limit is exceeded, then the bundle should also include a pagination +token so that the client can request more child events. + +Main use cases for ``in_reply_to``: + - Comments on a message. + - Non-local delivery/read receipts : If doing separate receipt events for each message. + - Meeting invite responses : Yes/No/Maybe for a meeting. + +Like with ``updates``, clients need to know how to apply the deltas because clients may receive the +events separately down the event stream. + +TODO: + - Can a child event reply to multiple parent events? Use case? + - Should a parent event and its children share a thread ID? Does the originating HS set this ID? Is + this thread ID exposed through federation? e.g. can a HS retrieve all events for a given thread ID from + another HS? + +Example using ``updates`` and ``in_reply_to`` +--------------------------------------------- +- Room with a single message. +- 10 comments are added to the message via ``in_reply_to``. +- An edit is made to the original message via ``updates``. +- An initial sync on this room with a limit of 3 comments, would return the message with the update + event bundled with it and the most recent 3 comments and a pagination token to request earlier comments + + .. code :: javascript + + { + content: { body: "I am teh winner!" }, + updated_by: [ + { content: { body: "I am the winner!" } } + ], + replies: { + start: "some_token", + chunk: [ + { content: { body: "8th comment" } }, + { content: { body: "9th comment" } }, + { content: { body: "10th comment" } } + ] + } + } + VoIP ---- WIPWIPWIPWIPWIPWIPWIPWIPWIPWIPWIPWIP From 2ae66c661a177399656a9e7eb867b6e17ab54579 Mon Sep 17 00:00:00 2001 From: Kegsay Date: Tue, 30 Dec 2014 16:26:27 +0000 Subject: [PATCH 13/95] General VoIP outline --- drafts/general_api.rst | 59 +++++++++++++++++++++++++++++------------- 1 file changed, 41 insertions(+), 18 deletions(-) diff --git a/drafts/general_api.rst b/drafts/general_api.rst index effa5e6f..0b8c5000 100644 --- a/drafts/general_api.rst +++ b/drafts/general_api.rst @@ -336,23 +336,46 @@ Example using ``updates`` and ``in_reply_to`` VoIP ---- -WIPWIPWIPWIPWIPWIPWIPWIPWIPWIPWIPWIP +This addresses one-to-one calling with multiple devices. This uses the ``updates`` key to +handle signalling. + +Event updates +~~~~~~~~~~~~~ +- Call is placed by caller. Event generated with offer. +- 1-N callees may pick up or reject this offer. +- Callees update the event (with sdp answer if they are accepting the call) +- Caller acknowledges *one* of the callees (either one which picked up or rejected) by updating the event. +- Callees who weren't chosen then give up (Answered elsewhere, Rejected elsewhere, etc) +- Update with ICE candidates as they appear. +- ... in call ... +- Send hangup update when hanging up. + +Placing a call +~~~~~~~~~~~~~~ +:: + + caller callee + |-----m.call.invite--->| + | | + |<----m.call.answer----| + | device_id=foo | + | | + |------m.call.ack----->| + | device_id=foo | + | | + |<--m.call.candidate---| + |---m.call.candidate-->| + | | + [...] [...] + | | + |<----m.call.hangup----| + | device_id=foo | + +Expiry +~~~~~~ +- WIP: Of invites +- WIP: Of calls themselves (as they may never send a ``m.call.hangup`` + + -Placing a call (initial) -~~~~~~~~~~~~~~~~~~~~~~~~ -Inputs: - - WIP -Outputs: - - WIP -What data flows does it address: - - WIP - -Placing a call (candidates) -~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Inputs: - - WIP -Outputs: - - WIP -What data flows does it address: - - WIP From 6904b2868ee7b68168e9a93e518f0da1a8368b00 Mon Sep 17 00:00:00 2001 From: Kegsay Date: Wed, 31 Dec 2014 12:36:56 +0000 Subject: [PATCH 14/95] Update general_api.rst Add info on smaller APIs / edge cases. --- drafts/general_api.rst | 79 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/drafts/general_api.rst b/drafts/general_api.rst index 0b8c5000..1644adad 100644 --- a/drafts/general_api.rst +++ b/drafts/general_api.rst @@ -1,5 +1,11 @@ Instant Messaging ================= + +TODO +---- +- Pagination: Would be nice to have "and X more". It will probably be Google-style estimates given + we can't know the exact number over federation, but as a purely informational display thing it would + be nice. Filter API ---------- @@ -7,6 +13,10 @@ Inputs: - Which event types (incl wildcards) - Which room IDs - Which user IDs (for profile/presence) + - Whether you want federation-style event JSON + - Whether you want coalesced ``updates`` events + - Whether you want coalesced ``in_reply_to`` events (and the max # to coalesce) + - limit= param? Outputs: - An opaque token which represents the inputs Notes: @@ -14,17 +24,31 @@ Notes: - The token could be as simple as a concatenation of the requested filters with a delimiter between them. - Omitting the token on APIs results in ALL THE THINGS coming down. - Clients should remember which token they need to use for which API. + - HTTP note: If the filter API is a separate endpoint, then you could easily allow APIs which use filtering + to ALSO specifiy query parameters to tweak the filter. Global ``/initialSync`` API --------------------------- Inputs: - A way of identifying the user (e.g. access token, user ID, etc) + - Streaming token (optional) - Filter to apply Outputs: - For each room the user is joined: Name, topic, # members, last message, room ID, aliases +Notes: + - If a streaming token is applied, you will get a delta rather than all the rooms. What data flows does it address: - Home screen: data required on load. +TODO: + - Will need some form of state event pagination like we have for message events to handle large + amounts of state events for a room. Need to think of the consequences of this: you may not get a + ``m.room.member`` for someone's message and so cannot display their display name / avatar. + - Handle paginating initial sync results themselves (e.g. 10 most recent rooms) + - No need for state events under the 'state' key to have a ``prev_content``. Can also apply some + optimisations depending on the direction of travel when scrolling back. + + Event Stream API ---------------- Inputs: @@ -53,6 +77,12 @@ Message Events Ordering Notes: where to insert it so that scrollback remains consistent and doesn't omit messages. - Clients can specify an input parameter stating that they wish to receive these out-of-order events. - The event, when it comes down the stream, will indicate which event it comes after. +Rejected events: + - A home server may find out via federation that it should not have accepted an event (e.g. to send a + message/state event in a room). + - If this happen, the home server will send a ``m.room.redaction`` for the event in question. + - If the event was a state event, it will synthesise a new state event to correct the client's room state. + - In practice, clients don't need any extra special handling for this. What data flows does it address: - Home Screen: Data required when new message arrives for a room - Home Screen: Data required when someone invites you to a room @@ -119,6 +149,18 @@ Outputs: What data flows does it address: - Chat Screen: Scrolling back (infinite scrolling) +Contextual messages +------------------- +Inputs: + - Event ID of the message to get the surrounding context for (this specifies the room to get messages in). + - Number of messages before/after this message to obtain. + - Filter to apply. +Outputs: + - Chunk of messages + - Start / End pagination tokens + - Current room state at the end of the chunk as per initial sync. + + Action APIs ----------- The following APIs are "action APIs". This is defined to be a request which alters the state of @@ -161,6 +203,25 @@ Outputs: What data flows does it address: - Chat Screen: Invite a user +Rejecting an invite +~~~~~~~~~~~~~~~~~~~ +Inputs: + - Event ID (to know which invite you're rejecting) +Outputs: + - None. +Notes: + - Giving the event ID rather than user ID/room ID combo because mutliple users can invite the + same user into the same room. + +Deleting a state event +~~~~~~~~~~~~~~~~~~~~~~ +Inputs: + - Event type + - State key + - Room ID +Outputs: + - None. + Kicking a user ~~~~~~~~~~~~~~ Inputs: @@ -194,6 +255,9 @@ Outputs: - When in the stream this action happened. (to correctly display local echo) What data flows does it address: - Chat Screen: Send a Message +E2E Notes: + - For signing: You send the original message to the HS and it will return the full event JSON which will + be sent. This full event is then signed and sent to the HS again to send the message. Sessions -------- @@ -333,6 +397,21 @@ Example using ``updates`` and ``in_reply_to`` ] } } + +Events (breaking changes; event version 2) +------------------------------------------ +- Prefix the event ``type`` to say if it is a state event, message event or ephemeral event. Needed + because you can't tell the different between message events and ephemeral ROOM events (e.g. typing). +- State keys need additional restrictions in order to increase flexibility on state event permissions. + State keys prefixed with an ``_`` have no specific restrictions. 0-length state keys are now represented + by just a single ``_``. State keys prefixed with ``@`` can be modified only by the named user ID *OR* the + room ops. They can have an optional path suffixed to it. State keys that start with a server name can only + be modified by that server name (e.g. ``some.server.com/some/path`` can only be modified by + ``some.server.com``). +- Do we want to specify what restrictions apply to the state key in the event type? This would allow HSes + to enforce this, making life easier for clients when dealing with custom event types. E.g. ``_custom.event`` + would allow anything in the state key, ``_@custom.event`` would only allow user IDs in the state key, etc. + VoIP ---- From 6e8cbb1eba3db44606e683daea47e2a23d603a42 Mon Sep 17 00:00:00 2001 From: Kegsay Date: Wed, 31 Dec 2014 13:53:45 +0000 Subject: [PATCH 15/95] Small HTTP note on message ordering --- drafts/general_api.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drafts/general_api.rst b/drafts/general_api.rst index 1644adad..cb005b00 100644 --- a/drafts/general_api.rst +++ b/drafts/general_api.rst @@ -255,6 +255,10 @@ Outputs: - When in the stream this action happened. (to correctly display local echo) What data flows does it address: - Chat Screen: Send a Message +Ordering notes: + - HTTP: When sending a message with a higher seqnum, it will block the request until it receives + earlier seqnums. The block will expire after a timeout and reject the message stating that it + was missing a seqnum. E2E Notes: - For signing: You send the original message to the HS and it will return the full event JSON which will be sent. This full event is then signed and sent to the HS again to send the message. From 9af912eba506c754505498027b736a514e953454 Mon Sep 17 00:00:00 2001 From: Kegsay Date: Wed, 31 Dec 2014 16:05:24 +0000 Subject: [PATCH 16/95] Add capability API --- drafts/general_api.rst | 57 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/drafts/general_api.rst b/drafts/general_api.rst index cb005b00..511d27a6 100644 --- a/drafts/general_api.rst +++ b/drafts/general_api.rst @@ -415,7 +415,64 @@ Events (breaking changes; event version 2) - Do we want to specify what restrictions apply to the state key in the event type? This would allow HSes to enforce this, making life easier for clients when dealing with custom event types. E.g. ``_custom.event`` would allow anything in the state key, ``_@custom.event`` would only allow user IDs in the state key, etc. +- s/user_id/sender/g given that home servers can send events, not just users. +Capabilities +------------ +How does a client know if the server it is using supports a content repository? How does a client know +if another client has VoIP support? This section outlines capability publishing for servers, +clients and federation. + +Server +~~~~~~ +- List of extensions it supports (e.g. content repo, contact repo, turn servers) + +Inputs: + - User ID (e.g. only @bob can use the content repo) +Output: + - Hash of the capabilities:: + + { + "sha256": "fD876SFrt3sugh23FWEjio3" + } + +This hash is fed into another API: + +Inputs: + - The hash of the capabilities +Output: + - A list of capabilities:: + + { + "custom.feature.v1": {}, + "m.cap.turnserver.v1": {} + } + +Client +~~~~~~ +- e.g. Whether this client supports VoIP + +When a session is started, the client needs to provide a capability set. The server will take the "union" +of all the user's connected clients' capability sets and send the hash of the capabilities as part of +presence information (not necesarily as a ``m.presence`` event, but it should act like presence events). + +On first signup, the client will attempt to send the hash and be most likely refused by the home server as +it does not know the full capability set for that hash. The client will then have to upload the full capability +set to the home server. The client will then be able to send the hash as normal. + +When a client receives a hash, the client will either recognise the hash or will have to request the capability +set from their home server: + +Inputs: + - Hash + - User ID +Output: + - A list of capabilities + +Federation +~~~~~~~~~~ +- e.g. Whether you support backfill, hypothetical search/query/threading APIs +- Same as the server capability API VoIP ---- From 3ec7a27edbe6ee23d97a6cd0046a093386a28a84 Mon Sep 17 00:00:00 2001 From: Kegsay Date: Wed, 31 Dec 2014 16:12:44 +0000 Subject: [PATCH 17/95] Clarify what deleted events look like --- drafts/general_api.rst | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drafts/general_api.rst b/drafts/general_api.rst index 511d27a6..9b8d6373 100644 --- a/drafts/general_api.rst +++ b/drafts/general_api.rst @@ -58,7 +58,8 @@ Inputs: - User ID - Device ID Outputs: - - 0-N events the client hasn't seen. + - 0-N events the client hasn't seen. NB: Deleted state events will be missing a ``content`` key. Deleted + message events are ``m.room.redaction`` events. - New position in the stream. State Events Ordering Notes: - Home servers may receive state events over federation that are superceded by state events previously @@ -212,6 +213,7 @@ Outputs: Notes: - Giving the event ID rather than user ID/room ID combo because mutliple users can invite the same user into the same room. + - Rejecting an invite results in the ``m.room.member`` state event being DELETEd for that user. Deleting a state event ~~~~~~~~~~~~~~~~~~~~~~ @@ -221,6 +223,9 @@ Inputs: - Room ID Outputs: - None. +Notes: + - This is represented on the event stream as an event lacking a ``content`` key (for symmetry + with ``prev_content``) Kicking a user ~~~~~~~~~~~~~~ From cf9bdf87fb5c212185958b38dc20f99dd68839c8 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Mon, 5 Jan 2015 13:34:52 +0000 Subject: [PATCH 18/95] Add summary --- drafts/general_api.rst | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/drafts/general_api.rst b/drafts/general_api.rst index 9b8d6373..cbd7f11e 100644 --- a/drafts/general_api.rst +++ b/drafts/general_api.rst @@ -6,6 +6,29 @@ TODO - Pagination: Would be nice to have "and X more". It will probably be Google-style estimates given we can't know the exact number over federation, but as a purely informational display thing it would be nice. + +Summary +------- +Included: + - Event filtering (type/room/users, federation-style events) + - Incremental syncing + - Rejecting invites + - Deleting state + - Contextual messages (view messages around an arbitrary message) + - Race conditions on event stream / actions + - Out-of-order events + - Capabilities + - Comments (in_reply_to key) + - Editing/updating messages (updates key) + +Excluded: + - Searching messages + - State event pagination (see Global /initialSync API) + - Initial sync pagination (see Global /initialSync API) + - PATCHing power levels + - Handling "duplicate" events in state/messages key on initial sync. + - Multiple devices (other than VoIP) + - Room directory lists (aka public room list, paginating, permissions on editing the list, etc) Filter API ---------- From 4e8713c9cec3a80abde029e5734c3ddca13523df Mon Sep 17 00:00:00 2001 From: Mark Haines Date: Mon, 29 Dec 2014 18:31:46 +0000 Subject: [PATCH 19/95] Add a chat history screen to the im client use case --- drafts/use_cases.rst | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/drafts/use_cases.rst b/drafts/use_cases.rst index 5bd6de33..956a4a3c 100644 --- a/drafts/use_cases.rst +++ b/drafts/use_cases.rst @@ -45,11 +45,12 @@ Home Screen - Message a user (with user ID) - Leave a recent room - Open a room - + - Open a chat history link. + Chat Screen What's visible: - Enough scrollback to fill a "screen full" of content. - - Each message: timestamp, user ID, display name at the time the message was + - Each message: timestamp, user ID, display name at the time the message was sent, avatar URL at the time the message was sent, whether it was a bing message or not. - User list: for each user: presence, current avatar url in the room, current @@ -102,7 +103,19 @@ Chat Screen - arbitrary files - location - vcards (potentially) - + +Chat History Screen + What's visible: + - The linked message and enough scrollback to fill a "screen full" of content. + - Each message: timestamp, user ID, display name at the time the message was + sent, avatar URL at the time the message was sent, whether it was a bing message + or not. + - The historical user list. *TODO: Is this taken at the linked message, or at + wherever the user has scrolled to?* + What you can do: + - Get older messages by scrolling up (scrollback) + - Get newer messages by scrolling down + User screen What's visible: - Display name @@ -116,7 +129,7 @@ User screen ================== Model:: - + Projects ----< Issues ---< Comments - key - summary - user - name - ID - message From a0063b1143990614dbfdc5167194a8a9cbbc6eeb Mon Sep 17 00:00:00 2001 From: Mark Haines Date: Mon, 5 Jan 2015 17:52:09 +0000 Subject: [PATCH 20/95] Add a section for searching for public rooms to the use cases --- drafts/use_cases.rst | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/drafts/use_cases.rst b/drafts/use_cases.rst index 956a4a3c..593b1036 100644 --- a/drafts/use_cases.rst +++ b/drafts/use_cases.rst @@ -38,7 +38,7 @@ Home Screen What's visible: - Recent chats ordered by timestamp of latest event (with # users) - Your own display name, user ID and avatar url - - A list of public rooms (with # users and alias + room name + room topic) + - A searchable list of public rooms (with # users and alias + room name + room topic) What you can do: - Create a room (public/private, with alias) - Join a room from alias @@ -46,6 +46,7 @@ Home Screen - Leave a recent room - Open a room - Open a chat history link. + - Search for a public room. Chat Screen What's visible: @@ -116,6 +117,17 @@ Chat History Screen - Get older messages by scrolling up (scrollback) - Get newer messages by scrolling down +Public Room Search Screen + What's visible: + - The current search text. + - The homeserver being searched. + - The results of the current search with enough results to fill the screen + with # users and alias + room name + room topic. + What you can do: + - Change what you are searching for. + - Change the server that's being searched. + - Scroll down to get more search results. + User screen What's visible: - Display name From 72f024570103d9434556d6cf95143d66951b68b2 Mon Sep 17 00:00:00 2001 From: Mark Haines Date: Mon, 5 Jan 2015 17:54:15 +0000 Subject: [PATCH 21/95] Note that default HS to search is the on the client connects to --- drafts/use_cases.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drafts/use_cases.rst b/drafts/use_cases.rst index 593b1036..a6931b08 100644 --- a/drafts/use_cases.rst +++ b/drafts/use_cases.rst @@ -120,7 +120,7 @@ Chat History Screen Public Room Search Screen What's visible: - The current search text. - - The homeserver being searched. + - The homeserver being searched (defaults to the HS the client is connected to). - The results of the current search with enough results to fill the screen with # users and alias + room name + room topic. What you can do: From 399600744913e6f0337abeab4a41464465cab8cb Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Tue, 6 Jan 2015 13:06:01 +0000 Subject: [PATCH 22/95] Add a bunch of TODO points. Tweak wording and give more info on some APIs. --- drafts/general_api.rst | 78 +++++++++++++++++++++++++++++------------- 1 file changed, 54 insertions(+), 24 deletions(-) diff --git a/drafts/general_api.rst b/drafts/general_api.rst index cbd7f11e..d7e0115a 100644 --- a/drafts/general_api.rst +++ b/drafts/general_api.rst @@ -6,6 +6,22 @@ TODO - Pagination: Would be nice to have "and X more". It will probably be Google-style estimates given we can't know the exact number over federation, but as a purely informational display thing it would be nice. +- HTTP Ordering: Blocking requests with higher seqnums is troublesome if there is a max # of concurrent + connections a client can have open. +- Session expiry: Do we really have to fonx the request if it was done with an old session ID? +- Server capabilities: Keep hashing step for consistency or not? Extra request. +- Client capabilities: List of hashes f.e device vs union of hashes on all devices? +- Client capabilities: Clients which are offline but can be pushed should have their capabilities visible. + How to manage unregistering them e.g. if they uninstall the app? +- What do server-generated events look like? +- What do read-up-to markers look like? +- Offline mode? How does that work with sessions? +- Per device presence +- Receiving events for unknown rooms. How do you handle this? +- Linking the termination of typing events to the message itself, so you don't need to send + two events and don't get flicker. +- Filtering: Blacklist (negative) filters? E.g. "don't give me ``event.type.here`` events +- Public room list API Summary ------- @@ -18,7 +34,7 @@ Included: - Race conditions on event stream / actions - Out-of-order events - Capabilities - - Comments (in_reply_to key) + - Comments (relates_to key) - Editing/updating messages (updates key) Excluded: @@ -38,8 +54,9 @@ Inputs: - Which user IDs (for profile/presence) - Whether you want federation-style event JSON - Whether you want coalesced ``updates`` events - - Whether you want coalesced ``in_reply_to`` events (and the max # to coalesce) + - Whether you want coalesced ``relates_to`` events (and the max # to coalesce) - limit= param? + - Which keys to return for events? e.g. no ``origin_server_ts`` if you don't show timestamps Outputs: - An opaque token which represents the inputs Notes: @@ -49,15 +66,22 @@ Notes: - Clients should remember which token they need to use for which API. - HTTP note: If the filter API is a separate endpoint, then you could easily allow APIs which use filtering to ALSO specifiy query parameters to tweak the filter. +TODO: + - Do we want to specify negative filters (e.g. don't give me ``event.type.here`` events) Global ``/initialSync`` API --------------------------- Inputs: - A way of identifying the user (e.g. access token, user ID, etc) - Streaming token (optional) + - Which state event types to return (e.g. ``m.room.name`` / ``m.room.topic`` / ``m.room.aliases``) - Filter to apply Outputs: - - For each room the user is joined: Name, topic, # members, last message, room ID, aliases + - For each room the user is joined: + - Requested state events + - # members + - max of limit= message events + - room ID Notes: - If a streaming token is applied, you will get a delta rather than all the rooms. What data flows does it address: @@ -67,6 +91,7 @@ TODO: - Will need some form of state event pagination like we have for message events to handle large amounts of state events for a room. Need to think of the consequences of this: you may not get a ``m.room.member`` for someone's message and so cannot display their display name / avatar. + Do we want to provide pagination on an event type basis? - Handle paginating initial sync results themselves (e.g. 10 most recent rooms) - No need for state events under the 'state' key to have a ``prev_content``. Can also apply some optimisations depending on the direction of travel when scrolling back. @@ -95,8 +120,8 @@ State Events Ordering Notes: Message Events Ordering Notes: - Home servers may receive message events over federation that happened a long time ago. The client may or may not be interested in these message events. - - For clients which do not persist scrollback for a room, this is not a problem as they only care - about the recent messages. + - For clients which do not store scrollback for a room (they discard events after processing them), + this is not a problem as they only care about the recent messages. - For clients which do persist scrollback for a room, they need to know about the message event and where to insert it so that scrollback remains consistent and doesn't omit messages. - Clients can specify an input parameter stating that they wish to receive these out-of-order events. @@ -104,8 +129,10 @@ Message Events Ordering Notes: Rejected events: - A home server may find out via federation that it should not have accepted an event (e.g. to send a message/state event in a room). - - If this happen, the home server will send a ``m.room.redaction`` for the event in question. + - If this happens, the home server will send a ``m.room.redaction`` for the event in question. This will + be a local server event (not shared with other servers). - If the event was a state event, it will synthesise a new state event to correct the client's room state. + This will be a local server event (not shared with other servers). - In practice, clients don't need any extra special handling for this. What data flows does it address: - Home Screen: Data required when new message arrives for a room @@ -120,7 +147,8 @@ What data flows does it address: Room Creation ------------- Inputs: - - Invitee list of user IDs, public/private, name of room, alias of room, topic of room + - Invitee list of user IDs, public/private, state events to set on creation e.g. + name of room, alias of room, topic of room Output: - Room ID Notes: @@ -131,13 +159,13 @@ What data flows does it address: Joining a room -------------- Inputs: - - Room ID / alias + - Room ID (with list of servers to join from) / room alias / invite event ID - Optional filter (which events to return, whether the returned events should come down the event stream) Outputs: - - Room ID, Room aliases (plural), Name, topic, member list (f.e. member: user ID, - avatar, presence, display name, power level, whether they are typing), enough - messages to fill screen (and whether there are more) + - Room ID, the returned state events from the filter e.g. Room aliases (plural), Name, + topic, member list (f.e. member: user ID, avatar, presence, display name, power level, + whether they are typing), enough messages to fill screen (and whether there are more) Notes: - How do you return room information? In response to the join, or from the event stream? - The events returned need to be filterable. Different clients for the same user may want @@ -293,7 +321,8 @@ E2E Notes: Sessions -------- -A session is a group of requests sent within a short amount of time by the same client. Starting +A session is a group of requests sent within a short amount of time by the same client. +Sessions time out after a short amount of time without any requests. Starting a session is known as going "online". Its purpose is to wrap up the expiry of presence and typing notifications into a clearer scope. A session starts when the client makes any request. A session ends when the client doesn't make a request for a particular amount of time (times out). @@ -360,7 +389,7 @@ represented as an array of events inside the top-level event.There are some issu - Pagination of child events: You don't necessarily want to have 1000000s of child events with the parent event. We can't reasonably paginate child events because we require all the child events in order to display the event correctly. Comments on a message should be done via another technique, - such as ``in_reply_to`. + such as ``relates_to`. - Do you allow child events to relate to other child events? There is no technical reason why we cannot nest child events, however we can't think of any use cases for it. The behaviour would be to get the child events recursively from the top-level event. @@ -377,9 +406,9 @@ Clients *always* need to know how to apply the deltas because clients may receiv down the event stream. Combining event updates server-side does not make client implementation simpler, as the client still needs to know how to combine the events. -In reply to (Events) --------------------- -Events may be in response to other events, e.g. comments. This is represented by the ``in_reply_to`` +Relates to (Events) +------------------- +Events may be in response to other events, e.g. comments. This is represented by the ``relates_to`` key. This differs from the ``updates`` key as they *do not update the event itself*, and are *not required* in order to display the parent event. Crucially, the child events can be paginated, whereas ``updates`` child events cannot be paginated. @@ -391,7 +420,7 @@ number of child events which can be bundled should be limited to prevent events limit should be set by the client. If the limit is exceeded, then the bundle should also include a pagination token so that the client can request more child events. -Main use cases for ``in_reply_to``: +Main use cases for ``relates_to``: - Comments on a message. - Non-local delivery/read receipts : If doing separate receipt events for each message. - Meeting invite responses : Yes/No/Maybe for a meeting. @@ -405,10 +434,10 @@ TODO: this thread ID exposed through federation? e.g. can a HS retrieve all events for a given thread ID from another HS? -Example using ``updates`` and ``in_reply_to`` +Example using ``updates`` and ``relates_to`` --------------------------------------------- - Room with a single message. -- 10 comments are added to the message via ``in_reply_to``. +- 10 comments are added to the message via ``relates_to``. - An edit is made to the original message via ``updates``. - An initial sync on this room with a limit of 3 comments, would return the message with the update event bundled with it and the most recent 3 comments and a pagination token to request earlier comments @@ -418,16 +447,17 @@ Example using ``updates`` and ``in_reply_to`` { content: { body: "I am teh winner!" }, updated_by: [ - { content: { body: "I am the winner!" } } + { content: { body: "I am the winner!" }, ... } ], replies: { start: "some_token", chunk: [ - { content: { body: "8th comment" } }, - { content: { body: "9th comment" } }, - { content: { body: "10th comment" } } + { content: { body: "8th comment" }, ... }, + { content: { body: "9th comment" }, ... }, + { content: { body: "10th comment" }, ... } ] - } + }, + ... } Events (breaking changes; event version 2) From 14624607bb827c09dc7e6570600a2e86c3674db1 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Tue, 6 Jan 2015 13:23:31 +0000 Subject: [PATCH 23/95] Add summary block with high-level view of the proposed APIs. --- drafts/general_api.rst | 86 +++++++++++++++++++++++++++++++++--------- 1 file changed, 69 insertions(+), 17 deletions(-) diff --git a/drafts/general_api.rst b/drafts/general_api.rst index d7e0115a..b43242bf 100644 --- a/drafts/general_api.rst +++ b/drafts/general_api.rst @@ -1,30 +1,77 @@ Instant Messaging ================= +This contains the formal proposal for Matrix Client-Server API v2. This API would +completely replace v1. It is a general API, not specific to any particular +protocol e.g. HTTP. It contains the following APIs: + +- Filtering API +- Global initial sync API +- Event stream API +- Room creation API +- Room joining API +- Scrollback API +- Contextual windowing API +- Action APIs: + - Inviting + - Rejecting invites + - Leaving + - Kicking + - Banning + - Sending message events + - Sending state events + - Deleting state events +- Presence API +- Typing API +- Capabilities API +- ``TODO`` Room Directory API +- ``TODO`` Public room list API +- ``TODO`` User Profile API + +The following APIs will remain unchanged from v1: + +- Registration API +- Login API +- Content repository API + +It also contains information on changes to events, including: + +- Relates to +- Updates +- State key restrictions +- Event type rule setting + +Notes +----- TODO ----- -- Pagination: Would be nice to have "and X more". It will probably be Google-style estimates given - we can't know the exact number over federation, but as a purely informational display thing it would - be nice. -- HTTP Ordering: Blocking requests with higher seqnums is troublesome if there is a max # of concurrent - connections a client can have open. -- Session expiry: Do we really have to fonx the request if it was done with an old session ID? +~~~~ +- Pagination: Would be nice to have "and X more". It will probably be + Google-style estimates given we can't know the exact number over federation, + but as a purely informational display thing it would be nice. +- HTTP Ordering: Blocking requests with higher seqnums is troublesome if there + is a max # of concurrent connections a client can have open. +- Session expiry: Do we really have to fonx the request if it was done with an + old session ID? - Server capabilities: Keep hashing step for consistency or not? Extra request. -- Client capabilities: List of hashes f.e device vs union of hashes on all devices? -- Client capabilities: Clients which are offline but can be pushed should have their capabilities visible. +- Client capabilities: List of hashes f.e device vs union of hashes on all + devices? +- Client capabilities: Clients which are offline but can be pushed should have + their capabilities visible. How to manage unregistering them e.g. if they uninstall the app? - What do server-generated events look like? - What do read-up-to markers look like? - Offline mode? How does that work with sessions? - Per device presence - Receiving events for unknown rooms. How do you handle this? -- Linking the termination of typing events to the message itself, so you don't need to send +- Linking the termination of typing events to the message itself, so you don't + need to send two events and don't get flicker. -- Filtering: Blacklist (negative) filters? E.g. "don't give me ``event.type.here`` events +- Filtering: Blacklist (negative) filters? E.g. "don't give me + ``event.type.here`` events - Public room list API -Summary -------- +Summary of changes from v1 +~~~~~~~~~~~~~~~~~~~~~~~~~~ Included: - Event filtering (type/room/users, federation-style events) - Incremental syncing @@ -44,7 +91,8 @@ Excluded: - PATCHing power levels - Handling "duplicate" events in state/messages key on initial sync. - Multiple devices (other than VoIP) - - Room directory lists (aka public room list, paginating, permissions on editing the list, etc) + - Room directory lists (aka public room list, paginating, permissions on + editing the list, etc) Filter API ---------- @@ -201,10 +249,14 @@ Outputs: What data flows does it address: - Chat Screen: Scrolling back (infinite scrolling) -Contextual messages -------------------- +Contextual windowing +-------------------- +This refers to showing a "window" of message events around a given message +event. The window provides the "context" for the given message event. + Inputs: - - Event ID of the message to get the surrounding context for (this specifies the room to get messages in). + - Event ID of the message to get the surrounding context for (this specifies + the room to get messages in). - Number of messages before/after this message to obtain. - Filter to apply. Outputs: From 494a691a8cac2b85e38e79dcac5a4815fdbdb3ac Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Tue, 6 Jan 2015 14:27:44 +0000 Subject: [PATCH 24/95] Limit 80; fix RST formatting. --- drafts/general_api.rst | 415 +++++++++++++++++++++++------------------ 1 file changed, 233 insertions(+), 182 deletions(-) diff --git a/drafts/general_api.rst b/drafts/general_api.rst index b43242bf..3898038e 100644 --- a/drafts/general_api.rst +++ b/drafts/general_api.rst @@ -1,7 +1,7 @@ Instant Messaging ================= -This contains the formal proposal for Matrix Client-Server API v2. This API would -completely replace v1. It is a general API, not specific to any particular +This contains the formal proposal for Matrix Client-Server API v2. This API +would completely replace v1. It is a general API, not specific to any particular protocol e.g. HTTP. It contains the following APIs: - Filtering API @@ -104,25 +104,30 @@ Inputs: - Whether you want coalesced ``updates`` events - Whether you want coalesced ``relates_to`` events (and the max # to coalesce) - limit= param? - - Which keys to return for events? e.g. no ``origin_server_ts`` if you don't show timestamps + - Which keys to return for events? e.g. no ``origin_server_ts`` if you don't + show timestamps Outputs: - An opaque token which represents the inputs Notes: - The token may expire, in which case you would need to request another one. - - The token could be as simple as a concatenation of the requested filters with a delimiter between them. + - The token could be as simple as a concatenation of the requested filters with + a delimiter between them. - Omitting the token on APIs results in ALL THE THINGS coming down. - Clients should remember which token they need to use for which API. - - HTTP note: If the filter API is a separate endpoint, then you could easily allow APIs which use filtering - to ALSO specifiy query parameters to tweak the filter. + - HTTP note: If the filter API is a separate endpoint, then you could easily + allow APIs which use filtering to ALSO specifiy query parameters to tweak the + filter. TODO: - - Do we want to specify negative filters (e.g. don't give me ``event.type.here`` events) + - Do we want to specify negative filters (e.g. don't give me + ``event.type.here`` events) Global ``/initialSync`` API --------------------------- Inputs: - A way of identifying the user (e.g. access token, user ID, etc) - Streaming token (optional) - - Which state event types to return (e.g. ``m.room.name`` / ``m.room.topic`` / ``m.room.aliases``) + - Which state event types to return (e.g. ``m.room.name`` / ``m.room.topic`` + / ``m.room.aliases``) - Filter to apply Outputs: - For each room the user is joined: @@ -131,56 +136,66 @@ Outputs: - max of limit= message events - room ID Notes: - - If a streaming token is applied, you will get a delta rather than all the rooms. + - If a streaming token is applied, you will get a delta rather than all the + rooms. What data flows does it address: - Home screen: data required on load. TODO: - - Will need some form of state event pagination like we have for message events to handle large - amounts of state events for a room. Need to think of the consequences of this: you may not get a - ``m.room.member`` for someone's message and so cannot display their display name / avatar. - Do we want to provide pagination on an event type basis? + - Will need some form of state event pagination like we have for message events + to handle large amounts of state events for a room. Need to think of the + consequences of this: you may not get a ``m.room.member`` for someone's + message and so cannot display their display name / avatar. Do we want to + provide pagination on an event type basis? - Handle paginating initial sync results themselves (e.g. 10 most recent rooms) - - No need for state events under the 'state' key to have a ``prev_content``. Can also apply some - optimisations depending on the direction of travel when scrolling back. + - No need for state events under the 'state' key to have a ``prev_content``. + Can also apply some optimisations depending on the direction of travel when + scrolling back. Event Stream API ---------------- Inputs: - Position in the stream - - Filter to apply: which event types, which room IDs, whether to get out-of-order events, which users - to get presence/profile updates for + - Filter to apply: which event types, which room IDs, whether to get + out-of-order events, which users to get presence/profile updates for - User ID - Device ID Outputs: - - 0-N events the client hasn't seen. NB: Deleted state events will be missing a ``content`` key. Deleted - message events are ``m.room.redaction`` events. + - 0-N events the client hasn't seen. NB: Deleted state events will be missing a + ``content`` key. Deleted message events are ``m.room.redaction`` events. - New position in the stream. State Events Ordering Notes: - - Home servers may receive state events over federation that are superceded by state events previously - sent to the client. The home server *cannot* send these events to the client else they would end up - erroneously clobbering the superceding state event. - - As a result, the home server reserves the right to omit sending state events which are known to be - superceded already. - - This may result in missed *state* events. However, the state of the room will always be eventually - consistent. + - Home servers may receive state events over federation that are superceded by + state events previously sent to the client. The home server *cannot* send + these events to the client else they would end up erroneously clobbering the + superceding state event. + - As a result, the home server reserves the right to omit sending state events + which are known to be superceded already. + - This may result in missed *state* events. However, the state of the room will + always be eventually consistent. Message Events Ordering Notes: - - Home servers may receive message events over federation that happened a long time ago. The client - may or may not be interested in these message events. - - For clients which do not store scrollback for a room (they discard events after processing them), - this is not a problem as they only care about the recent messages. - - For clients which do persist scrollback for a room, they need to know about the message event and - where to insert it so that scrollback remains consistent and doesn't omit messages. - - Clients can specify an input parameter stating that they wish to receive these out-of-order events. - - The event, when it comes down the stream, will indicate which event it comes after. + - Home servers may receive message events over federation that happened a long + time ago. The client may or may not be interested in these message events. + - For clients which do not store scrollback for a room (they discard events + after processing them), this is not a problem as they only care about the + recent messages. + - For clients which do persist scrollback for a room, they need to know about + the message event and where to insert it so that scrollback remains + consistent and doesn't omit messages. + - Clients can specify an input parameter stating that they wish to receive + these out-of-order events. + - The event, when it comes down the stream, will indicate which event it comes + after. Rejected events: - - A home server may find out via federation that it should not have accepted an event (e.g. to send a - message/state event in a room). - - If this happens, the home server will send a ``m.room.redaction`` for the event in question. This will - be a local server event (not shared with other servers). - - If the event was a state event, it will synthesise a new state event to correct the client's room state. - This will be a local server event (not shared with other servers). + - A home server may find out via federation that it should not have accepted + an event (e.g. to send a message/state event in a room). + - If this happens, the home server will send a ``m.room.redaction`` for the + event in question. This will be a local server event (not shared with other + servers). + - If the event was a state event, it will synthesise a new state event to + correct the client's room state. This will be a local server event (not + shared with other servers). - In practice, clients don't need any extra special handling for this. What data flows does it address: - Home Screen: Data required when new message arrives for a room @@ -195,12 +210,12 @@ What data flows does it address: Room Creation ------------- Inputs: - - Invitee list of user IDs, public/private, state events to set on creation e.g. - name of room, alias of room, topic of room + - Invitee list of user IDs, public/private, state events to set on creation + e.g. name of room, alias of room, topic of room Output: - Room ID Notes: - - This is a special case of joining a room. See the notes on joining a room. + - This is a special case of joining a room. See the notes on joining a room. What data flows does it address: - Home Screen: Creating a room @@ -208,30 +223,34 @@ Joining a room -------------- Inputs: - Room ID (with list of servers to join from) / room alias / invite event ID - - Optional filter (which events to return, whether the returned events should come down - the event stream) + - Optional filter (which events to return, whether the returned events should + come down the event stream) Outputs: - - Room ID, the returned state events from the filter e.g. Room aliases (plural), Name, - topic, member list (f.e. member: user ID, avatar, presence, display name, power level, - whether they are typing), enough messages to fill screen (and whether there are more) + - Room ID, the returned state events from the filter e.g. Room aliases + (plural), Name, topic, member list (f.e. member: user ID, avatar, presence, + display name, power level, whether they are typing), enough messages to fill + screen (and whether there are more) Notes: - - How do you return room information? In response to the join, or from the event stream? - - The events returned need to be filterable. Different clients for the same user may want - different information (e.g. the client performing the join may jump to the chat screen and - therefore want some messages, whereas the client not performing the join just needs to be - aware of the new room). - - As a result, the join response should return events *instead of* to the event stream, depending - on the client. + - How do you return room information? In response to the join, or from the + event stream? + - The events returned need to be filterable. Different clients for the same + user may want different information (e.g. the client performing the join may + jump to the chat screen and therefore want some messages, whereas the client + not performing the join just needs to be aware of the new room). + - As a result, the join response should return events *instead of* to the + event stream, depending on the client. Mapping messages to the event stream: - - Once you join a room, you will start getting message events for it. How do you know when - you started getting events for this room? You need to know so you can provide a token when - scrolling back. You cannot currently infer this from the join event itself, as individual - events do not have tokens (only chunks do). - - This token can be provided as a separate server-generated event, or an annotation on the join - event itself. - - We propose that a server-generated event is sent down the event stream to all clients, rather - than annotating the join event. The server-generated event works nicely for Application - Services where an entity subscribes to a room without a join event. + - Once you join a room, you will start getting message events for it. How do + you know when you started getting events for this room? You need to know so + you can provide a token when scrolling back. You cannot currently infer this + from the join event itself, as individual events do not have tokens (only + chunks do). + - This token can be provided as a separate server-generated event, or an + annotation on the join event itself. + - We propose that a server-generated event is sent down the event stream to all + clients, rather than annotating the join event. The server-generated event + works nicely for Application Services where an entity subscribes to a room + without a join event. What data flows does it address: - Home Screen: Joining a room @@ -267,13 +286,13 @@ Outputs: Action APIs ----------- -The following APIs are "action APIs". This is defined to be a request which alters the state of -a room you are already joined to. +The following APIs are "action APIs". This is defined to be a request which +alters the state of a room you are already joined to. -When you perform an action in a room, you immediately want to display the local echo. The client -can receive the response to the action either directly or from the event stream. The order in which -you receive these responses is undefined. As a result, clients MUST be able to handle all possible -orderings:: +When you perform an action in a room, you immediately want to display the local +echo. The client can receive the response to the action either directly or from +the event stream. The order in which you receive these responses is undefined. +As a result, clients MUST be able to handle all possible orderings:: 1 2a 3 START ----> REQUEST SENT ---> RESPONSE TO REQUEST RECEIVED --------> GOT BOTH @@ -283,18 +302,23 @@ orderings:: 1: Can display local echo at this point. 2a: The request has been successfully processed and can be displayed as Sent. - 2b/3: The request has been successfully processed and the client knows its position in the event stream. - -When a client sends a request, they can include an "action ID" so that they can match up the event in -the event stream to the request which they made. This ID is created by the client, and MUST be a -monotonically increasing integer for that client. This ID serves as a transaction ID for idempotency as -well as a sequence ID for ordering actions performed in parallel by that client. Events for actions -performed by a client in that client's event stream will include the action ID the client submitted -when making the request. The action ID will *not* appear in other client's event streams. - -Action IDs are optional and are only needed by clients that retransmit their requests, or display local -echo, or allow the submission of multiple requests in parallel. An example of a client which may not need -the use of action IDs includes bots which operate using basic request/responses in a synchronous fashion. + 2b/3: The request has been successfully processed and the client knows its + position in the event stream. + +When a client sends a request, they can include an "action ID" so that they can +match up the event in the event stream to the request which they made. This ID +is created by the client, and MUST be a monotonically increasing integer for +that client. This ID serves as a transaction ID for idempotency as well as a +sequence ID for ordering actions performed in parallel by that client. Events +for actions performed by a client in that client's event stream will include the +action ID the client submitted when making the request. The action ID will *not* +appear in other client's event streams. + +Action IDs are optional and are only needed by clients that retransmit their +requests, or display local echo, or allow the submission of multiple requests +in parallel. An example of a client which may not need the use of action IDs +includes bots which operate using basic request/responses in a synchronous +fashion. Inviting a user ~~~~~~~~~~~~~~~ @@ -314,9 +338,10 @@ Inputs: Outputs: - None. Notes: - - Giving the event ID rather than user ID/room ID combo because mutliple users can invite the - same user into the same room. - - Rejecting an invite results in the ``m.room.member`` state event being DELETEd for that user. + - Giving the event ID rather than user ID/room ID combo because mutliple users + can invite the same user into the same room. + - Rejecting an invite results in the ``m.room.member`` state event being + DELETEd for that user. Deleting a state event ~~~~~~~~~~~~~~~~~~~~~~ @@ -327,8 +352,8 @@ Inputs: Outputs: - None. Notes: - - This is represented on the event stream as an event lacking a ``content`` key (for symmetry - with ``prev_content``) + - This is represented on the event stream as an event lacking a ``content`` + key (for symmetry with ``prev_content``) Kicking a user ~~~~~~~~~~~~~~ @@ -364,32 +389,37 @@ Outputs: What data flows does it address: - Chat Screen: Send a Message Ordering notes: - - HTTP: When sending a message with a higher seqnum, it will block the request until it receives - earlier seqnums. The block will expire after a timeout and reject the message stating that it - was missing a seqnum. + - HTTP: When sending a message with a higher seqnum, it will block the request + until it receives earlier seqnums. The block will expire after a timeout and + reject the message stating that it was missing a seqnum. E2E Notes: - - For signing: You send the original message to the HS and it will return the full event JSON which will - be sent. This full event is then signed and sent to the HS again to send the message. + - For signing: You send the original message to the HS and it will return the + full event JSON which will be sent. This full event is then signed and sent + to the HS again to send the message. Sessions -------- -A session is a group of requests sent within a short amount of time by the same client. -Sessions time out after a short amount of time without any requests. Starting -a session is known as going "online". Its purpose is to wrap up the expiry of presence and -typing notifications into a clearer scope. A session starts when the client makes any request. -A session ends when the client doesn't make a request for a particular amount of time (times out). -A session can also end when explicitly hitting a particular endpoint. This is known as going "offline". - -When a session starts, a session ID is sent in response to the first request the client makes. This -session ID should be sent in *all* subsequent requests. If the server expires a session and the client -uses an old session ID, the server should fail the request with the old session ID and send a new -session ID in response for the client to use. If the client receives a new session ID mid-session, -it must re-establish its typing status and presence status, as they are linked to the session ID. +A session is a group of requests sent within a short amount of time by the same +client. Sessions time out after a short amount of time without any requests. +Starting a session is known as going "online". Its purpose is to wrap up the +expiry of presence and typing notifications into a clearer scope. A session +starts when the client makes any request. A session ends when the client doesn't +make a request for a particular amount of time (times out). A session can also +end when explicitly hitting a particular endpoint. This is known as going +"offline". + +When a session starts, a session ID is sent in response to the first request the +client makes. This session ID should be sent in *all* subsequent requests. If +the server expires a session and the client uses an old session ID, the server +should fail the request with the old session ID and send a new session ID in +response for the client to use. If the client receives a new session ID +mid-session, it must re-establish its typing status and presence status, as they +are linked to the session ID. Presence ~~~~~~~~ -When a session starts, the home server can treat the user as "online". When the session ends, the home -server can treat the user as "offline". +When a session starts, the home server can treat the user as "online". When the +session ends, the home server can treat the user as "offline". Inputs: - Presence state (online, offline, away, busy, do not disturb, etc) @@ -401,9 +431,9 @@ Notes: Typing ~~~~~~ -When in a session, a user can send a request stating that they are typing in a room. They are no longer -typing when either the session ends or they explicitly send another request to say they are no longer -typing. +When in a session, a user can send a request stating that they are typing in a +room. They are no longer typing when either the session ends or they explicitly +send another request to say they are no longer typing. Inputs: - Room ID @@ -415,84 +445,96 @@ Notes: Action IDs ~~~~~~~~~~ -Action IDs are scoped per session. The first action ID for a session should be 0. For each subsequent -action request, the ID should be incremented by 1. It should be reset to 0 when a new session starts. +Action IDs are scoped per session. The first action ID for a session should be +0. For each subsequent action request, the ID should be incremented by 1. It +should be reset to 0 when a new session starts. -If the client sends an action request with a stale session ID, the home server MUST fail the request -and start a new session. The request needs to be failed in order to avoid edge cases with incrementing -action IDs. +If the client sends an action request with a stale session ID, the home server +MUST fail the request and start a new session. The request needs to be failed +in order to avoid edge cases with incrementing action IDs. Updates (Events) ---------------- -Events may update other events. This is represented by the ``updates`` key. This is a key which -contains the event ID for the event it relates to. Events that relate to other events are referred to -as "Child Events". The event being related to is referred to as "Parent Events". Child events cannot -stand alone as a separate entity; they require the parent event in order to make sense. +Events may update other events. This is represented by the ``updates`` key. This +is a key which contains the event ID for the event it relates to. Events that +relate to other events are referred to as "Child Events". The event being +related to is referred to as "Parent Events". Child events cannot stand alone as +a separate entity; they require the parent event in order to make sense. Bundling ~~~~~~~~ -Events that relate to another event should come down inside that event. That is, the top-level event -should come down with all the child events at the same time. This is called a "bundle" and it is -represented as an array of events inside the top-level event.There are some issues with this however: - -- Scrollback: Should you be told about child events for which you do not know the parent event? - Conclusion: No you shouldn't be told about child events. You will receive them when you scroll back - to the parent event. -- Pagination of child events: You don't necessarily want to have 1000000s of child events with the - parent event. We can't reasonably paginate child events because we require all the child events - in order to display the event correctly. Comments on a message should be done via another technique, - such as ``relates_to`. -- Do you allow child events to relate to other child events? There is no technical reason why we - cannot nest child events, however we can't think of any use cases for it. The behaviour would be - to get the child events recursively from the top-level event. +Events that relate to another event should come down inside that event. That is, +the top-level event should come down with all the child events at the same time. +This is called a "bundle" and it is represented as an array of events inside the +top-level event.There are some issues with this however: + +- Scrollback: Should you be told about child events for which you do not know + the parent event? Conclusion: No you shouldn't be told about child events. + You will receive them when you scroll back to the parent event. +- Pagination of child events: You don't necessarily want to have 1000000s of + child events with the parent event. We can't reasonably paginate child events + because we require all the child events in order to display the event + correctly. Comments on a message should be done via another technique, + such as ``relates_to``. +- Do you allow child events to relate to other child events? There is no + technical reason why we cannot nest child events, however we can't think of + any use cases for it. The behaviour would be to get the child events + recursively from the top-level event. Main use cases for ``updates``: - - Call signalling (child events are ICE candidates, answer to the offer, and termination) - - *Local* Delivery/Read receipts : "Local" means they are not shared with other users on the same home - server or via federation but *are* shared between clients for the same user; useful for push - notifications, read count markers, etc. This is done to avoid the ``n^2`` problem for sending - receipts, where the vast majority of traffic tends towards sending more receipts. + - Call signalling (child events are ICE candidates, answer to the offer, and + termination) + - *Local* Delivery/Read receipts : "Local" means they are not shared with other + users on the same home server or via federation but *are* shared between + clients for the same user; useful for push notifications, read count markers, + etc. This is done to avoid the ``n^2`` problem for sending receipts, where + the vast majority of traffic tends towards sending more receipts. - s/foo/bar/ style message edits -Clients *always* need to know how to apply the deltas because clients may receive the events separately -down the event stream. Combining event updates server-side does not make client implementation simpler, -as the client still needs to know how to combine the events. +Clients *always* need to know how to apply the deltas because clients may +receive the events separately down the event stream. Combining event updates +server-side does not make client implementation simpler, as the client still +needs to know how to combine the events. Relates to (Events) ------------------- -Events may be in response to other events, e.g. comments. This is represented by the ``relates_to`` -key. This differs from the ``updates`` key as they *do not update the event itself*, and are *not required* -in order to display the parent event. Crucially, the child events can be paginated, whereas ``updates`` child events cannot -be paginated. +Events may be in response to other events, e.g. comments. This is represented +by the ``relates_to`` key. This differs from the ``updates`` key as they *do +not update the event itself*, and are *not required* in order to display the +parent event. Crucially, the child events can be paginated, whereas ``updates`` +child events cannot be paginated. Bundling ~~~~~~~~ -Child events can be optionally bundled with the parent event, depending on your display mechanism. The -number of child events which can be bundled should be limited to prevent events becoming too large. This -limit should be set by the client. If the limit is exceeded, then the bundle should also include a pagination -token so that the client can request more child events. +Child events can be optionally bundled with the parent event, depending on your +display mechanism. The number of child events which can be bundled should be +limited to prevent events becoming too large. This limit should be set by the +client. If the limit is exceeded, then the bundle should also include a +pagination token so that the client can request more child events. Main use cases for ``relates_to``: - Comments on a message. - - Non-local delivery/read receipts : If doing separate receipt events for each message. + - Non-local delivery/read receipts : If doing separate receipt events for each + message. - Meeting invite responses : Yes/No/Maybe for a meeting. -Like with ``updates``, clients need to know how to apply the deltas because clients may receive the -events separately down the event stream. +Like with ``updates``, clients need to know how to apply the deltas because +clients may receive the events separately down the event stream. TODO: - Can a child event reply to multiple parent events? Use case? - - Should a parent event and its children share a thread ID? Does the originating HS set this ID? Is - this thread ID exposed through federation? e.g. can a HS retrieve all events for a given thread ID from - another HS? + - Should a parent event and its children share a thread ID? Does the + originating HS set this ID? Is this thread ID exposed through federation? + e.g. can a HS retrieve all events for a given thread ID from another HS? Example using ``updates`` and ``relates_to`` --------------------------------------------- - Room with a single message. - 10 comments are added to the message via ``relates_to``. - An edit is made to the original message via ``updates``. -- An initial sync on this room with a limit of 3 comments, would return the message with the update - event bundled with it and the most recent 3 comments and a pagination token to request earlier comments +- An initial sync on this room with a limit of 3 comments, would return the + message with the update event bundled with it and the most recent 3 comments + and a pagination token to request earlier comments .. code :: javascript @@ -514,24 +556,28 @@ Example using ``updates`` and ``relates_to`` Events (breaking changes; event version 2) ------------------------------------------ -- Prefix the event ``type`` to say if it is a state event, message event or ephemeral event. Needed - because you can't tell the different between message events and ephemeral ROOM events (e.g. typing). -- State keys need additional restrictions in order to increase flexibility on state event permissions. - State keys prefixed with an ``_`` have no specific restrictions. 0-length state keys are now represented - by just a single ``_``. State keys prefixed with ``@`` can be modified only by the named user ID *OR* the - room ops. They can have an optional path suffixed to it. State keys that start with a server name can only - be modified by that server name (e.g. ``some.server.com/some/path`` can only be modified by - ``some.server.com``). -- Do we want to specify what restrictions apply to the state key in the event type? This would allow HSes - to enforce this, making life easier for clients when dealing with custom event types. E.g. ``_custom.event`` - would allow anything in the state key, ``_@custom.event`` would only allow user IDs in the state key, etc. +- Prefix the event ``type`` to say if it is a state event, message event or + ephemeral event. Needed because you can't tell the different between message + events and ephemeral ROOM events (e.g. typing). +- State keys need additional restrictions in order to increase flexibility on + state event permissions. State keys prefixed with an ``_`` have no specific + restrictions. 0-length state keys are now represented by just a single ``_``. + State keys prefixed with ``@`` can be modified only by the named user ID *OR* + the room ops. They can have an optional path suffixed to it. State keys that + start with a server name can only be modified by that server name (e.g. + ``some.server.com/some/path`` can only be modified by ``some.server.com``). +- Do we want to specify what restrictions apply to the state key in the event + type? This would allow HSes to enforce this, making life easier for clients + when dealing with custom event types. E.g. ``_custom.event`` would allow + anything in the state key, ``_@custom.event`` would only allow user IDs in + the state key, etc. - s/user_id/sender/g given that home servers can send events, not just users. Capabilities ------------ -How does a client know if the server it is using supports a content repository? How does a client know -if another client has VoIP support? This section outlines capability publishing for servers, -clients and federation. +How does a client know if the server it is using supports a content repository? +How does a client know if another client has VoIP support? This section outlines +capability publishing for servers, clients and federation. Server ~~~~~~ @@ -562,16 +608,19 @@ Client ~~~~~~ - e.g. Whether this client supports VoIP -When a session is started, the client needs to provide a capability set. The server will take the "union" -of all the user's connected clients' capability sets and send the hash of the capabilities as part of -presence information (not necesarily as a ``m.presence`` event, but it should act like presence events). +When a session is started, the client needs to provide a capability set. The +server will take the "union" of all the user's connected clients' capability +sets and send the hash of the capabilities as part of presence information +(not necesarily as a ``m.presence`` event, but it should act like presence +events). -On first signup, the client will attempt to send the hash and be most likely refused by the home server as -it does not know the full capability set for that hash. The client will then have to upload the full capability -set to the home server. The client will then be able to send the hash as normal. +On first signup, the client will attempt to send the hash and be most likely +refused by the home server as it does not know the full capability set for that +hash. The client will then have to upload the full capability set to the home +server. The client will then be able to send the hash as normal. -When a client receives a hash, the client will either recognise the hash or will have to request the capability -set from their home server: +When a client receives a hash, the client will either recognise the hash or +will have to request the capability set from their home server: Inputs: - Hash @@ -586,16 +635,18 @@ Federation VoIP ---- -This addresses one-to-one calling with multiple devices. This uses the ``updates`` key to -handle signalling. +This addresses one-to-one calling with multiple devices. This uses the +``updates`` key to handle signalling. Event updates ~~~~~~~~~~~~~ - Call is placed by caller. Event generated with offer. - 1-N callees may pick up or reject this offer. - Callees update the event (with sdp answer if they are accepting the call) -- Caller acknowledges *one* of the callees (either one which picked up or rejected) by updating the event. -- Callees who weren't chosen then give up (Answered elsewhere, Rejected elsewhere, etc) +- Caller acknowledges *one* of the callees (either one which picked up or + rejected) by updating the event. +- Callees who weren't chosen then give up (Answered elsewhere, Rejected + elsewhere, etc) - Update with ICE candidates as they appear. - ... in call ... - Send hangup update when hanging up. From 760fc2e15bb78266922e5969956da00b41051656 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Tue, 6 Jan 2015 15:00:28 +0000 Subject: [PATCH 25/95] Refactor document to be clearer on where the APIs are. Mark each one as either TODO, ONGOING, Draft or Final. --- drafts/general_api.rst | 192 ++++++++++++++++++++++++++++------------- 1 file changed, 133 insertions(+), 59 deletions(-) diff --git a/drafts/general_api.rst b/drafts/general_api.rst index 3898038e..2cb0bf63 100644 --- a/drafts/general_api.rst +++ b/drafts/general_api.rst @@ -1,31 +1,40 @@ Instant Messaging ================= + +Legend: + - ``TODO``: API is not in this document yet. + - ``ONGOING``: API is proposed but needs more work. There are known issues to be + addressed. + - ``Draft``: API is proposed and has no outstanding issues to be addressed, but + needs more feedback. + - ``Final``: The API has no outstanding issues. + This contains the formal proposal for Matrix Client-Server API v2. This API would completely replace v1. It is a general API, not specific to any particular protocol e.g. HTTP. It contains the following APIs: -- Filtering API -- Global initial sync API -- Event stream API -- Room creation API -- Room joining API -- Scrollback API -- Contextual windowing API +- Filtering API ``ONGOING`` +- Global initial sync API ``ONGOING`` +- Event stream API ``Draft`` +- Room creation API ``Draft`` +- Room joining API ``Draft`` +- Scrollback API ``Draft`` +- Contextual windowing API ``Draft`` - Action APIs: - - Inviting - - Rejecting invites - - Leaving - - Kicking - - Banning - - Sending message events - - Sending state events - - Deleting state events -- Presence API -- Typing API -- Capabilities API -- ``TODO`` Room Directory API -- ``TODO`` Public room list API -- ``TODO`` User Profile API + - Inviting ``Final`` + - Rejecting invites ``Final`` + - Leaving ``Final`` + - Kicking ``Final`` + - Banning ``Final`` + - Sending message events ``ONGOING`` + - Sending state events ``Final`` + - Deleting state events ``Draft`` +- Presence API ``ONGOING`` +- Typing API ``ONGOING`` +- Capabilities API ``ONGOING`` +- Room Directory API ``TODO`` +- Public room list API ``TODO`` +- User Profile API ``TODO`` The following APIs will remain unchanged from v1: @@ -35,40 +44,20 @@ The following APIs will remain unchanged from v1: It also contains information on changes to events, including: -- Relates to -- Updates -- State key restrictions -- Event type rule setting +- Action IDs ``ONGOING`` +- Sessions ``ONGOING`` +- Relates to ``Draft`` +- Updates ``Draft`` +- State key restrictions ``Draft`` +- Event type rule setting ``Draft`` Notes ----- TODO ~~~~ -- Pagination: Would be nice to have "and X more". It will probably be - Google-style estimates given we can't know the exact number over federation, - but as a purely informational display thing it would be nice. -- HTTP Ordering: Blocking requests with higher seqnums is troublesome if there - is a max # of concurrent connections a client can have open. -- Session expiry: Do we really have to fonx the request if it was done with an - old session ID? -- Server capabilities: Keep hashing step for consistency or not? Extra request. -- Client capabilities: List of hashes f.e device vs union of hashes on all - devices? -- Client capabilities: Clients which are offline but can be pushed should have - their capabilities visible. - How to manage unregistering them e.g. if they uninstall the app? -- What do server-generated events look like? - What do read-up-to markers look like? -- Offline mode? How does that work with sessions? -- Per device presence - Receiving events for unknown rooms. How do you handle this? -- Linking the termination of typing events to the message itself, so you don't - need to send - two events and don't get flicker. -- Filtering: Blacklist (negative) filters? E.g. "don't give me - ``event.type.here`` events -- Public room list API Summary of changes from v1 ~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -96,6 +85,9 @@ Excluded: Filter API ---------- +``ONGOING`` : Exactly what can be filtered? Which APIs use this? Are we +conflating too much? + Inputs: - Which event types (incl wildcards) - Which room IDs @@ -123,6 +115,8 @@ TODO: Global ``/initialSync`` API --------------------------- +``ONGOING`` : See TODO section. + Inputs: - A way of identifying the user (e.g. access token, user ID, etc) - Streaming token (optional) @@ -209,6 +203,8 @@ What data flows does it address: Room Creation ------------- +``Draft`` + Inputs: - Invitee list of user IDs, public/private, state events to set on creation e.g. name of room, alias of room, topic of room @@ -221,6 +217,8 @@ What data flows does it address: Joining a room -------------- +``Draft`` + Inputs: - Room ID (with list of servers to join from) / room alias / invite event ID - Optional filter (which events to return, whether the returned events should @@ -251,11 +249,21 @@ Mapping messages to the event stream: clients, rather than annotating the join event. The server-generated event works nicely for Application Services where an entity subscribes to a room without a join event. + - This will look like an event for the room, but have a special + "server-generated" event type e.g. ``m.homeserver.scrollback`` with a + ``token`` containing the start token for the room. What data flows does it address: - Home Screen: Joining a room Scrolling back (infinite scrolling) ----------------------------------- +``Draft`` + +.. NOTE:: + - Pagination: Would be nice to have "and X more". It will probably be + Google-style estimates given we can't know the exact number over federation, + but as a purely informational display thing it would be nice. + Inputs: - Identifier for the earliest event - # requested events @@ -270,6 +278,8 @@ What data flows does it address: Contextual windowing -------------------- +``Draft`` + This refers to showing a "window" of message events around a given message event. The window provides the "context" for the given message event. @@ -322,6 +332,8 @@ fashion. Inviting a user ~~~~~~~~~~~~~~~ +``Final`` + Inputs: - User ID - Room ID @@ -333,6 +345,8 @@ What data flows does it address: Rejecting an invite ~~~~~~~~~~~~~~~~~~~ +``Final`` + Inputs: - Event ID (to know which invite you're rejecting) Outputs: @@ -343,8 +357,22 @@ Notes: - Rejecting an invite results in the ``m.room.member`` state event being DELETEd for that user. -Deleting a state event -~~~~~~~~~~~~~~~~~~~~~~ +Sending state events +~~~~~~~~~~~~~~~~~~~~ +``Final`` + +Inputs: + - Event type + - State key + - Room ID + - Content +Outputs: + - None. + +Deleting state events +~~~~~~~~~~~~~~~~~~~~~ +``Draft`` + Inputs: - Event type - State key @@ -357,6 +385,8 @@ Notes: Kicking a user ~~~~~~~~~~~~~~ +``Final`` + Inputs: - User ID - Room ID @@ -368,6 +398,8 @@ What data flows does it address: Leaving a room ~~~~~~~~~~~~~~ +``Final`` + Inputs: - Room ID - A way of identifying the user (user ID, access token) @@ -379,6 +411,8 @@ What data flows does it address: Send a message ~~~~~~~~~~~~~~ +``ONGOING`` : Semantics for HTTP ordering. + Inputs: - Room ID - Message contents @@ -399,6 +433,11 @@ E2E Notes: Sessions -------- +``ONGOING`` + +.. NOTE:: + - Offline mode? How does that work with sessions? + A session is a group of requests sent within a short amount of time by the same client. Sessions time out after a short amount of time without any requests. Starting a session is known as going "online". Its purpose is to wrap up the @@ -416,8 +455,14 @@ response for the client to use. If the client receives a new session ID mid-session, it must re-establish its typing status and presence status, as they are linked to the session ID. -Presence -~~~~~~~~ +Presence API +------------ +``ONGOING`` + +.. NOTE:: + - Per device presence + - Presence lists / roster? + When a session starts, the home server can treat the user as "online". When the session ends, the home server can treat the user as "offline". @@ -425,12 +470,16 @@ Inputs: - Presence state (online, offline, away, busy, do not disturb, etc) Outputs: - None. -Notes: - - TODO: Handle multiple devices. -Typing -~~~~~~ +Typing API +---------- +``ONGOING`` + +.. NOTE:: + - Linking the termination of typing events to the message itself, so you don't + need to send two events and don't get flicker. + When in a session, a user can send a request stating that they are typing in a room. They are no longer typing when either the session ends or they explicitly send another request to say they are no longer typing. @@ -444,7 +493,15 @@ Notes: - Typing will time out when the session ends. Action IDs -~~~~~~~~~~ +---------- +``ONGOING`` + +.. NOTE:: + - HTTP Ordering: Blocking requests with higher seqnums is troublesome if there + is a max # of concurrent connections a client can have open. + - Session expiry: Do we really have to fonx the request if it was done with an + old session ID? + Action IDs are scoped per session. The first action ID for a session should be 0. For each subsequent action request, the ID should be incremented by 1. It should be reset to 0 when a new session starts. @@ -455,6 +512,8 @@ in order to avoid edge cases with incrementing action IDs. Updates (Events) ---------------- +``Draft`` + Events may update other events. This is represented by the ``updates`` key. This is a key which contains the event ID for the event it relates to. Events that relate to other events are referred to as "Child Events". The event being @@ -498,6 +557,8 @@ needs to know how to combine the events. Relates to (Events) ------------------- +``Draft`` + Events may be in response to other events, e.g. comments. This is represented by the ``relates_to`` key. This differs from the ``updates`` key as they *do not update the event itself*, and are *not required* in order to display the @@ -528,7 +589,7 @@ TODO: e.g. can a HS retrieve all events for a given thread ID from another HS? Example using ``updates`` and ``relates_to`` ---------------------------------------------- +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - Room with a single message. - 10 comments are added to the message via ``relates_to``. - An edit is made to the original message via ``updates``. @@ -556,6 +617,8 @@ Example using ``updates`` and ``relates_to`` Events (breaking changes; event version 2) ------------------------------------------ +``Draft`` + - Prefix the event ``type`` to say if it is a state event, message event or ephemeral event. Needed because you can't tell the different between message events and ephemeral ROOM events (e.g. typing). @@ -573,8 +636,19 @@ Events (breaking changes; event version 2) the state key, etc. - s/user_id/sender/g given that home servers can send events, not just users. -Capabilities ------------- +Capabilities API +---------------- +``ONGOING`` + +.. NOTE:: + - Server capabilities: Keep hashing step for consistency or not? Extra request. + - Client capabilities: List of hashes f.e device vs union of hashes on all + devices? + - Client capabilities: Clients which are offline but can be pushed should have + their capabilities visible. How to manage unregistering them e.g. if they + uninstall the app? + + How does a client know if the server it is using supports a content repository? How does a client know if another client has VoIP support? This section outlines capability publishing for servers, clients and federation. From 1e27cddf95cdcf7fbfaffc3776776cb5f27ef14a Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Tue, 6 Jan 2015 15:04:08 +0000 Subject: [PATCH 26/95] Missed a marker --- drafts/general_api.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drafts/general_api.rst b/drafts/general_api.rst index 2cb0bf63..f3ca4508 100644 --- a/drafts/general_api.rst +++ b/drafts/general_api.rst @@ -149,6 +149,8 @@ TODO: Event Stream API ---------------- +``Draft`` + Inputs: - Position in the stream - Filter to apply: which event types, which room IDs, whether to get From a01dd787c50d55370460872edddcefeaa26968c5 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Tue, 6 Jan 2015 15:49:52 +0000 Subject: [PATCH 27/95] Add read-up-to markers and clear up the remaining TODO notes. --- drafts/general_api.rst | 48 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 43 insertions(+), 5 deletions(-) diff --git a/drafts/general_api.rst b/drafts/general_api.rst index f3ca4508..c6352633 100644 --- a/drafts/general_api.rst +++ b/drafts/general_api.rst @@ -29,6 +29,7 @@ protocol e.g. HTTP. It contains the following APIs: - Sending message events ``ONGOING`` - Sending state events ``Final`` - Deleting state events ``Draft`` + - Read-up-to markers ``Draft`` - Presence API ``ONGOING`` - Typing API ``ONGOING`` - Capabilities API ``ONGOING`` @@ -53,11 +54,6 @@ It also contains information on changes to events, including: Notes ----- - -TODO -~~~~ -- What do read-up-to markers look like? -- Receiving events for unknown rooms. How do you handle this? Summary of changes from v1 ~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -193,6 +189,13 @@ Rejected events: correct the client's room state. This will be a local server event (not shared with other servers). - In practice, clients don't need any extra special handling for this. +Unknown rooms: + - You could receive events for rooms you are unaware of (e.g. you didn't do an + initial sync, or your HS lost its database and is told from another HS that + they are in this room). How do you handle this? + - The simplest option would be to redo the initial sync with a filter on the + room ID you're unaware of. This would retrieve the room state so you can + display the room. What data flows does it address: - Home Screen: Data required when new message arrives for a room - Home Screen: Data required when someone invites you to a room @@ -384,6 +387,41 @@ Outputs: Notes: - This is represented on the event stream as an event lacking a ``content`` key (for symmetry with ``prev_content``) + +Read-up-to markers +~~~~~~~~~~~~~~~~~~ +``Draft`` + +Inputs: + - State Event type (``m.room.marker.delivered`` and ``m.room.marker.read``) + - Event ID to mark up to. This is inclusive of the event ID specified. +Outputs: + - None. +Efficiency notes: + - Sending "read up to" markers is preferable to sending receipts for every + message due to scaling problems on the client with one receipt per message. + This results in an ever increasing amount of bandwidth being devoted to + receipts and not messages. + - For individual receipts, each person would need to send at least 1 receipt + for every message, which would give a total number of ``msgs * num_people`` + receipts per room. Assuming that people in a room generally converse at say + a rate of 1 message per unit time, this scales ``n^2`` on the number of + people in the room. + - Sending "read up to" markers in contrast allows people to skip some messages + entirely. By making them state events, each user would clobber their own + marker, keeping the scaling at ``n``. For scrollback, the event filter would + NOT want to retrieve these markers as they will be updated frequently. + - This primarily benefits clients when doing an initial sync. Event graphs + will still have a lot of events, most of them from clobbering these state + events. Some gains can be made by skipping receipts, but it is difficult to + judge whether this would be substantial. +Notes: + - What do you do if you get a marker for an event you don't have? Do you fall + back to some kind of ordering heuristic e.g. ``if origin_server_ts > + latest message``. Do you request that event ID directly from the HS? How do + you fit that in to the message thread if you did so? Would probably have to + fall back to the timestamp heuristic. After all, these markers are only ever + going to be heuristics given they are not acknowledging each message event. Kicking a user ~~~~~~~~~~~~~~ From dbedfacfa47402728e86d87f9145d07e88b269cf Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Tue, 6 Jan 2015 16:19:04 +0000 Subject: [PATCH 28/95] Rejig sections. Add a TOC to reduce duplication of API status. --- drafts/general_api.rst | 507 ++++++++++++++++++----------------------- 1 file changed, 226 insertions(+), 281 deletions(-) diff --git a/drafts/general_api.rst b/drafts/general_api.rst index c6352633..787d3804 100644 --- a/drafts/general_api.rst +++ b/drafts/general_api.rst @@ -1,62 +1,11 @@ -Instant Messaging +Table of Contents ================= -Legend: - - ``TODO``: API is not in this document yet. - - ``ONGOING``: API is proposed but needs more work. There are known issues to be - addressed. - - ``Draft``: API is proposed and has no outstanding issues to be addressed, but - needs more feedback. - - ``Final``: The API has no outstanding issues. - -This contains the formal proposal for Matrix Client-Server API v2. This API -would completely replace v1. It is a general API, not specific to any particular -protocol e.g. HTTP. It contains the following APIs: - -- Filtering API ``ONGOING`` -- Global initial sync API ``ONGOING`` -- Event stream API ``Draft`` -- Room creation API ``Draft`` -- Room joining API ``Draft`` -- Scrollback API ``Draft`` -- Contextual windowing API ``Draft`` -- Action APIs: - - Inviting ``Final`` - - Rejecting invites ``Final`` - - Leaving ``Final`` - - Kicking ``Final`` - - Banning ``Final`` - - Sending message events ``ONGOING`` - - Sending state events ``Final`` - - Deleting state events ``Draft`` - - Read-up-to markers ``Draft`` -- Presence API ``ONGOING`` -- Typing API ``ONGOING`` -- Capabilities API ``ONGOING`` -- Room Directory API ``TODO`` -- Public room list API ``TODO`` -- User Profile API ``TODO`` - -The following APIs will remain unchanged from v1: - -- Registration API -- Login API -- Content repository API - -It also contains information on changes to events, including: +.. contents:: Table of Contents +.. sectnum:: -- Action IDs ``ONGOING`` -- Sessions ``ONGOING`` -- Relates to ``Draft`` -- Updates ``Draft`` -- State key restrictions ``Draft`` -- Event type rule setting ``Draft`` - -Notes ------ - Summary of changes from v1 -~~~~~~~~~~~~~~~~~~~~~~~~~~ +========================== Included: - Event filtering (type/room/users, federation-style events) - Incremental syncing @@ -78,11 +27,32 @@ Excluded: - Multiple devices (other than VoIP) - Room directory lists (aka public room list, paginating, permissions on editing the list, etc) + +Version 2 API +============= + +Legend: + - ``[TODO]``: API is not in this document yet. + - ``[ONGOING]``: API is proposed but needs more work. There are known issues to be + addressed. + - ``[Draft]``: API is proposed and has no outstanding issues to be addressed, but + needs more feedback. + - ``[Final]``: The API has no outstanding issues. + +This contains the formal proposal for Matrix Client-Server API v2. This API +would completely replace v1. It is a general API, not specific to any particular +protocol e.g. HTTP. The following APIs will remain unchanged from v1: + +- Registration API +- Login API +- Content repository API + -Filter API ----------- -``ONGOING`` : Exactly what can be filtered? Which APIs use this? Are we -conflating too much? +Filter API ``[ONGOING]`` +------------------------ +.. NOTE:: + Exactly what can be filtered? Which APIs use this? Are we + conflating too much? Inputs: - Which event types (incl wildcards) @@ -109,10 +79,8 @@ TODO: - Do we want to specify negative filters (e.g. don't give me ``event.type.here`` events) -Global ``/initialSync`` API ---------------------------- -``ONGOING`` : See TODO section. - +Global initial sync API ``[ONGOING]`` +------------------------------------- Inputs: - A way of identifying the user (e.g. access token, user ID, etc) - Streaming token (optional) @@ -143,10 +111,8 @@ TODO: scrolling back. -Event Stream API ----------------- -``Draft`` - +Event Stream API ``[Draft]`` +---------------------------- Inputs: - Position in the stream - Filter to apply: which event types, which room IDs, whether to get @@ -206,10 +172,8 @@ What data flows does it address: - Chat Screen: Data required when the room name changes - Chat Screen: Data required when a new message arrives -Room Creation -------------- -``Draft`` - +Room Creation ``[Draft]`` +------------------------- Inputs: - Invitee list of user IDs, public/private, state events to set on creation e.g. name of room, alias of room, topic of room @@ -220,10 +184,8 @@ Notes: What data flows does it address: - Home Screen: Creating a room -Joining a room --------------- -``Draft`` - +Joining a room ``[Draft]`` +-------------------------- Inputs: - Room ID (with list of servers to join from) / room alias / invite event ID - Optional filter (which events to return, whether the returned events should @@ -260,10 +222,8 @@ Mapping messages to the event stream: What data flows does it address: - Home Screen: Joining a room -Scrolling back (infinite scrolling) ------------------------------------ -``Draft`` - +Scrollback API ``[Draft]`` +-------------------------- .. NOTE:: - Pagination: Would be nice to have "and X more". It will probably be Google-style estimates given we can't know the exact number over federation, @@ -281,10 +241,8 @@ Outputs: What data flows does it address: - Chat Screen: Scrolling back (infinite scrolling) -Contextual windowing --------------------- -``Draft`` - +Contextual windowing API ``[Draft]`` +------------------------------------ This refers to showing a "window" of message events around a given message event. The window provides the "context" for the given message event. @@ -335,10 +293,8 @@ in parallel. An example of a client which may not need the use of action IDs includes bots which operate using basic request/responses in a synchronous fashion. -Inviting a user -~~~~~~~~~~~~~~~ -``Final`` - +Inviting a user ``[Final]`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~ Inputs: - User ID - Room ID @@ -348,10 +304,8 @@ Outputs: What data flows does it address: - Chat Screen: Invite a user -Rejecting an invite -~~~~~~~~~~~~~~~~~~~ -``Final`` - +Rejecting an invite ``[Final]`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Inputs: - Event ID (to know which invite you're rejecting) Outputs: @@ -362,10 +316,8 @@ Notes: - Rejecting an invite results in the ``m.room.member`` state event being DELETEd for that user. -Sending state events -~~~~~~~~~~~~~~~~~~~~ -``Final`` - +Sending state events ``[Final]`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Inputs: - Event type - State key @@ -374,10 +326,8 @@ Inputs: Outputs: - None. -Deleting state events -~~~~~~~~~~~~~~~~~~~~~ -``Draft`` - +Deleting state events ``[Draft]`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Inputs: - Event type - State key @@ -388,10 +338,8 @@ Notes: - This is represented on the event stream as an event lacking a ``content`` key (for symmetry with ``prev_content``) -Read-up-to markers -~~~~~~~~~~~~~~~~~~ -``Draft`` - +Read-up-to markers ``[Draft]`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Inputs: - State Event type (``m.room.marker.delivered`` and ``m.room.marker.read``) - Event ID to mark up to. This is inclusive of the event ID specified. @@ -423,10 +371,8 @@ Notes: fall back to the timestamp heuristic. After all, these markers are only ever going to be heuristics given they are not acknowledging each message event. -Kicking a user -~~~~~~~~~~~~~~ -``Final`` - +Kicking a user ``[Final]`` +~~~~~~~~~~~~~~~~~~~~~~~~~~ Inputs: - User ID - Room ID @@ -436,10 +382,8 @@ Outputs: What data flows does it address: - Chat Screen: Kick a user -Leaving a room -~~~~~~~~~~~~~~ -``Final`` - +Leaving a room ``[Final]`` +~~~~~~~~~~~~~~~~~~~~~~~~~~ Inputs: - Room ID - A way of identifying the user (user ID, access token) @@ -449,9 +393,10 @@ Outputs: What data flows does it address: - Chat Screen: Leave a room -Send a message -~~~~~~~~~~~~~~ -``ONGOING`` : Semantics for HTTP ordering. +Send a message ``[ONGOING]`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.. NOTE:: + Semantics for HTTP ordering. Inputs: - Room ID @@ -470,35 +415,9 @@ E2E Notes: - For signing: You send the original message to the HS and it will return the full event JSON which will be sent. This full event is then signed and sent to the HS again to send the message. - -Sessions --------- -``ONGOING`` - -.. NOTE:: - - Offline mode? How does that work with sessions? - -A session is a group of requests sent within a short amount of time by the same -client. Sessions time out after a short amount of time without any requests. -Starting a session is known as going "online". Its purpose is to wrap up the -expiry of presence and typing notifications into a clearer scope. A session -starts when the client makes any request. A session ends when the client doesn't -make a request for a particular amount of time (times out). A session can also -end when explicitly hitting a particular endpoint. This is known as going -"offline". - -When a session starts, a session ID is sent in response to the first request the -client makes. This session ID should be sent in *all* subsequent requests. If -the server expires a session and the client uses an old session ID, the server -should fail the request with the old session ID and send a new session ID in -response for the client to use. If the client receives a new session ID -mid-session, it must re-establish its typing status and presence status, as they -are linked to the session ID. - -Presence API ------------- -``ONGOING`` +Presence API ``[ONGOING]`` +-------------------------- .. NOTE:: - Per device presence - Presence lists / roster? @@ -512,10 +431,8 @@ Outputs: - None. -Typing API ----------- -``ONGOING`` - +Typing API ``[ONGOING]`` +------------------------ .. NOTE:: - Linking the termination of typing events to the message itself, so you don't need to send two events and don't get flicker. @@ -532,10 +449,159 @@ Output: Notes: - Typing will time out when the session ends. -Action IDs ----------- -``ONGOING`` +Relates-to pagination API ``[Draft]`` +------------------------------------- +Inputs: + - Event ID + - Pagination token + - limit +Output: + - A chunk of child events + - A new pagination token + +Capabilities API ``[ONGOING]`` +------------------------------ +.. NOTE:: + - Server capabilities: Keep hashing step for consistency or not? Extra request. + - Client capabilities: List of hashes f.e device vs union of hashes on all + devices? + - Client capabilities: Clients which are offline but can be pushed should have + their capabilities visible. How to manage unregistering them e.g. if they + uninstall the app? + + +How does a client know if the server it is using supports a content repository? +How does a client know if another client has VoIP support? This section outlines +capability publishing for servers, clients and federation. + +Server +~~~~~~ +- List of extensions it supports (e.g. content repo, contact repo, turn servers) + +Inputs: + - User ID (e.g. only @bob can use the content repo) +Output: + - Hash of the capabilities:: + + { + "sha256": "fD876SFrt3sugh23FWEjio3" + } + +This hash is fed into another API: + +Inputs: + - The hash of the capabilities +Output: + - A list of capabilities:: + + { + "custom.feature.v1": {}, + "m.cap.turnserver.v1": {} + } + +Client +~~~~~~ +- e.g. Whether this client supports VoIP + +When a session is started, the client needs to provide a capability set. The +server will take the "union" of all the user's connected clients' capability +sets and send the hash of the capabilities as part of presence information +(not necesarily as a ``m.presence`` event, but it should act like presence +events). + +On first signup, the client will attempt to send the hash and be most likely +refused by the home server as it does not know the full capability set for that +hash. The client will then have to upload the full capability set to the home +server. The client will then be able to send the hash as normal. + +When a client receives a hash, the client will either recognise the hash or +will have to request the capability set from their home server: + +Inputs: + - Hash + - User ID +Output: + - A list of capabilities + +Federation +~~~~~~~~~~ +- e.g. Whether you support backfill, hypothetical search/query/threading APIs +- Same as the server capability API + +VoIP +---- +This addresses one-to-one calling with multiple devices. This uses the +``updates`` key to handle signalling. + +Event updates +~~~~~~~~~~~~~ +- Call is placed by caller. Event generated with offer. +- 1-N callees may pick up or reject this offer. +- Callees update the event (with sdp answer if they are accepting the call) +- Caller acknowledges *one* of the callees (either one which picked up or + rejected) by updating the event. +- Callees who weren't chosen then give up (Answered elsewhere, Rejected + elsewhere, etc) +- Update with ICE candidates as they appear. +- ... in call ... +- Send hangup update when hanging up. + +Placing a call +~~~~~~~~~~~~~~ +:: + + caller callee + |-----m.call.invite--->| + | | + |<----m.call.answer----| + | device_id=foo | + | | + |------m.call.ack----->| + | device_id=foo | + | | + |<--m.call.candidate---| + |---m.call.candidate-->| + | | + [...] [...] + | | + |<----m.call.hangup----| + | device_id=foo | + +Expiry +~~~~~~ +- WIP: Of invites +- WIP: Of calls themselves (as they may never send a ``m.call.hangup`` + + +General client changes +---------------------- +These are changes which do not introduce new APIs, but are required for the new +APIs in order to fix certain issues. + +Sessions ``[ONGOING]`` +~~~~~~~~~~~~~~~~~~~~~~ +.. NOTE:: + - Offline mode? How does that work with sessions? + +A session is a group of requests sent within a short amount of time by the same +client. Sessions time out after a short amount of time without any requests. +Starting a session is known as going "online". Its purpose is to wrap up the +expiry of presence and typing notifications into a clearer scope. A session +starts when the client makes any request. A session ends when the client doesn't +make a request for a particular amount of time (times out). A session can also +end when explicitly hitting a particular endpoint. This is known as going +"offline". +When a session starts, a session ID is sent in response to the first request the +client makes. This session ID should be sent in *all* subsequent requests. If +the server expires a session and the client uses an old session ID, the server +should fail the request with the old session ID and send a new session ID in +response for the client to use. If the client receives a new session ID +mid-session, it must re-establish its typing status and presence status, as they +are linked to the session ID. + +Action IDs ``[ONGOING]`` +~~~~~~~~~~~~~~~~~~~~~~~~ .. NOTE:: - HTTP Ordering: Blocking requests with higher seqnums is troublesome if there is a max # of concurrent connections a client can have open. @@ -550,10 +616,8 @@ If the client sends an action request with a stale session ID, the home server MUST fail the request and start a new session. The request needs to be failed in order to avoid edge cases with incrementing action IDs. -Updates (Events) ----------------- -``Draft`` - +Updates (Events) ``[Draft]`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Events may update other events. This is represented by the ``updates`` key. This is a key which contains the event ID for the event it relates to. Events that relate to other events are referred to as "Child Events". The event being @@ -561,7 +625,7 @@ related to is referred to as "Parent Events". Child events cannot stand alone as a separate entity; they require the parent event in order to make sense. Bundling -~~~~~~~~ +++++++++ Events that relate to another event should come down inside that event. That is, the top-level event should come down with all the child events at the same time. This is called a "bundle" and it is represented as an array of events inside the @@ -595,10 +659,8 @@ receive the events separately down the event stream. Combining event updates server-side does not make client implementation simpler, as the client still needs to know how to combine the events. -Relates to (Events) -------------------- -``Draft`` - +Relates to (Events) ``[Draft]`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Events may be in response to other events, e.g. comments. This is represented by the ``relates_to`` key. This differs from the ``updates`` key as they *do not update the event itself*, and are *not required* in order to display the @@ -606,7 +668,7 @@ parent event. Crucially, the child events can be paginated, whereas ``updates`` child events cannot be paginated. Bundling -~~~~~~~~ +++++++++ Child events can be optionally bundled with the parent event, depending on your display mechanism. The number of child events which can be bundled should be limited to prevent events becoming too large. This limit should be set by the @@ -627,9 +689,10 @@ TODO: - Should a parent event and its children share a thread ID? Does the originating HS set this ID? Is this thread ID exposed through federation? e.g. can a HS retrieve all events for a given thread ID from another HS? + -Example using ``updates`` and ``relates_to`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Example using 'updates' and 'relates_to' +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - Room with a single message. - 10 comments are added to the message via ``relates_to``. - An edit is made to the original message via ``updates``. @@ -655,9 +718,8 @@ Example using ``updates`` and ``relates_to`` ... } -Events (breaking changes; event version 2) ------------------------------------------- -``Draft`` +Events (breaking changes; event version 2) ``[Draft]`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - Prefix the event ``type`` to say if it is a state event, message event or ephemeral event. Needed because you can't tell the different between message @@ -676,121 +738,4 @@ Events (breaking changes; event version 2) the state key, etc. - s/user_id/sender/g given that home servers can send events, not just users. -Capabilities API ----------------- -``ONGOING`` - -.. NOTE:: - - Server capabilities: Keep hashing step for consistency or not? Extra request. - - Client capabilities: List of hashes f.e device vs union of hashes on all - devices? - - Client capabilities: Clients which are offline but can be pushed should have - their capabilities visible. How to manage unregistering them e.g. if they - uninstall the app? - - -How does a client know if the server it is using supports a content repository? -How does a client know if another client has VoIP support? This section outlines -capability publishing for servers, clients and federation. - -Server -~~~~~~ -- List of extensions it supports (e.g. content repo, contact repo, turn servers) - -Inputs: - - User ID (e.g. only @bob can use the content repo) -Output: - - Hash of the capabilities:: - - { - "sha256": "fD876SFrt3sugh23FWEjio3" - } - -This hash is fed into another API: - -Inputs: - - The hash of the capabilities -Output: - - A list of capabilities:: - - { - "custom.feature.v1": {}, - "m.cap.turnserver.v1": {} - } - -Client -~~~~~~ -- e.g. Whether this client supports VoIP - -When a session is started, the client needs to provide a capability set. The -server will take the "union" of all the user's connected clients' capability -sets and send the hash of the capabilities as part of presence information -(not necesarily as a ``m.presence`` event, but it should act like presence -events). - -On first signup, the client will attempt to send the hash and be most likely -refused by the home server as it does not know the full capability set for that -hash. The client will then have to upload the full capability set to the home -server. The client will then be able to send the hash as normal. - -When a client receives a hash, the client will either recognise the hash or -will have to request the capability set from their home server: - -Inputs: - - Hash - - User ID -Output: - - A list of capabilities - -Federation -~~~~~~~~~~ -- e.g. Whether you support backfill, hypothetical search/query/threading APIs -- Same as the server capability API - -VoIP ----- -This addresses one-to-one calling with multiple devices. This uses the -``updates`` key to handle signalling. - -Event updates -~~~~~~~~~~~~~ -- Call is placed by caller. Event generated with offer. -- 1-N callees may pick up or reject this offer. -- Callees update the event (with sdp answer if they are accepting the call) -- Caller acknowledges *one* of the callees (either one which picked up or - rejected) by updating the event. -- Callees who weren't chosen then give up (Answered elsewhere, Rejected - elsewhere, etc) -- Update with ICE candidates as they appear. -- ... in call ... -- Send hangup update when hanging up. - -Placing a call -~~~~~~~~~~~~~~ -:: - - caller callee - |-----m.call.invite--->| - | | - |<----m.call.answer----| - | device_id=foo | - | | - |------m.call.ack----->| - | device_id=foo | - | | - |<--m.call.candidate---| - |---m.call.candidate-->| - | | - [...] [...] - | | - |<----m.call.hangup----| - | device_id=foo | - -Expiry -~~~~~~ -- WIP: Of invites -- WIP: Of calls themselves (as they may never send a ``m.call.hangup`` - - - From 2c186907668fb2a176dd660dd94028738b16909a Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Tue, 6 Jan 2015 16:35:41 +0000 Subject: [PATCH 29/95] Add back in missed TODOs --- drafts/general_api.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drafts/general_api.rst b/drafts/general_api.rst index 787d3804..2898d6c8 100644 --- a/drafts/general_api.rst +++ b/drafts/general_api.rst @@ -256,6 +256,14 @@ Outputs: - Start / End pagination tokens - Current room state at the end of the chunk as per initial sync. +Room Directory API ``[TODO]`` +----------------------------- + +Public room list API ``[TODO]`` +------------------------------- + +User Profile API ``[TODO]`` +--------------------------- Action APIs ----------- From 6ce518eb19e03a0aad07d61d90153caf09ca6525 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Tue, 6 Jan 2015 16:54:14 +0000 Subject: [PATCH 30/95] Add v2 path prefix. Add draft public room list API. --- drafts/general_api.rst | 48 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 44 insertions(+), 4 deletions(-) diff --git a/drafts/general_api.rst b/drafts/general_api.rst index 2898d6c8..eb568c84 100644 --- a/drafts/general_api.rst +++ b/drafts/general_api.rst @@ -47,6 +47,12 @@ protocol e.g. HTTP. The following APIs will remain unchanged from v1: - Login API - Content repository API +This version will change the path prefix for HTTP: + - Version 1: ``/_matrix/client/api/v1`` + - Version 2: ``/_matrix/client/v2`` + +Note the lack of the ``api`` segment. This is for consistency between other +home server path prefixes. Filter API ``[ONGOING]`` ------------------------ @@ -256,14 +262,48 @@ Outputs: - Start / End pagination tokens - Current room state at the end of the chunk as per initial sync. -Room Directory API ``[TODO]`` ------------------------------ +Room Alias API ``[TODO]`` +------------------------- +This provides mechanisms for creating and removing room aliases for a room on a +home server. + +Public room list API ``[Draft]`` +-------------------------------- +This provides mechanisms for searching for public rooms on a home server. + +Inputs: + - Search text (e.g. room alias/name/topic to search on) + - Home server to search on (this may just be the URL hit for HTTP) + - Any existing pagination token + - Limit for pagination +Output: + - Pagination token + - Total number of rooms + - Which 'page' of results this response represents + - A list of rooms with: + - # users + - A set of 'public' room state events, presumably ``m.room.name``, + ``m.room.topic`` and ``m.room.aliases``. This cannot be user-configured + since the user is not in the room. +Notes: + - This API would be hit again for the next page of results, with the pagination + token provided from the previous hit. + - We should probably provide "and X more" estimates for the number of + pagination results. This can be calculated by providing the total number of + rooms e.g. '100' and the page e.g. '3' coupled with the limit parameter (aka + the number of results per page) specified e.g. '10'. + - In order to prevent the dataset from changing underneath the client whilst + they paginate, a request without a pagination token should take a "snapshot" + of the underlying data which is then paginated on, rather than the database + which is a moving target as other clients add new public rooms. -Public room list API ``[TODO]`` -------------------------------- User Profile API ``[TODO]`` --------------------------- +Every user on a home server has a profile. This profile is effectively a +key-value store scoped to a user ID. It can include an ``avatar_url``, +``displayname`` and other metadata. Updates to a profile should propagate to +other interested users. Action APIs ----------- From 9408bc8260d4f666fa1936ca586a032389300696 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Tue, 6 Jan 2015 17:20:00 +0000 Subject: [PATCH 31/95] Add notes to ONGOING APIs. --- drafts/general_api.rst | 49 ++++++++++++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 16 deletions(-) diff --git a/drafts/general_api.rst b/drafts/general_api.rst index eb568c84..021b24cd 100644 --- a/drafts/general_api.rst +++ b/drafts/general_api.rst @@ -444,7 +444,11 @@ What data flows does it address: Send a message ``[ONGOING]`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. NOTE:: - Semantics for HTTP ordering. + Semantics for HTTP ordering. Do we really want to block requests with higher + sequence numbers if the server hasn't received earlier ones? Is this even + practical, given clients have a limit on the number of concurrent connections? + How can this be done in a way which doesn't suck for clients? Could we just + say "it isn't 'Sent' until it comes back down your event stream"? Inputs: - Room ID @@ -467,8 +471,12 @@ E2E Notes: Presence API ``[ONGOING]`` -------------------------- .. NOTE:: - - Per device presence - - Presence lists / roster? + - Per device presence: how does this work? Union of devices? Priority order for + statuses? E.g. online trumps away trumps offline. So if any device is online, + then the user is online, etc. + - Presence lists / roster? We probably do want this, but are we happy with the + v1 semantics? + When a session starts, the home server can treat the user as "online". When the session ends, the home server can treat the user as "offline". @@ -483,7 +491,7 @@ Typing API ``[ONGOING]`` ------------------------ .. NOTE:: - Linking the termination of typing events to the message itself, so you don't - need to send two events and don't get flicker. + need to send two events and don't get flicker? When in a session, a user can send a request stating that they are typing in a room. They are no longer typing when either the session ends or they explicitly @@ -495,27 +503,29 @@ Inputs: Output: - None. Notes: - - Typing will time out when the session ends. + - Typing will time out when the session ends. If a session is restarted, the + typing notification must be sent again. Relates-to pagination API ``[Draft]`` ------------------------------------- +See the "Relates to" section for more info. + Inputs: - Event ID - Pagination token - limit Output: - A chunk of child events - - A new pagination token + - A new pagination token for earlier child events. Capabilities API ``[ONGOING]`` ------------------------------ .. NOTE:: - - Server capabilities: Keep hashing step for consistency or not? Extra request. - - Client capabilities: List of hashes f.e device vs union of hashes on all - devices? + - Server capabilities: Keep hashing step for consistency or not? Extra request + if we do. - Client capabilities: Clients which are offline but can be pushed should have their capabilities visible. How to manage unregistering them e.g. if they - uninstall the app? + uninstall the app? Have a set of 'offline' capabilities? How does a client know if the server it is using supports a content repository? @@ -552,10 +562,16 @@ Client - e.g. Whether this client supports VoIP When a session is started, the client needs to provide a capability set. The -server will take the "union" of all the user's connected clients' capability -sets and send the hash of the capabilities as part of presence information +server will take the hashes of all the user's connected clients' capability +sets and send the list of hashes as part of presence information (not necesarily as a ``m.presence`` event, but it should act like presence -events). +events). It is sent as a list instead of a union of hashes because hashes work +best when they don't change. A union of many devices' hashes will change +frequently when devices come on and offline (``max hashes = 2^num_devices``). +In contrast, the size of the list would vary, but the hashes themselves +would remain the same for a given device (``max hashes = num_devices``). Keeping +the hashes the same is the best as that means clients do not need to request +the capabilities for the given hash. On first signup, the client will attempt to send the hash and be most likely refused by the home server as it does not know the full capability set for that @@ -576,8 +592,8 @@ Federation - e.g. Whether you support backfill, hypothetical search/query/threading APIs - Same as the server capability API -VoIP ----- +VoIP ``[TODO]`` +--------------- This addresses one-to-one calling with multiple devices. This uses the ``updates`` key to handle signalling. @@ -629,7 +645,8 @@ APIs in order to fix certain issues. Sessions ``[ONGOING]`` ~~~~~~~~~~~~~~~~~~~~~~ .. NOTE:: - - Offline mode? How does that work with sessions? + - Offline mode? How does that work with sessions? Separate endpoint to say + "start a session only"? A session is a group of requests sent within a short amount of time by the same client. Sessions time out after a short amount of time without any requests. From f88527724fa9a732db98e14d6d408051cd190019 Mon Sep 17 00:00:00 2001 From: Kegsay Date: Tue, 6 Jan 2015 17:25:31 +0000 Subject: [PATCH 32/95] TODO/notes consistency. Add a few minor points. --- drafts/general_api.rst | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/drafts/general_api.rst b/drafts/general_api.rst index 021b24cd..2ac340a9 100644 --- a/drafts/general_api.rst +++ b/drafts/general_api.rst @@ -57,8 +57,10 @@ home server path prefixes. Filter API ``[ONGOING]`` ------------------------ .. NOTE:: - Exactly what can be filtered? Which APIs use this? Are we - conflating too much? + - Exactly what can be filtered? Which APIs use this? Are we + conflating too much? + - Do we want to specify negative filters (e.g. don't give me + ``event.type.here`` events) Inputs: - Which event types (incl wildcards) @@ -81,12 +83,22 @@ Notes: - HTTP note: If the filter API is a separate endpoint, then you could easily allow APIs which use filtering to ALSO specifiy query parameters to tweak the filter. -TODO: - - Do we want to specify negative filters (e.g. don't give me - ``event.type.here`` events) Global initial sync API ``[ONGOING]`` ------------------------------------- +.. NOTE:: + - Will need some form of state event pagination like we have for message events + to handle large amounts of state events for a room. Need to think of the + consequences of this: you may not get a ``m.room.member`` for someone's + message and so cannot display their display name / avatar. Do we want to + provide pagination on an event type basis? + - Handle paginating initial sync results themselves (e.g. 10 most recent rooms) + - No need for state events under the 'state' key to have a ``prev_content``. + Can also apply some optimisations depending on the direction of travel when + scrolling back. + - Do we want to treat global / specific room initial syncs as separate entities? + Aren't they just a filter? + Inputs: - A way of identifying the user (e.g. access token, user ID, etc) - Streaming token (optional) @@ -104,17 +116,6 @@ Notes: rooms. What data flows does it address: - Home screen: data required on load. - -TODO: - - Will need some form of state event pagination like we have for message events - to handle large amounts of state events for a room. Need to think of the - consequences of this: you may not get a ``m.room.member`` for someone's - message and so cannot display their display name / avatar. Do we want to - provide pagination on an event type basis? - - Handle paginating initial sync results themselves (e.g. 10 most recent rooms) - - No need for state events under the 'state' key to have a ``prev_content``. - Can also apply some optimisations depending on the direction of travel when - scrolling back. Event Stream API ``[Draft]`` @@ -178,8 +179,8 @@ What data flows does it address: - Chat Screen: Data required when the room name changes - Chat Screen: Data required when a new message arrives -Room Creation ``[Draft]`` -------------------------- +Room Creation API ``[Draft]`` +----------------------------- Inputs: - Invitee list of user IDs, public/private, state events to set on creation e.g. name of room, alias of room, topic of room @@ -190,8 +191,8 @@ Notes: What data flows does it address: - Home Screen: Creating a room -Joining a room ``[Draft]`` --------------------------- +Joining API``[Draft]`` +---------------------- Inputs: - Room ID (with list of servers to join from) / room alias / invite event ID - Optional filter (which events to return, whether the returned events should From 33b2ee562ccaa5811c6730f24c3a903ebe456541 Mon Sep 17 00:00:00 2001 From: Kegsay Date: Tue, 6 Jan 2015 17:27:05 +0000 Subject: [PATCH 33/95] Formatting. --- drafts/general_api.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drafts/general_api.rst b/drafts/general_api.rst index 2ac340a9..f58bfefc 100644 --- a/drafts/general_api.rst +++ b/drafts/general_api.rst @@ -191,8 +191,8 @@ Notes: What data flows does it address: - Home Screen: Creating a room -Joining API``[Draft]`` ----------------------- +Joining API ``[Draft]`` +----------------------- Inputs: - Room ID (with list of servers to join from) / room alias / invite event ID - Optional filter (which events to return, whether the returned events should From 8f1d06e94bc0f7c28eadba0be181027a7e09cc7b Mon Sep 17 00:00:00 2001 From: Mark Haines Date: Tue, 6 Jan 2015 19:23:28 +0000 Subject: [PATCH 34/95] Add use cases for email and multithreaded IM --- drafts/use_cases.rst | 61 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 54 insertions(+), 7 deletions(-) diff --git a/drafts/use_cases.rst b/drafts/use_cases.rst index a6931b08..a748f462 100644 --- a/drafts/use_cases.rst +++ b/drafts/use_cases.rst @@ -16,13 +16,15 @@ Use cases - #2: Bug tracking software - #3: Forum - #4: Google + style communities -- #5: Mobile IM client (perm storage) -- #6: MIDI client -- #7: Animatrix client -- #8: Unity object trees -- #9: Social Network ("Walls", PMs, groups) -- #10: Minecraft-clone -- #11: Global 'Like' widget, which links through to a room. +- #5: Email style threading +- #6: Multi-column threaded IM +- #7: Mobile IM client (perm storage) +- #8: MIDI client +- #9: Animatrix client +- #10: Unity object trees +- #11: Social Network ("Walls", PMs, groups) +- #12: Minecraft-clone +- #13: Global 'Like' widget, which links through to a room. #1 Web client UI @@ -259,3 +261,48 @@ Community Page - Share a post - +1 a post +#5 Email style threading +======================== + +Chat Screen + What's visible: + - Enough scrollback to fill a "screen full" of content. + - Threads: + - Initially will only display the timestamp and user ID of the *first* + message. But can expand to show the entire tree. + - Tree of messages indicating which message is a reply to which. + - Ordered the timestamp of the most recent message in the threads + - Each message: timestamp, user ID, display name at the time of the message + - Room name + - Room topic + - Typing notifications + - Desktop/Push Notifications for messages + What you can do: + - Send a message in reply to another message: + - Immediate local echo, thread moves to bottom of screen + - Messages that haven't reached the server are queued. + - Thread is displayed where it should be in the thread order once the + message is sent. + - Start a new thread by sending a message. + +#6 Multi-threaded IM +==================== + +Chat Screen + What's visible: + - A multi-column grid of threads from a number of chatrooms + Each concurrent thread is displayed in a different column. + The columns start and end as threads split and rejoin the main conversation + The messages for each thread are ordered by how recent they are:: + + Room #1 Room # 2 Room # 2 + +------------+ +----------------+ Side thread. + | * Message1 | | * Root | +--------------+ + | * Message2 | | * A1 -> Root | | * B1 -> Root | + +------------+ | * A2 -> A1 | | * B2 -> B1 | + | * M -> A2, B2 | +--------------+ + +----------------+ + + What you can do: + - Send a message into a particular thread/column. + - Move an *existing* message into new thread creating a new column. From 6515dbc4672c7066335fe2e8cf94c4412a763f22 Mon Sep 17 00:00:00 2001 From: Mark Haines Date: Tue, 6 Jan 2015 19:28:27 +0000 Subject: [PATCH 35/95] Add note about typing notifs to Multi-Threaded IM --- drafts/use_cases.rst | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drafts/use_cases.rst b/drafts/use_cases.rst index a748f462..044ff5ab 100644 --- a/drafts/use_cases.rst +++ b/drafts/use_cases.rst @@ -268,10 +268,10 @@ Chat Screen What's visible: - Enough scrollback to fill a "screen full" of content. - Threads: - - Initially will only display the timestamp and user ID of the *first* + - Initially will only display the timestamp and user ID of the *first* message. But can expand to show the entire tree. - Tree of messages indicating which message is a reply to which. - - Ordered the timestamp of the most recent message in the threads + - Ordered by the timestamp of the most recent message in the thread - Each message: timestamp, user ID, display name at the time of the message - Room name - Room topic @@ -302,7 +302,8 @@ Chat Screen +------------+ | * A2 -> A1 | | * B2 -> B1 | | * M -> A2, B2 | +--------------+ +----------------+ + - Typing notifications. Displayed within the correct thread/column. What you can do: - Send a message into a particular thread/column. - - Move an *existing* message into new thread creating a new column. + - Move an *existing* message into a new thread creating a new column. From 7a4515def8d4154dd3b7991552de00833b27c007 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Tue, 6 Jan 2015 19:45:40 +0000 Subject: [PATCH 36/95] support arbitrary ordering of mail-style use case, and add mergable threads to IM --- drafts/use_cases.rst | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drafts/use_cases.rst b/drafts/use_cases.rst index 044ff5ab..955db4b3 100644 --- a/drafts/use_cases.rst +++ b/drafts/use_cases.rst @@ -271,7 +271,8 @@ Chat Screen - Initially will only display the timestamp and user ID of the *first* message. But can expand to show the entire tree. - Tree of messages indicating which message is a reply to which. - - Ordered by the timestamp of the most recent message in the thread + - Ordered by the arbitrary field (timestamp of oldest message in thread; + newest message in thread; sender id; sender display name; etc) - Each message: timestamp, user ID, display name at the time of the message - Room name - Room topic @@ -279,7 +280,7 @@ Chat Screen - Desktop/Push Notifications for messages What you can do: - Send a message in reply to another message: - - Immediate local echo, thread moves to bottom of screen + - Immediate local echo, may cause messages to re-order - Messages that haven't reached the server are queued. - Thread is displayed where it should be in the thread order once the message is sent. @@ -306,4 +307,8 @@ Chat Screen What you can do: - Send a message into a particular thread/column. - - Move an *existing* message into a new thread creating a new column. + - Move an *existing* message into a new thread creating a new column + - Move an existing message into an existing thread, causing the threads to + reconverge (i.e. provide a route from the sidebar back into the existing + thread). This does not imply terminating the thread, which can continue + independently of the merge. From a42ab432659345b912ecebfd27e064f193078aa9 Mon Sep 17 00:00:00 2001 From: Kegsay Date: Wed, 7 Jan 2015 10:53:34 +0000 Subject: [PATCH 37/95] Add draft room alias API --- drafts/general_api.rst | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/drafts/general_api.rst b/drafts/general_api.rst index f58bfefc..24ff5bf1 100644 --- a/drafts/general_api.rst +++ b/drafts/general_api.rst @@ -263,10 +263,40 @@ Outputs: - Start / End pagination tokens - Current room state at the end of the chunk as per initial sync. -Room Alias API ``[TODO]`` +Room Alias API ``[Draft]`` ------------------------- This provides mechanisms for creating and removing room aliases for a room on a -home server. +home server. Typically, any user in a room can make an alias for that room. The +alias creator (or anyone in the room?) can delete that alias. Server admins can +also delete any alias on their server. + +Mapping a room alias to a room: + +Inputs: + - Room Alias +Output: + - Room ID + - List of home servers to join via. + +Mapping a room to an alias: + +Inputs: + - Room ID + - Desired room alias localpart + - User ID (for auth) +Output: + - Room alias +Notes: + - The home server may add restrictions e.g. the user must be in the room. + +Deleting a mapping: + +Inputs: + - Room alias + - User ID (for auth) +Output: + - None. + Public room list API ``[Draft]`` -------------------------------- From efd9b8a53e8893419ac2f4b303ab915746cd4309 Mon Sep 17 00:00:00 2001 From: Kegsay Date: Wed, 7 Jan 2015 11:33:05 +0000 Subject: [PATCH 38/95] Add draft User Profile API --- drafts/general_api.rst | 53 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/drafts/general_api.rst b/drafts/general_api.rst index 24ff5bf1..8f4e4b82 100644 --- a/drafts/general_api.rst +++ b/drafts/general_api.rst @@ -329,13 +329,64 @@ Notes: which is a moving target as other clients add new public rooms. -User Profile API ``[TODO]`` +User Profile API ``[Draft]`` --------------------------- Every user on a home server has a profile. This profile is effectively a key-value store scoped to a user ID. It can include an ``avatar_url``, ``displayname`` and other metadata. Updates to a profile should propagate to other interested users. +Setting display name (strings): + +Inputs: + - User ID + - New display name +Output: + - None. +Notes: + - This is a generic problem, so should probably not be special cased for + display names. E.g. having an arbitrary key-value store here. + +Setting avatar url (blob data): + +Inputs: + - User ID + - New avatar url / file blob? +Output: + - None. +Notes: + - We may want to provide file uploading on this API for convenience. + +Retrieving profile information: + +Inputs: + - User ID + - Which keys to retrieve +Output: + - The key/values specified. + +Propagation +~~~~~~~~~~~ +The goals of propagation are: + +- Profile updates should propagate to all rooms the user is in. +- We should support different kinds of profiles for different rooms. + +In v1, users have a single profile. This information is duplicated for +every room the user is in. This duplication means that things like +display names *could* change on a room-by-room basis. However, this is +extremely inefficient when updating the display name, as you have to +send ``num_joined_rooms`` events to inform everyone of the update. + +There's no easy solution to this. The room needs a record of the name +changes; it's not good enough to send it just to the users (the set of +all users in all rooms the user changing their display name is in), as +new users who join later still need to know about these changes. The +ordering information needs to be preserved as well. + +An improvement would be to allow the client to not automatically share +updates of their profile information to all rooms. + Action APIs ----------- The following APIs are "action APIs". This is defined to be a request which From c068dd414e5a286d854bc46bfd5765db1a7ac4b7 Mon Sep 17 00:00:00 2001 From: Kegsay Date: Wed, 7 Jan 2015 15:28:05 +0000 Subject: [PATCH 39/95] Send message API: Add compact flag notes --- drafts/general_api.rst | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/drafts/general_api.rst b/drafts/general_api.rst index 8f4e4b82..6607dc4d 100644 --- a/drafts/general_api.rst +++ b/drafts/general_api.rst @@ -536,9 +536,11 @@ Inputs: - Room ID - Message contents - Action ID (optional) + - Whether the full event should be returned, or a compact version (default=full) Outputs: - - Actual content sent (if server modified it) - - When in the stream this action happened. (to correctly display local echo) + - The actual event sent incl content OR: + - The extra keys added or keys modified e.g. 'content' from a policy server + (if compact=true) What data flows does it address: - Chat Screen: Send a Message Ordering notes: @@ -549,6 +551,14 @@ E2E Notes: - For signing: You send the original message to the HS and it will return the full event JSON which will be sent. This full event is then signed and sent to the HS again to send the message. +Compact flag notes: + - You need to know information about the event sent, including the event ID, + timestamp, etc. + - Default behaviour will return the entire event JSON sent to make client + implementations simple (just clobber it). + - It sucks to have your own messages echoed back to you in response though. + As a result, you can ask for a compact version which just sends down the + keys which were added, e.g. timestamp and event ID. Presence API ``[ONGOING]`` -------------------------- From af3baef164c930d6ce65da262e08fa9cf66c0028 Mon Sep 17 00:00:00 2001 From: Kegsay Date: Thu, 8 Jan 2015 17:37:05 +0000 Subject: [PATCH 40/95] Add draft Account Management API Mainly for SPEC-50 --- drafts/general_api.rst | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/drafts/general_api.rst b/drafts/general_api.rst index 6607dc4d..b1165f93 100644 --- a/drafts/general_api.rst +++ b/drafts/general_api.rst @@ -387,6 +387,36 @@ ordering information needs to be preserved as well. An improvement would be to allow the client to not automatically share updates of their profile information to all rooms. +Account Management API ``[Draft]`` +---------------------------------- +Users may wish to delete their account, revoke access tokens, manage +their devices, etc. This is achieved using an account management API. + +Deleting an account: + +Inputs: + - User ID to delete + - Auth key (e.g. access_token of user, of server admin, etc) +Output: + - None. + +Viewing access tokens related to this account: + +Inputs: + - User ID + - Auth key (e.g. access_token of user, of server admin, etc) +Output: + - A list of access tokens (+ last used / creation date / device / user-agent?) + +Removing an access token: + +Inputs: + - User ID + - Auth key (e.g. access_token of user, of server admin, etc) + - Access token to revoke. +Output: + - None. + Action APIs ----------- The following APIs are "action APIs". This is defined to be a request which From 79c510dadc8c9115bd52faaccccf3ac7a9eaa3c7 Mon Sep 17 00:00:00 2001 From: Kegsay Date: Thu, 8 Jan 2015 17:39:01 +0000 Subject: [PATCH 41/95] Add TODO stub for room knocking Mainly for SPEC-60 --- drafts/general_api.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drafts/general_api.rst b/drafts/general_api.rst index b1165f93..14240186 100644 --- a/drafts/general_api.rst +++ b/drafts/general_api.rst @@ -498,6 +498,11 @@ Notes: - This is represented on the event stream as an event lacking a ``content`` key (for symmetry with ``prev_content``) +Knocking on a room ``[TODO]`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +If a room has the right ``join_rule`` e.g. ``knock``, then it should be able +to send a special knock event to ask to join the room. + Read-up-to markers ``[Draft]`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Inputs: From eb237ed297778fbeaa6278e0f6fe3c151fda26aa Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Mon, 12 Jan 2015 11:49:20 +0000 Subject: [PATCH 42/95] Blob v2 features into 2.0/2.1 --- drafts/general_api.rst | 42 +++++++++++++++++++++++++++++------------- 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/drafts/general_api.rst b/drafts/general_api.rst index 14240186..370287e2 100644 --- a/drafts/general_api.rst +++ b/drafts/general_api.rst @@ -6,27 +6,43 @@ Table of Contents Summary of changes from v1 ========================== -Included: + +Version 2.0 +----------- - Event filtering (type/room/users, federation-style events) - - Incremental syncing - - Rejecting invites + - Incremental initial syncing - Deleting state - - Contextual messages (view messages around an arbitrary message) - Race conditions on event stream / actions - Out-of-order events + +TODO (In scope but needs a bit more work): + - Published room API: support searching remote HSes. + - Device management (see Account management; s/access token/device ID/g? + - Multiple devices + - Presence status unioning: is partially specced (needs more eyes). + - Syncing data between multiple devices: is specced. + - TODO: Push for offline devices. + - Handling "duplicate" events in state/messages key on initial sync. + - Have a compact=true flag. + - Have an events = { "event_id": , ... } per room + - messages/state keys just have an array of event IDs. + - PATCHing power levels - not over federation; just local HS. Spec that. + +Lower priority +~~~~~~~~~~~~~~ - Capabilities - - Comments (relates_to key) - Editing/updating messages (updates key) + - Room alias API + +Version 2.1 +----------- + - Comments (relates_to key) + - Contextual messages (view messages around an arbitrary message) + - Rejecting invites -Excluded: +Out of scope +------------ - Searching messages - - State event pagination (see Global /initialSync API) - - Initial sync pagination (see Global /initialSync API) - - PATCHing power levels - - Handling "duplicate" events in state/messages key on initial sync. - - Multiple devices (other than VoIP) - - Room directory lists (aka public room list, paginating, permissions on - editing the list, etc) Version 2 API ============= From 6705f0891d0aee7a00bf49f7fc79d970e14798d6 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Mon, 12 Jan 2015 12:59:11 +0000 Subject: [PATCH 43/95] Factor out parts of room history APIs. Add in a terminology section for the different kinds of tokens. s/public rooms/published rooms/g --- drafts/general_api.rst | 80 ++++++++++++++++++++++++++---------------- 1 file changed, 49 insertions(+), 31 deletions(-) diff --git a/drafts/general_api.rst b/drafts/general_api.rst index 370287e2..0020c6f1 100644 --- a/drafts/general_api.rst +++ b/drafts/general_api.rst @@ -69,6 +69,19 @@ This version will change the path prefix for HTTP: Note the lack of the ``api`` segment. This is for consistency between other home server path prefixes. + +Terminology: + - ``Chunk token`` : An opaque string which can be used to return another chunk + of results. For example, the initial sync API and scrollback/contextual + windowing APIs. If the total size of the data set is unknown, it should + return a chunk token to navigate it. + - ``Filter token`` : An opaque string representing the inputs originally given + to the filter API. + - ``Pagination token`` : An opaque string used for pagination requests. For + example, the published room list API. The size of the data set is known (e.g. + because a snapshot of it was taken) and you can do "Page X of Y" style + navigation. + Filter API ``[ONGOING]`` ------------------------ @@ -89,7 +102,7 @@ Inputs: - Which keys to return for events? e.g. no ``origin_server_ts`` if you don't show timestamps Outputs: - - An opaque token which represents the inputs + - An opaque token which represents the inputs, the "filter token". Notes: - The token may expire, in which case you would need to request another one. - The token could be as simple as a concatenation of the requested filters with @@ -117,10 +130,8 @@ Global initial sync API ``[ONGOING]`` Inputs: - A way of identifying the user (e.g. access token, user ID, etc) - - Streaming token (optional) - - Which state event types to return (e.g. ``m.room.name`` / ``m.room.topic`` - / ``m.room.aliases``) - Filter to apply + - Chunk token (for incremental deltas) Outputs: - For each room the user is joined: - Requested state events @@ -128,7 +139,7 @@ Outputs: - max of limit= message events - room ID Notes: - - If a streaming token is applied, you will get a delta rather than all the + - If a chunk token is applied, you will get a delta rather than all the rooms. What data flows does it address: - Home screen: data required on load. @@ -198,7 +209,7 @@ What data flows does it address: Room Creation API ``[Draft]`` ----------------------------- Inputs: - - Invitee list of user IDs, public/private, state events to set on creation + - Invitee list of user IDs, published/not, state events to set on creation e.g. name of room, alias of room, topic of room Output: - Room ID @@ -245,38 +256,45 @@ Mapping messages to the event stream: What data flows does it address: - Home Screen: Joining a room +Room History +------------ +This concerns APIs which return historical events for a room. There are several +common parameters. + +Inputs: + - Room ID + - Max number of events to return + - Filter to apply. +Outputs: + - Requested events + - Chunk token to use to request more events. + + Scrollback API ``[Draft]`` --------------------------- +~~~~~~~~~~~~~~~~~~~~~~~~~~ .. NOTE:: - Pagination: Would be nice to have "and X more". It will probably be Google-style estimates given we can't know the exact number over federation, but as a purely informational display thing it would be nice. -Inputs: - - Identifier for the earliest event - - # requested events - - filter to apply +Additional Inputs: - flag to say if the home server should do a backfill over federation -Outputs: - - requested events (f.e change in display name, what the old name was), +Additional Outputs: - whether there are more events on the local HS / over federation. - - new identifier for the earliest event What data flows does it address: - Chat Screen: Scrolling back (infinite scrolling) Contextual windowing API ``[Draft]`` ------------------------------------- +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This refers to showing a "window" of message events around a given message event. The window provides the "context" for the given message event. -Inputs: +Additional Inputs: - Event ID of the message to get the surrounding context for (this specifies the room to get messages in). - - Number of messages before/after this message to obtain. - - Filter to apply. -Outputs: - - Chunk of messages - - Start / End pagination tokens + - Whether to get results before / after / around (mid point) this event +Additional Outputs: + - Start / End chunk tokens to go either way (not just one token) - Current room state at the end of the chunk as per initial sync. Room Alias API ``[Draft]`` @@ -314,14 +332,14 @@ Output: - None. -Public room list API ``[Draft]`` --------------------------------- -This provides mechanisms for searching for public rooms on a home server. +Published room list API ``[Draft]`` +----------------------------------- +This provides mechanisms for searching for published rooms on a home server. Inputs: - Search text (e.g. room alias/name/topic to search on) - Home server to search on (this may just be the URL hit for HTTP) - - Any existing pagination token + - Any existing pagination token, can be missing if this is the first hit. - Limit for pagination Output: - Pagination token @@ -329,7 +347,7 @@ Output: - Which 'page' of results this response represents - A list of rooms with: - # users - - A set of 'public' room state events, presumably ``m.room.name``, + - A set of 'publishable' room state events, presumably ``m.room.name``, ``m.room.topic`` and ``m.room.aliases``. This cannot be user-configured since the user is not in the room. Notes: @@ -342,7 +360,7 @@ Notes: - In order to prevent the dataset from changing underneath the client whilst they paginate, a request without a pagination token should take a "snapshot" of the underlying data which is then paginated on, rather than the database - which is a moving target as other clients add new public rooms. + which is a moving target as other clients add new published rooms. User Profile API ``[Draft]`` @@ -655,11 +673,11 @@ See the "Relates to" section for more info. Inputs: - Event ID - - Pagination token + - Chunk token - limit Output: - A chunk of child events - - A new pagination token for earlier child events. + - A new chunk token for earlier child events. Capabilities API ``[ONGOING]`` ------------------------------ @@ -881,7 +899,7 @@ Child events can be optionally bundled with the parent event, depending on your display mechanism. The number of child events which can be bundled should be limited to prevent events becoming too large. This limit should be set by the client. If the limit is exceeded, then the bundle should also include a -pagination token so that the client can request more child events. +chunk token so that the client can request more child events. Main use cases for ``relates_to``: - Comments on a message. @@ -906,7 +924,7 @@ Example using 'updates' and 'relates_to' - An edit is made to the original message via ``updates``. - An initial sync on this room with a limit of 3 comments, would return the message with the update event bundled with it and the most recent 3 comments - and a pagination token to request earlier comments + and a chunk token to request earlier comments .. code :: javascript From 4a4f9151ef7c2f353b2934e86df4a503ece86c01 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Mon, 12 Jan 2015 13:20:04 +0000 Subject: [PATCH 44/95] Add notes on patching power levels and compacting initial sync events. --- drafts/general_api.rst | 67 ++++++++++++++++++++++++++++++++---------- 1 file changed, 51 insertions(+), 16 deletions(-) diff --git a/drafts/general_api.rst b/drafts/general_api.rst index 0020c6f1..0d5fde0c 100644 --- a/drafts/general_api.rst +++ b/drafts/general_api.rst @@ -14,19 +14,12 @@ Version 2.0 - Deleting state - Race conditions on event stream / actions - Out-of-order events - -TODO (In scope but needs a bit more work): - Published room API: support searching remote HSes. - - Device management (see Account management; s/access token/device ID/g? + - Account management: specifically the concept of devices so push works. - Multiple devices - Presence status unioning: is partially specced (needs more eyes). - Syncing data between multiple devices: is specced. - TODO: Push for offline devices. - - Handling "duplicate" events in state/messages key on initial sync. - - Have a compact=true flag. - - Have an events = { "event_id": , ... } per room - - messages/state keys just have an array of event IDs. - - PATCHing power levels - not over federation; just local HS. Spec that. Lower priority ~~~~~~~~~~~~~~ @@ -141,6 +134,16 @@ Outputs: Notes: - If a chunk token is applied, you will get a delta rather than all the rooms. +Compacting notes: + - Fixes the problem with the same event appearing in both the ``messages`` and + ``state`` keys. Represent as something like:: + + { + events: { event_id: Event, ... }, + messages: [ event_id, event_id, ...], + state: [ event_id, event_id, ...], + } + What data flows does it address: - Home screen: data required on load. @@ -148,7 +151,7 @@ What data flows does it address: Event Stream API ``[Draft]`` ---------------------------- Inputs: - - Position in the stream + - Position in the stream (chunk token) - Filter to apply: which event types, which room IDs, whether to get out-of-order events, which users to get presence/profile updates for - User ID @@ -156,7 +159,7 @@ Inputs: Outputs: - 0-N events the client hasn't seen. NB: Deleted state events will be missing a ``content`` key. Deleted message events are ``m.room.redaction`` events. - - New position in the stream. + - New position in the stream. (chunk token) State Events Ordering Notes: - Home servers may receive state events over federation that are superceded by state events previously sent to the client. The home server *cannot* send @@ -298,7 +301,7 @@ Additional Outputs: - Current room state at the end of the chunk as per initial sync. Room Alias API ``[Draft]`` -------------------------- +-------------------------- This provides mechanisms for creating and removing room aliases for a room on a home server. Typically, any user in a room can make an alias for that room. The alias creator (or anyone in the room?) can delete that alias. Server admins can @@ -364,7 +367,7 @@ Notes: User Profile API ``[Draft]`` ---------------------------- +---------------------------- Every user on a home server has a profile. This profile is effectively a key-value store scoped to a user ID. It can include an ``avatar_url``, ``displayname`` and other metadata. Updates to a profile should propagate to @@ -421,8 +424,12 @@ ordering information needs to be preserved as well. An improvement would be to allow the client to not automatically share updates of their profile information to all rooms. -Account Management API ``[Draft]`` ----------------------------------- +Account Management API ``[ONGOING]`` +------------------------------------ +.. NOTE:: + - How do device IDs fit into everything else? Namely, where do we tell the HS + what device ID we are? + Users may wish to delete their account, revoke access tokens, manage their devices, etc. This is achieved using an account management API. @@ -434,13 +441,14 @@ Inputs: Output: - None. -Viewing access tokens related to this account: +Viewing devices related to this account: Inputs: - User ID - Auth key (e.g. access_token of user, of server admin, etc) Output: - - A list of access tokens (+ last used / creation date / device / user-agent?) + - A list of devices (+ last used / access tokens / creation date / device / + user-agent?) Removing an access token: @@ -450,6 +458,17 @@ Inputs: - Access token to revoke. Output: - None. + +Removing a device: + +Inputs: + - User ID + - Auth key (e.g. access_token of user, of server admin, etc) + - Device ID to remove. +Output: + - None. +Notes: + - This revokes all access tokens linked to this device. Action APIs ----------- @@ -532,6 +551,22 @@ Notes: - This is represented on the event stream as an event lacking a ``content`` key (for symmetry with ``prev_content``) +Patching power levels ``[Draft`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Inputs: + - Room ID + - Key to update + - New value +Outputs: + - None. +Notes: + - This allows a single key on power levels to be updated e.g. specifying + ``kick`` as the key and ``60`` as the value to change the level required to + kick someone. + - The local HS will take the current ``m.room.power_levels`` event and set the + new key before sending it to other HSes *in its full form*. This means HSes + will not need to worry about partial power level events. + Knocking on a room ``[TODO]`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ If a room has the right ``join_rule`` e.g. ``knock``, then it should be able From 8fc472c13c34d8112301c0549f690b3348de3ee9 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Mon, 12 Jan 2015 13:29:48 +0000 Subject: [PATCH 45/95] Clarify rejected events better. --- drafts/general_api.rst | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drafts/general_api.rst b/drafts/general_api.rst index 0d5fde0c..50e55df2 100644 --- a/drafts/general_api.rst +++ b/drafts/general_api.rst @@ -184,7 +184,9 @@ Message Events Ordering Notes: after. Rejected events: - A home server may find out via federation that it should not have accepted - an event (e.g. to send a message/state event in a room). + an event (e.g. to send a message/state event in a room). For example, it may + send an event to another home server and receive an auth event stating + that the event should not have been sent. - If this happens, the home server will send a ``m.room.redaction`` for the event in question. This will be a local server event (not shared with other servers). @@ -551,8 +553,8 @@ Notes: - This is represented on the event stream as an event lacking a ``content`` key (for symmetry with ``prev_content``) -Patching power levels ``[Draft`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Patching power levels ``[Draft]`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Inputs: - Room ID - Key to update From e2894045d503f206b571b6765bf28bf5b61b8786 Mon Sep 17 00:00:00 2001 From: Kegsay Date: Mon, 12 Jan 2015 15:52:19 +0000 Subject: [PATCH 46/95] Initial Sync: Ongoing > Draft with additional work Descoped some points to v2.1 as discussed irl. --- drafts/general_api.rst | 67 ++++++++++++++++++++++-------------------- 1 file changed, 35 insertions(+), 32 deletions(-) diff --git a/drafts/general_api.rst b/drafts/general_api.rst index 50e55df2..9762e946 100644 --- a/drafts/general_api.rst +++ b/drafts/general_api.rst @@ -9,33 +9,35 @@ Summary of changes from v1 Version 2.0 ----------- - - Event filtering (type/room/users, federation-style events) - - Incremental initial syncing - - Deleting state - - Race conditions on event stream / actions - - Out-of-order events - - Published room API: support searching remote HSes. - - Account management: specifically the concept of devices so push works. - - Multiple devices - - Presence status unioning: is partially specced (needs more eyes). - - Syncing data between multiple devices: is specced. - - TODO: Push for offline devices. +- Event filtering (type/room/users, federation-style events) +- Incremental initial syncing +- Deleting state +- Race conditions on event stream / actions +- Out-of-order events +- Published room API: support searching remote HSes. +- Account management: specifically the concept of devices so push works. +- Multiple devices + - Presence status unioning: is partially specced (needs more eyes). + - Syncing data between multiple devices: is specced. + - TODO: Push for offline devices. Lower priority ~~~~~~~~~~~~~~ - - Capabilities - - Editing/updating messages (updates key) - - Room alias API +- Capabilities +- Editing/updating messages (updates key) +- Room alias API Version 2.1 ----------- - - Comments (relates_to key) - - Contextual messages (view messages around an arbitrary message) - - Rejecting invites +- Comments (relates_to key) +- Contextual messages (view messages around an arbitrary message) +- Rejecting invites +- State event pagination (e.g. from initial sync) +- Initial sync pagination (e.g. X most recent rooms) Out of scope ------------ - - Searching messages +- Searching messages Version 2 API ============= @@ -106,24 +108,21 @@ Notes: allow APIs which use filtering to ALSO specifiy query parameters to tweak the filter. -Global initial sync API ``[ONGOING]`` +Global initial sync API ``[Draft]`` ------------------------------------- .. NOTE:: - - Will need some form of state event pagination like we have for message events - to handle large amounts of state events for a room. Need to think of the - consequences of this: you may not get a ``m.room.member`` for someone's - message and so cannot display their display name / avatar. Do we want to - provide pagination on an event type basis? - - Handle paginating initial sync results themselves (e.g. 10 most recent rooms) - - No need for state events under the 'state' key to have a ``prev_content``. - Can also apply some optimisations depending on the direction of travel when - scrolling back. - - Do we want to treat global / specific room initial syncs as separate entities? - Aren't they just a filter? + + v2.1: + - Will need some form of state event pagination like we have for message events + to handle large amounts of state events for a room. Need to think of the + consequences of this: you may not get a ``m.room.member`` for someone's + message and so cannot display their display name / avatar. Do we want to + provide pagination on an event type basis? + - Handle paginating initial sync results themselves (e.g. 10 most recent rooms) Inputs: - A way of identifying the user (e.g. access token, user ID, etc) - - Filter to apply + - Filter to apply (e.g. a single room ID for a 'room initial sync') - Chunk token (for incremental deltas) Outputs: - For each room the user is joined: @@ -143,7 +142,11 @@ Compacting notes: messages: [ event_id, event_id, ...], state: [ event_id, event_id, ...], } - +Duplicate content notes: + - For non-compacted state events, duplicate state events in the ``messages`` key need to + have a ``prev_content`` to correctly display the state change text. This is not required + for ``state`` key events, which just represent the *current* state and as such do not + need a ``prev_content``. Compacted state events will need to specify the ``prev_content``. What data flows does it address: - Home screen: data required on load. From d987afe3e226a58731fd741d5997f9d854edd812 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Tue, 13 Jan 2015 09:17:13 +0000 Subject: [PATCH 47/95] 80 characters --- drafts/general_api.rst | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/drafts/general_api.rst b/drafts/general_api.rst index 9762e946..b29605bd 100644 --- a/drafts/general_api.rst +++ b/drafts/general_api.rst @@ -44,10 +44,10 @@ Version 2 API Legend: - ``[TODO]``: API is not in this document yet. - - ``[ONGOING]``: API is proposed but needs more work. There are known issues to be - addressed. - - ``[Draft]``: API is proposed and has no outstanding issues to be addressed, but - needs more feedback. + - ``[ONGOING]``: API is proposed but needs more work. There are known issues + to be addressed. + - ``[Draft]``: API is proposed and has no outstanding issues to be addressed, + but needs more feedback. - ``[Final]``: The API has no outstanding issues. This contains the formal proposal for Matrix Client-Server API v2. This API @@ -113,12 +113,13 @@ Global initial sync API ``[Draft]`` .. NOTE:: v2.1: - - Will need some form of state event pagination like we have for message events - to handle large amounts of state events for a room. Need to think of the - consequences of this: you may not get a ``m.room.member`` for someone's + - Will need some form of state event pagination like we have for message + events to handle large amounts of state events for a room. Need to think of + the consequences of this: you may not get a ``m.room.member`` for someone's message and so cannot display their display name / avatar. Do we want to provide pagination on an event type basis? - - Handle paginating initial sync results themselves (e.g. 10 most recent rooms) + - Handle paginating initial sync results themselves (e.g. 10 most recent + rooms) Inputs: - A way of identifying the user (e.g. access token, user ID, etc) @@ -143,10 +144,11 @@ Compacting notes: state: [ event_id, event_id, ...], } Duplicate content notes: - - For non-compacted state events, duplicate state events in the ``messages`` key need to - have a ``prev_content`` to correctly display the state change text. This is not required - for ``state`` key events, which just represent the *current* state and as such do not - need a ``prev_content``. Compacted state events will need to specify the ``prev_content``. + - For non-compacted state events, duplicate state events in the ``messages`` + key need to have a ``prev_content`` to correctly display the state change + text. This is not required for ``state`` key events, which just represent + the *current* state and as such do not need a ``prev_content``. Compacted + state events will need to specify the ``prev_content``. What data flows does it address: - Home screen: data required on load. @@ -645,7 +647,8 @@ Inputs: - Room ID - Message contents - Action ID (optional) - - Whether the full event should be returned, or a compact version (default=full) + - Whether the full event should be returned, or a compact version + (default=full) Outputs: - The actual event sent incl content OR: - The extra keys added or keys modified e.g. 'content' from a policy server From d5099b2656961dbd24ac7bd9477ea5adea4c88a0 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Tue, 13 Jan 2015 09:51:53 +0000 Subject: [PATCH 48/95] Add session API. Add server-generated events. Moved 'Inviting' API from Final to ONGOING in light of issues brought up from PR comments and irl discussions. --- drafts/general_api.rst | 97 +++++++++++++++++++++++++++++++++++------- 1 file changed, 81 insertions(+), 16 deletions(-) diff --git a/drafts/general_api.rst b/drafts/general_api.rst index b29605bd..2e990155 100644 --- a/drafts/general_api.rst +++ b/drafts/general_api.rst @@ -513,8 +513,22 @@ in parallel. An example of a client which may not need the use of action IDs includes bots which operate using basic request/responses in a synchronous fashion. -Inviting a user ``[Final]`` +Inviting a user ``[ONGOING]`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.. NOTE:: + - Clients need to know *why* they are being invited (e.g. a ``reason`` key, + just like for kicks/bans). However, this opens up a spam vector where any + user can send any other user a string. Do we really want to do that? + - It may be useful to send other state information such as the room name, + topic, etc. How is this specified in this request? Does the inviter even + specify this, or is it a room config option which fields are shared? This + has a lot of parallels with the published room API which exposes some state + events. How do we prevent spam attacks via the room name/topic? + - The spam attack vector may be something we're just going to have to deal + with. Ultimately, we need to expose more data about the room. This data is + going to be set by the client. Compromises like "just give the event type" + don't really fix the problem "because.my.event.type.could.be.like.this". + Inputs: - User ID - Room ID @@ -722,6 +736,42 @@ Output: - A chunk of child events - A new chunk token for earlier child events. + +Session API ``[Draft]`` +----------------------- +See the "Session" section in "General client changes" for more information on +sessions. + +Starting a new session: + +Inputs: + - User ID + - Device ID + - Some sort of auth (e.g. ``access_token``) + - Desired presence status (e.g. "appear offline", "away") +Output: + - Session ID +Notes: + - This is an explicit endpoint for starting a new session. Clients typically + will not use this API to create a new session. + - Sessions will typically be started implicitly, whenever a session-less client + makes a new request to any API. + - Another form of identification e.g. ``access_token`` can be used to represent + the user ID / device ID combination. + - Presence is tied to the creation of a session. This endpoint can be used to + configure the starting presence of a new session, allowing the possibility + of an offline mode. + +Ending a session: + +Inputs: + - Session ID + - Some sort of auth (e.g. ``access_token``) +Output: + - None. +Notes: + - This is typically done when "going dark", e.g. closing an app. + Capabilities API ``[ONGOING]`` ------------------------------ .. NOTE:: @@ -846,20 +896,17 @@ General client changes These are changes which do not introduce new APIs, but are required for the new APIs in order to fix certain issues. -Sessions ``[ONGOING]`` -~~~~~~~~~~~~~~~~~~~~~~ -.. NOTE:: - - Offline mode? How does that work with sessions? Separate endpoint to say - "start a session only"? - -A session is a group of requests sent within a short amount of time by the same -client. Sessions time out after a short amount of time without any requests. -Starting a session is known as going "online". Its purpose is to wrap up the -expiry of presence and typing notifications into a clearer scope. A session -starts when the client makes any request. A session ends when the client doesn't -make a request for a particular amount of time (times out). A session can also -end when explicitly hitting a particular endpoint. This is known as going -"offline". +Sessions ``[Draft]`` +~~~~~~~~~~~~~~~~~~~~ + +A session is a group of requests sent by the same client. Sessions time out +after a short amount of time without any requests. Starting a session is known +as going "online". Its purpose is to wrap up the expiry of presence and typing +notifications into a clearer scope. A session starts when the client makes any +request. A session ends when the client doesn't make a request for a particular +amount of time (times out). A session can also end when explicitly hitting a +particular endpoint. This is known as going "offline". A session can also be +created by explicitly hitting a particular endpoint. When a session starts, a session ID is sent in response to the first request the client makes. This session ID should be sent in *all* subsequent requests. If @@ -867,7 +914,12 @@ the server expires a session and the client uses an old session ID, the server should fail the request with the old session ID and send a new session ID in response for the client to use. If the client receives a new session ID mid-session, it must re-establish its typing status and presence status, as they -are linked to the session ID. +are linked to the session ID. + +Lightweight clients who do not wish to manage their session can omit the +session ID on their requests. The home server MUST treat these requests as +coming from the active session in order to ensure that presence works correctly +for these simple clients. Action IDs ``[ONGOING]`` ~~~~~~~~~~~~~~~~~~~~~~~~ @@ -1007,4 +1059,17 @@ Events (breaking changes; event version 2) ``[Draft]`` the state key, etc. - s/user_id/sender/g given that home servers can send events, not just users. +Server-generated events ``[Draft]`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Home servers may want to send events to their local clients or to other home +servers e.g. for server status notifications. + +These events look like regular events but have a server domain name as the +``sender`` and not a user ID. This can be easily detected by clients by the +absence of a starting ``@``. + +Different types of events (e.g. EDUs, room EDUs) are detected in the same way +as normal events (as proposed in the ``Events`` section of this document). + + From 1c69ba974fea96018fe80c8d4e97797310421674 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Tue, 13 Jan 2015 13:20:25 +0000 Subject: [PATCH 49/95] some comments on general_api --- drafts/general_api.rst | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drafts/general_api.rst b/drafts/general_api.rst index 2e990155..8283a832 100644 --- a/drafts/general_api.rst +++ b/drafts/general_api.rst @@ -93,9 +93,9 @@ Inputs: - Whether you want federation-style event JSON - Whether you want coalesced ``updates`` events - Whether you want coalesced ``relates_to`` events (and the max # to coalesce) - - limit= param? + - limit= param? (XXX: probably not; this should be done in the query itself) - Which keys to return for events? e.g. no ``origin_server_ts`` if you don't - show timestamps + show timestamps (n.b. encrypted keys can't be filtered out) Outputs: - An opaque token which represents the inputs, the "filter token". Notes: @@ -132,8 +132,8 @@ Outputs: - max of limit= message events - room ID Notes: - - If a chunk token is applied, you will get a delta rather than all the - rooms. + - If a chunk token is applied, you will get a delta relative to the last request + performed with that streaming token rather than all the rooms. Compacting notes: - Fixes the problem with the same event appearing in both the ``messages`` and ``state`` keys. Represent as something like:: @@ -151,8 +151,8 @@ Duplicate content notes: state events will need to specify the ``prev_content``. What data flows does it address: - Home screen: data required on load. - - + - XXX: would this also be used for initially loading room history screens too? + Event Stream API ``[Draft]`` ---------------------------- Inputs: @@ -408,7 +408,7 @@ Inputs: - Which keys to retrieve Output: - The key/values specified. - + Propagation ~~~~~~~~~~~ The goals of propagation are: From e283d8e4ea4bf0fa3eae1218fe9830da36a2a280 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Tue, 13 Jan 2015 13:20:45 +0000 Subject: [PATCH 50/95] WIP http api definition for cs-v2 --- drafts/cs-v2-http-api.rst | 281 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 281 insertions(+) create mode 100644 drafts/cs-v2-http-api.rst diff --git a/drafts/cs-v2-http-api.rst b/drafts/cs-v2-http-api.rst new file mode 100644 index 00000000..f39d7998 --- /dev/null +++ b/drafts/cs-v2-http-api.rst @@ -0,0 +1,281 @@ +Client-Server v2 HTTP API Draft +=============================== + +Filter API +---------- + +The intersection of the parameters given defines the filter. Unions of filters +can be achieved by querying multiple filters in parallel. + +XXX: Filters describe both what data to query and how to present it, so could +perhaps be better described as 'queries' or even 'subscriptions'? + +XXX: Is this actually sensible - should we instead have ordering & pagination & +limits etc provided per-query rather than lost in the filter? Or you provide the +defaults in the filter definition, and then clobber them with query params. + +You can define a reusable filter with the below POST API. The resulting filter_id +can then be passed to methods which can be filter. + +You can only create filters for your own user (filters are namespaced per user +as they are defined per-user data, like profiles etc) + +TODO: excluding filters (e.g. filter out "org.matrix.neb.*") + +XXX: how do we transition between non-coalesced pagination and coalesced pagination for related_to/updates + +POST /user/{userId}/filter +{ + // selectors: (bluntly selecting on the unencrypted fields) + types: [ "m.*", "net.arasphere.*" ], // default: all + rooms: [ "!83wy7whi:matrix.org" ], // default: all + sender_ids: [ "@matthew:matrix.org" ], // default: all (e.g. for narrowing down presence, and stalker mode) + + // XXX: do we want this per-query; is it valid to reuse it? + // we probably don't need this as querying per-event-ID will be a parameter from a seperate API, + // with its own seperate filter token & pagination semantics. + event_ids: [ "$192318719:matrix.org" ], // default: all - useful for selecting relates_to data for a given event + + // parameters + + // 'format' gives the desired shape of the response + // federation = include the federation layer as well as the raw content of events. + // events = the plain events + format: "federation", + + // select specific specific fields of the event to be returned. + // N.B. you cannot guaranteeing filter content fields as they may be encrypted. + // e.g. selecting just event_id could be useful for doing server-side + // sorting/pagination/threading + select: [ "event_id", "origin_server_ts", "thread_id", "content", "content.body" ], + + // include bundled child-event updates + bundle_updates: true, + + // include bundled related events + bundle_relates_to: [ + { + relationship: "in_reply_to", + // As this is an optimisation to avoid having to explicitly select/paginate the + // related messages per-message, we have to include a limit here as if we were + // actually executing the query per-message for the initial result set. + // Limit gives the number of related events to bundle; the bundled events return chunk tokens + // to let you seperately paginate on them. + limit: 10, // maximum number of related events to bundle in the results of this filtered result set. + ancestors: true, // include all ancestors (default: true) + descendents: true, // include all descendents (default: true) + + + // need to support a sort criteria which reflects the linearised ordering of the relation graph + }, + ], + + // server-side sorting, so we can paginate serverside on a thin client. + // N.B. we can only order by unencrypted fields. + // N.B. clients will need to handle out-of-order messages intelligently + // N.B. subset of things you're allowed to sort by may be arbitrarily + restricted by the server impl (XXX: capabilities?) + // Servers must support the "timeline" ordering - which is linearised logical chronological ordering. + // XXX: should this be done per-request rather than per-filter? Given streaming APIs (like eventStream) + // will be limited to sorting via timeline due to causality... + sort: [ + // sort by sender, and then by the timeline + { + type: "sender_id", + dir: "asc", // default asc + }, + { + type: "timeline", + dir: "asc", + }, + ], +} + +200 OK +{ + "filter_id": "583e98c2d983", +} + + +Global initial sync API +----------------------- + +XXX: need much more concrete general API definition first + +GET /initialSync + +GET parameters:: + limit: maximum number of events + + + filter: filter_id (XXX: allow different filters per room?) + presence: true/false + compact: boolean (default false): factor out common events. + XXX: I *really* think this should be turned on by default --matthew + + +FIXME: example using v1 API just for compact proofing. Needs to be updated with streaming_token voodoo +200 OK +// where compact is false: +{ + // XXX: does "end" die now? + "end": "s72595_4483_1934", + + // global presence info. (XXX: should we only send content deltas if this is an delta initialsync - e.g. to avoid re-sending all avatar_urls?) + "presence": [{ + "content": { + "avatar_url": "http://matrix.tp.mu:8008/_matrix/content/QG1hdHRoZXc6dHAubXUOeJQMWFMvUdqdeLovZKsyaOT.aW1hZ2UvanBlZw==.jpeg", + "displayname": "Matthew Hodgson", + "last_active_ago": 368200528, + "presence": "online", + "user_id": "@matthew:tp.mu" + }, + "type": "m.presence" + }], + + "rooms": [{ + "membership": "join", + "messages": { + "chunk": [{ + "content": { + "avatar_url": "https://matrix.org/_matrix/content/QG1hdHRoZXc6bWF0cml4Lm9yZwxaesQWnqdynuXIYaRisFnZdG.aW1hZ2UvanBlZw==.jpeg", + "displayname": "Matthew", + "membership": "join" + }, + "event_id": "$1417731086506PgoVf:matrix.org", + "membership": "join", + "origin_server_ts": 1417731086795, + "prev_content": { + "avatar_url": "https://matrix.org/_matrix/content/QG1hdHRoZXc6bWF0cml4Lm9yZwxaesQWnqdynuXIYaRisFnZdG.aW1hZ2UvanBlZw==.jpeg", + "displayname": "Ara4n", + "membership": "join" + } + "prev_state": [["$1416420706925RVAWP:matrix.org", { + "sha256": "zVzi02R5aeO2HQDnybu1XuuyR6yBG8utLE/i1Sv8eyA" + } + ]], + "room_id": "!KrLWMLDnZAyTapqLWW:matrix.org", + "state_key": "@matthew:matrix.org", + "type": "m.room.member", + "user_id": "@matthew:matrix.org" + }], + "end": "s72595_4483_1934", + "start": "t67-41151_4483_1934" + }, + "room_id": "!KrLWMLDnZAyTapqLWW:matrix.org", + "state": [{ + "content": { + "avatar_url": "https://matrix.org/_matrix/content/QG1hdHRoZXc6bWF0cml4Lm9yZwxaesQWnqdynuXIYaRisFnZdG.aW1hZ2UvanBlZw==.jpeg", + "displayname": "Matthew", + "membership": "join" + }, + "event_id": "$1417731086506PgoVf:matrix.org", + "membership": "join", + "origin_server_ts": 1417731086795, + "room_id": "!KrLWMLDnZAyTapqLWW:matrix.org", + "state_key": "@matthew:matrix.org", + "type": "m.room.member", + "user_id": "@matthew:matrix.org" + }], + "visibility": "public" + }] +} + + +// where compact is true: +{ + "end": "s72595_4483_1934", + // global presence info + "presence": [{ + "content": { + "avatar_url": "http://matrix.tp.mu:8008/_matrix/content/QG1hdHRoZXc6dHAubXUOeJQMWFMvUdqdeLovZKsyaOT.aW1hZ2UvanBlZw==.jpeg", + "displayname": "Matthew Hodgson", + "last_active_ago": 368200528, + "presence": "online", + "user_id": "@matthew:tp.mu" + }, + "type": "m.presence" + }], + "rooms": [{ + "events": { + "$1417731086506PgoVf:matrix.org": { + "content": { + "avatar_url": "https://matrix.org/_matrix/content/QG1hdHRoZXc6bWF0cml4Lm9yZwxaesQWnqdynuXIYaRisFnZdG.aW1hZ2UvanBlZw==.jpeg", + "displayname": "Matthew", + "membership": "join" + }, + "membership": "join", + "origin_server_ts": 1417731086795, + "prev_state": [["$1416420706925RVAWP:matrix.org", { + "sha256": "zVzi02R5aeO2HQDnybu1XuuyR6yBG8utLE/i1Sv8eyA" + } + ]], + "room_id": "!KrLWMLDnZAyTapqLWW:matrix.org", + "state_key": "@matthew:matrix.org", + "type": "m.room.member", + "user_id": "@matthew:matrix.org" + } + }, + "membership": "join", + "messages": { + "chunk": [ "$1417731086506PgoVf:matrix.org" ], + "end": "s72595_4483_1934", + "start": "t67-41151_4483_1934" + }, + "room_id": "!KrLWMLDnZAyTapqLWW:matrix.org", + "state": [ "$1417731086506PgoVf:matrix.org" ], + "visibility": "public" + }] +} + +Event Stream API +---------------- + +Room Creation API +----------------- + +Joining API +----------- + +Scrollback API +-------------- + +Contextual windowing API +------------------------ + +Room Alias API +-------------- + +Room Directory API +------------------ + +User Profile API +---------------- + +Provides arbitrary per-user global state JSON storage with namespaced keys, +some of which have specific predefined serverside semantics. Keys must be named +(we don't support POSTing to anonymous key names) + +PUT /user/{userId}/data/m.displayname +PUT /user/{userId}/data/m.avatar_url +PUT /user/{userId}/data/m.contact_vcard +PUT /user/{userId}/data/net.arasphere.client.preferences + +Account Management API +---------------------- + +Actions API +----------- + +Presence API +------------ + +Typing API +---------- + +Relates_to pagination API +------------------------- + +Capabilities API +---------------- + From 91ad3924fc685e0a398d10b5739eb4568aa3c374 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Wed, 14 Jan 2015 01:06:21 +0000 Subject: [PATCH 51/95] initial http api proposal for initialsync, eventstream and history + our theoretical design workflow atm --- drafts/cs-v2-http-api.rst | 187 ++++++++++++++++++++++++++++++++----- drafts/design_workflow.rst | 14 +++ 2 files changed, 180 insertions(+), 21 deletions(-) create mode 100644 drafts/design_workflow.rst diff --git a/drafts/cs-v2-http-api.rst b/drafts/cs-v2-http-api.rst index f39d7998..d6d247eb 100644 --- a/drafts/cs-v2-http-api.rst +++ b/drafts/cs-v2-http-api.rst @@ -28,8 +28,8 @@ POST /user/{userId}/filter { // selectors: (bluntly selecting on the unencrypted fields) types: [ "m.*", "net.arasphere.*" ], // default: all - rooms: [ "!83wy7whi:matrix.org" ], // default: all - sender_ids: [ "@matthew:matrix.org" ], // default: all (e.g. for narrowing down presence, and stalker mode) + rooms: [ "!83wy7whi:matrix.org" ], // default: all (may be aliases or IDs. wildcards supported) + sender_ids: [ "@matthew:matrix.org" ], // default: all (e.g. for narrowing down presence, and stalker mode. wildcards supported) // XXX: do we want this per-query; is it valid to reuse it? // we probably don't need this as querying per-event-ID will be a parameter from a seperate API, @@ -49,7 +49,7 @@ POST /user/{userId}/filter // sorting/pagination/threading select: [ "event_id", "origin_server_ts", "thread_id", "content", "content.body" ], - // include bundled child-event updates + // include bundled child-event updates (default false) bundle_updates: true, // include bundled related events @@ -91,6 +91,7 @@ POST /user/{userId}/filter ], } +Returns: 200 OK { "filter_id": "583e98c2d983", @@ -100,28 +101,36 @@ POST /user/{userId}/filter Global initial sync API ----------------------- -XXX: need much more concrete general API definition first - GET /initialSync GET parameters:: - limit: maximum number of events - - - filter: filter_id (XXX: allow different filters per room?) - presence: true/false + limit: maximum number of events per room to return + sort: fieldname, direction (e.g. "sender_id,asc"). // default: "timeline,asc". may appear multiple times. + since: to request an incremental update (*not* pagination) since the specified chunk token + We call this 'since' rather than 'from' because it's not for pagination, + backfill: true/false (default true): do we want to pull in state from federation if we have less than events available for a room? + presence: true/false (default true): return presence info compact: boolean (default false): factor out common events. XXX: I *really* think this should be turned on by default --matthew - - -FIXME: example using v1 API just for compact proofing. Needs to be updated with streaming_token voodoo + filter: (XXX: allow different filters per room?) + # filter overrides: + filter_type: wildcard event type match e.g. "m.*": default, all. may appear multiple times. + filter_room: wildcard room id/name match e.g. "!83wy7whi:matrix.org": default, all. may appear multiple times. + filter_sender_id: wildcard sender id match e.g. "@matthew:matrix.org": default, all. may appear multiple times. + filter_event_id: event id to match e.g. "$192318719:matrix.org" // default, all: may appear multiple times + filter_format: "federation" or "events" + filter_select: event fields to return: default, all. may appear multiple times + filter_bundle_updates: true/false: default, false. bundle updates in events. + +// FIXME: kegan: how much does the v1 response actually change here? + +Returns: 200 OK // where compact is false: { - // XXX: does "end" die now? - "end": "s72595_4483_1934", + "end": "s72595_4483_1934", // the chunk token we pass to from= - // global presence info. (XXX: should we only send content deltas if this is an delta initialsync - e.g. to avoid re-sending all avatar_urls?) + // global presence info (if presence=true) "presence": [{ "content": { "avatar_url": "http://matrix.tp.mu:8008/_matrix/content/QG1hdHRoZXc6dHAubXUOeJQMWFMvUdqdeLovZKsyaOT.aW1hZ2UvanBlZw==.jpeg", @@ -135,7 +144,7 @@ FIXME: example using v1 API just for compact proofing. Needs to be updated with "rooms": [{ "membership": "join", - "messages": { + "eventStream": { // rename messages to eventstream as this is a list of all events, not just messages (non-state events) "chunk": [{ "content": { "avatar_url": "https://matrix.org/_matrix/content/QG1hdHRoZXc6bWF0cml4Lm9yZwxaesQWnqdynuXIYaRisFnZdG.aW1hZ2UvanBlZw==.jpeg", @@ -217,10 +226,10 @@ FIXME: example using v1 API just for compact proofing. Needs to be updated with } }, "membership": "join", - "messages": { + "eventStream": { // rename messages to eventstream as this is a list of all events, not just messages (non-state events) "chunk": [ "$1417731086506PgoVf:matrix.org" ], "end": "s72595_4483_1934", - "start": "t67-41151_4483_1934" + "start": "t67-41151_4483_1934" // XXX: do we need start? }, "room_id": "!KrLWMLDnZAyTapqLWW:matrix.org", "state": [ "$1417731086506PgoVf:matrix.org" ], @@ -231,17 +240,141 @@ FIXME: example using v1 API just for compact proofing. Needs to be updated with Event Stream API ---------------- +GET /eventStream +GET parameters:: + from: chunk token to continue streaming from (e.g. "end" given by initialsync) + filter*: as per initialSync (XXX: do we inherit this from the chunk token?) + // N.B. there is no limit or sort param here, as we get events in timeline order as fast as they come. + access_token: identifies both user and device + timeout: maximum time to poll before returning the request + presence: "offline" // optional parameter to tell the server not to interpret this as coming online + +XXX: this needs to be updated from v1. Presumably s/user_id/sender_id/? + +Returns: +200 OK + +// events precisely as per a room's eventStream key as returned by initialSync +// includes non-graph events like presence +{ + "chunk": [{ + "content": { + "avatar_url": "https://matrix.org/_matrix/content/QG1hdHRoZXc6bWF0cml4Lm9yZwxaesQWnqdynuXIYaRisFnZdG.aW1hZ2UvanBlZw==.jpeg", + "displayname": "Matthew", + "last_active_ago": 1241, + "presence": "online", + "user_id": "@matthew:matrix.org" + }, + "type": "m.presence" + }, { + "age": 2595, + "content": { + "body": "test", + "msgtype": "m.text" + }, + "event_id": "$14211894201675TMbmz:matrix.org", + "origin_server_ts": 1421189420147, + "room_id": "!cURbafjkfsMDVwdRDQ:matrix.org", + "type": "m.room.message", + "user_id": "@matthew:matrix.org" + }], + "end": "s75460_2478_981", + "start": "s75459_2477_981" // XXX: do we need start here? +} + Room Creation API ----------------- Joining API ----------- +Room History +------------ + Scrollback API --------------- +~~~~~~~~~~~~~~ + +GET /rooms//events +GET parameters:: + from: the chunk token to paginate from + Otherwise same as initialSync, except "compact", "since" and "presence" are not implemented + +Returns: +200 OK + +// events precisely as per a room's eventStream key as returned by initialSync +{ + "chunk": [{ + "age": 28153452, // XXX: age and origin_server_ts are redundant here surely + "content": { + "body": "but obviously the XSF believes XMPP is the One True Way", + "msgtype": "m.text" + }, + "event_id": "$1421165049511TJpDp:matrix.org", + "origin_server_ts": 1421165049435, + "room_id": "!cURbafjkfsMDVwdRDQ:matrix.org", + "type": "m.room.message", + "user_id": "@irc_Arathorn:matrix.org" + }, { + "age": 28167245, + "content": { + "body": "which is all fair enough", + "msgtype": "m.text" + }, + "event_id": "$1421165035510CBwsU:matrix.org", + "origin_server_ts": 1421165035643, + "room_id": "!cURbafjkfsMDVwdRDQ:matrix.org", + "type": "m.room.message", + "user_id": "@irc_Arathorn:matrix.org" + }], + "end": "t9571-74545_2470_979", + "start": "t9601-75400_2470_979" // XXX: don't we just need end here as we can only paginate one way? +} Contextual windowing API ------------------------- +~~~~~~~~~~~~~~~~~~~~~~~~ + +GET /events/ +GET parameters: + context: "before", "after" or "around" + Otherwise same as initialSync, except "since" and "presence" are not implemented + +Returns: +200 OK + +// the room in question, formatted exactly as a room entry returned by /initialSync +// with the event in question present in the list as determined by the context param +{ + "events": { + "$1417731086506PgoVf:matrix.org": { + "content": { + "avatar_url": "https://matrix.org/_matrix/content/QG1hdHRoZXc6bWF0cml4Lm9yZwxaesQWnqdynuXIYaRisFnZdG.aW1hZ2UvanBlZw==.jpeg", + "displayname": "Matthew", + "membership": "join" + }, + "membership": "join", + "origin_server_ts": 1417731086795, + "prev_state": [["$1416420706925RVAWP:matrix.org", { + "sha256": "zVzi02R5aeO2HQDnybu1XuuyR6yBG8utLE/i1Sv8eyA" + } + ]], + "room_id": "!KrLWMLDnZAyTapqLWW:matrix.org", + "state_key": "@matthew:matrix.org", + "type": "m.room.member", + "user_id": "@matthew:matrix.org" + } + }, + "membership": "join", + "eventStream": { + "chunk": [ "$1417731086506PgoVf:matrix.org" ], + "end": "s72595_4483_1934", + "start": "t67-41151_4483_1934" + }, + "room_id": "!KrLWMLDnZAyTapqLWW:matrix.org", + "state": [ "$1417731086506PgoVf:matrix.org" ], + "visibility": "public" +} + Room Alias API -------------- @@ -270,6 +403,18 @@ Actions API Presence API ------------ +PUT /user/{userId}/presence/m.status // set DND/asleep/on holiday etc - +// XXX: do we need to distinguish between internationalisable presets like DND +// and free-form textual status messages? +// XXX: should this be in /user/{userId}/data/m.status instead? +// what's actually the difference? surely status is no different to avatar +// updates in terms of needing to be pushed around + +PUT /device/{deviceId}/presence/m.presence // explicitly set online/idle/offline +// or /presence/device/{deviceId} + +// XXX: need to remember how to handle activity notifications + Typing API ---------- diff --git a/drafts/design_workflow.rst b/drafts/design_workflow.rst new file mode 100644 index 00000000..32a37655 --- /dev/null +++ b/drafts/design_workflow.rst @@ -0,0 +1,14 @@ +Matrix Spec Design Workflow +=========================== + +1. Write use cases + +2. Design data flows for use cases + +3. Design generic API (factoring out commonalities where possible) + +4. Design transport-specific API with justifications + +5. Formalise transport-specific API as swagger or similar + +6. Evolve the generic API design doc and transport-specific API into the actual spec. \ No newline at end of file From a1aaf781864fb1cda5d1d6aabf5d38f87990cf4d Mon Sep 17 00:00:00 2001 From: Kegsay Date: Wed, 14 Jan 2015 09:28:41 +0000 Subject: [PATCH 52/95] Typing API, Action ID, Event ordering changes Apply most changes discussed in https://github.com/matrix-org/matrix-doc/pull/4#issuecomment-69751210 - Revert typing API to be like v1 - Revert Action IDs to be like Transaction IDs are for v1. Keep the echo down the event stream though. - Add batching notes to some Action APIs and remove ordering by action ID. Sessions still need to scrapped. --- drafts/general_api.rst | 98 +++++++++--------------------------------- 1 file changed, 20 insertions(+), 78 deletions(-) diff --git a/drafts/general_api.rst b/drafts/general_api.rst index 8283a832..d69991fb 100644 --- a/drafts/general_api.rst +++ b/drafts/general_api.rst @@ -500,18 +500,19 @@ As a result, clients MUST be able to handle all possible orderings:: When a client sends a request, they can include an "action ID" so that they can match up the event in the event stream to the request which they made. This ID -is created by the client, and MUST be a monotonically increasing integer for -that client. This ID serves as a transaction ID for idempotency as well as a -sequence ID for ordering actions performed in parallel by that client. Events -for actions performed by a client in that client's event stream will include the -action ID the client submitted when making the request. The action ID will *not* -appear in other client's event streams. +is created by the client. This ID serves as a transaction ID for idempotency +as well as a marker to match the response when it appears in the event stream. +Events for actions performed by a client in that client's event stream will +include the action ID the client submitted when making the request. The +action ID will *not* appear in other client's event streams. Action IDs are optional and are only needed by clients that retransmit their -requests, or display local echo, or allow the submission of multiple requests -in parallel. An example of a client which may not need the use of action IDs -includes bots which operate using basic request/responses in a synchronous -fashion. +requests or display local echo. An example of a client which may not need the +use of action IDs includes bots which operate using basic request/responses +in a synchronous fashion. + +A client may wish to send multiple actions in parallel. The send event APIs +support sending multiple events in a batch. Inviting a user ``[ONGOING]`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -553,12 +554,14 @@ Notes: Sending state events ``[Final]`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Inputs: - - Event type - - State key + - Event type[s] + - State key[s] - Room ID - - Content + - Content[s] Outputs: - None. +Notes: + - A batching version of this API needs to be provided. Deleting state events ``[Draft]`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -685,6 +688,8 @@ Compact flag notes: - It sucks to have your own messages echoed back to you in response though. As a result, you can ask for a compact version which just sends down the keys which were added, e.g. timestamp and event ID. +Notes: + - A batching version of this API needs to be provided. Presence API ``[ONGOING]`` -------------------------- @@ -705,24 +710,13 @@ Outputs: - None. -Typing API ``[ONGOING]`` +Typing API ``[Final]`` ------------------------ .. NOTE:: - Linking the termination of typing events to the message itself, so you don't need to send two events and don't get flicker? -When in a session, a user can send a request stating that they are typing in a -room. They are no longer typing when either the session ends or they explicitly -send another request to say they are no longer typing. - -Inputs: - - Room ID - - Whether you are typing or not. -Output: - - None. -Notes: - - Typing will time out when the session ends. If a session is restarted, the - typing notification must be sent again. +The typing API remains unchanged from v1. Relates-to pagination API ``[Draft]`` ------------------------------------- @@ -736,42 +730,6 @@ Output: - A chunk of child events - A new chunk token for earlier child events. - -Session API ``[Draft]`` ------------------------ -See the "Session" section in "General client changes" for more information on -sessions. - -Starting a new session: - -Inputs: - - User ID - - Device ID - - Some sort of auth (e.g. ``access_token``) - - Desired presence status (e.g. "appear offline", "away") -Output: - - Session ID -Notes: - - This is an explicit endpoint for starting a new session. Clients typically - will not use this API to create a new session. - - Sessions will typically be started implicitly, whenever a session-less client - makes a new request to any API. - - Another form of identification e.g. ``access_token`` can be used to represent - the user ID / device ID combination. - - Presence is tied to the creation of a session. This endpoint can be used to - configure the starting presence of a new session, allowing the possibility - of an offline mode. - -Ending a session: - -Inputs: - - Session ID - - Some sort of auth (e.g. ``access_token``) -Output: - - None. -Notes: - - This is typically done when "going dark", e.g. closing an app. - Capabilities API ``[ONGOING]`` ------------------------------ .. NOTE:: @@ -920,22 +878,6 @@ Lightweight clients who do not wish to manage their session can omit the session ID on their requests. The home server MUST treat these requests as coming from the active session in order to ensure that presence works correctly for these simple clients. - -Action IDs ``[ONGOING]`` -~~~~~~~~~~~~~~~~~~~~~~~~ -.. NOTE:: - - HTTP Ordering: Blocking requests with higher seqnums is troublesome if there - is a max # of concurrent connections a client can have open. - - Session expiry: Do we really have to fonx the request if it was done with an - old session ID? - -Action IDs are scoped per session. The first action ID for a session should be -0. For each subsequent action request, the ID should be incremented by 1. It -should be reset to 0 when a new session starts. - -If the client sends an action request with a stale session ID, the home server -MUST fail the request and start a new session. The request needs to be failed -in order to avoid edge cases with incrementing action IDs. Updates (Events) ``[Draft]`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ From 8ca0c65fd383fdaf8cf0b5e1bffb15360f9b4898 Mon Sep 17 00:00:00 2001 From: Kegsay Date: Wed, 14 Jan 2015 10:44:47 +0000 Subject: [PATCH 53/95] RST Formatting for github --- drafts/cs-v2-http-api.rst | 555 ++++++++++++++++++++------------------ 1 file changed, 289 insertions(+), 266 deletions(-) diff --git a/drafts/cs-v2-http-api.rst b/drafts/cs-v2-http-api.rst index d6d247eb..c95db315 100644 --- a/drafts/cs-v2-http-api.rst +++ b/drafts/cs-v2-http-api.rst @@ -24,78 +24,84 @@ TODO: excluding filters (e.g. filter out "org.matrix.neb.*") XXX: how do we transition between non-coalesced pagination and coalesced pagination for related_to/updates -POST /user/{userId}/filter -{ - // selectors: (bluntly selecting on the unencrypted fields) - types: [ "m.*", "net.arasphere.*" ], // default: all - rooms: [ "!83wy7whi:matrix.org" ], // default: all (may be aliases or IDs. wildcards supported) - sender_ids: [ "@matthew:matrix.org" ], // default: all (e.g. for narrowing down presence, and stalker mode. wildcards supported) - - // XXX: do we want this per-query; is it valid to reuse it? - // we probably don't need this as querying per-event-ID will be a parameter from a seperate API, - // with its own seperate filter token & pagination semantics. - event_ids: [ "$192318719:matrix.org" ], // default: all - useful for selecting relates_to data for a given event - - // parameters - - // 'format' gives the desired shape of the response - // federation = include the federation layer as well as the raw content of events. - // events = the plain events - format: "federation", - - // select specific specific fields of the event to be returned. - // N.B. you cannot guaranteeing filter content fields as they may be encrypted. - // e.g. selecting just event_id could be useful for doing server-side - // sorting/pagination/threading - select: [ "event_id", "origin_server_ts", "thread_id", "content", "content.body" ], - - // include bundled child-event updates (default false) - bundle_updates: true, - - // include bundled related events - bundle_relates_to: [ - { - relationship: "in_reply_to", - // As this is an optimisation to avoid having to explicitly select/paginate the - // related messages per-message, we have to include a limit here as if we were - // actually executing the query per-message for the initial result set. - // Limit gives the number of related events to bundle; the bundled events return chunk tokens - // to let you seperately paginate on them. - limit: 10, // maximum number of related events to bundle in the results of this filtered result set. - ancestors: true, // include all ancestors (default: true) - descendents: true, // include all descendents (default: true) - - - // need to support a sort criteria which reflects the linearised ordering of the relation graph - }, - ], +:: + + POST /user/{userId}/filter + +.. code:: javascript - // server-side sorting, so we can paginate serverside on a thin client. - // N.B. we can only order by unencrypted fields. - // N.B. clients will need to handle out-of-order messages intelligently - // N.B. subset of things you're allowed to sort by may be arbitrarily - restricted by the server impl (XXX: capabilities?) - // Servers must support the "timeline" ordering - which is linearised logical chronological ordering. - // XXX: should this be done per-request rather than per-filter? Given streaming APIs (like eventStream) - // will be limited to sorting via timeline due to causality... - sort: [ - // sort by sender, and then by the timeline - { - type: "sender_id", - dir: "asc", // default asc - }, - { - type: "timeline", - dir: "asc", - }, - ], -} + { + // selectors: (bluntly selecting on the unencrypted fields) + types: [ "m.*", "net.arasphere.*" ], // default: all + rooms: [ "!83wy7whi:matrix.org" ], // default: all (may be aliases or IDs. wildcards supported) + sender_ids: [ "@matthew:matrix.org" ], // default: all (e.g. for narrowing down presence, and stalker mode. wildcards supported) + + // XXX: do we want this per-query; is it valid to reuse it? + // we probably don't need this as querying per-event-ID will be a parameter from a seperate API, + // with its own seperate filter token & pagination semantics. + event_ids: [ "$192318719:matrix.org" ], // default: all - useful for selecting relates_to data for a given event + + // parameters + + // 'format' gives the desired shape of the response + // federation = include the federation layer as well as the raw content of events. + // events = the plain events + format: "federation", + + // select specific specific fields of the event to be returned. + // N.B. you cannot guaranteeing filter content fields as they may be encrypted. + // e.g. selecting just event_id could be useful for doing server-side + // sorting/pagination/threading + select: [ "event_id", "origin_server_ts", "thread_id", "content", "content.body" ], + + // include bundled child-event updates (default false) + bundle_updates: true, + + // include bundled related events + bundle_relates_to: [ + { + relationship: "in_reply_to", + // As this is an optimisation to avoid having to explicitly select/paginate the + // related messages per-message, we have to include a limit here as if we were + // actually executing the query per-message for the initial result set. + // Limit gives the number of related events to bundle; the bundled events return chunk tokens + // to let you seperately paginate on them. + limit: 10, // maximum number of related events to bundle in the results of this filtered result set. + ancestors: true, // include all ancestors (default: true) + descendents: true, // include all descendents (default: true) + + + // need to support a sort criteria which reflects the linearised ordering of the relation graph + }, + ], + + // server-side sorting, so we can paginate serverside on a thin client. + // N.B. we can only order by unencrypted fields. + // N.B. clients will need to handle out-of-order messages intelligently + // N.B. subset of things you're allowed to sort by may be arbitrarily + // restricted by the server impl (XXX: capabilities?) + // Servers must support the "timeline" ordering - which is linearised logical chronological ordering. + // XXX: should this be done per-request rather than per-filter? Given streaming APIs (like eventStream) + // will be limited to sorting via timeline due to causality... + sort: [ + // sort by sender, and then by the timeline + { + type: "sender_id", + dir: "asc", // default asc + }, + { + type: "timeline", + dir: "asc", + }, + ], + } -Returns: -200 OK -{ - "filter_id": "583e98c2d983", -} +Returns:: + + 200 OK + { + "filter_id": "583e98c2d983" + } Global initial sync API @@ -104,6 +110,7 @@ Global initial sync API GET /initialSync GET parameters:: + limit: maximum number of events per room to return sort: fieldname, direction (e.g. "sender_id,asc"). // default: "timeline,asc". may appear multiple times. since: to request an incremental update (*not* pagination) since the specified chunk token @@ -122,30 +129,60 @@ GET parameters:: filter_select: event fields to return: default, all. may appear multiple times filter_bundle_updates: true/false: default, false. bundle updates in events. -// FIXME: kegan: how much does the v1 response actually change here? + // FIXME: kegan: how much does the v1 response actually change here? Returns: -200 OK -// where compact is false: -{ - "end": "s72595_4483_1934", // the chunk token we pass to from= - - // global presence info (if presence=true) - "presence": [{ - "content": { - "avatar_url": "http://matrix.tp.mu:8008/_matrix/content/QG1hdHRoZXc6dHAubXUOeJQMWFMvUdqdeLovZKsyaOT.aW1hZ2UvanBlZw==.jpeg", - "displayname": "Matthew Hodgson", - "last_active_ago": 368200528, - "presence": "online", - "user_id": "@matthew:tp.mu" - }, - "type": "m.presence" - }], - - "rooms": [{ - "membership": "join", - "eventStream": { // rename messages to eventstream as this is a list of all events, not just messages (non-state events) - "chunk": [{ + +.. code:: javascript + + 200 OK + // where compact is false: + { + "end": "s72595_4483_1934", // the chunk token we pass to from= + + // global presence info (if presence=true) + "presence": [{ + "content": { + "avatar_url": "http://matrix.tp.mu:8008/_matrix/content/QG1hdHRoZXc6dHAubXUOeJQMWFMvUdqdeLovZKsyaOT.aW1hZ2UvanBlZw==.jpeg", + "displayname": "Matthew Hodgson", + "last_active_ago": 368200528, + "presence": "online", + "user_id": "@matthew:tp.mu" + }, + "type": "m.presence" + }], + + "rooms": [{ + "membership": "join", + "eventStream": { // rename messages to eventstream as this is a list of all events, not just messages (non-state events) + "chunk": [{ + "content": { + "avatar_url": "https://matrix.org/_matrix/content/QG1hdHRoZXc6bWF0cml4Lm9yZwxaesQWnqdynuXIYaRisFnZdG.aW1hZ2UvanBlZw==.jpeg", + "displayname": "Matthew", + "membership": "join" + }, + "event_id": "$1417731086506PgoVf:matrix.org", + "membership": "join", + "origin_server_ts": 1417731086795, + "prev_content": { + "avatar_url": "https://matrix.org/_matrix/content/QG1hdHRoZXc6bWF0cml4Lm9yZwxaesQWnqdynuXIYaRisFnZdG.aW1hZ2UvanBlZw==.jpeg", + "displayname": "Ara4n", + "membership": "join" + } + "prev_state": [["$1416420706925RVAWP:matrix.org", { + "sha256": "zVzi02R5aeO2HQDnybu1XuuyR6yBG8utLE/i1Sv8eyA" + } + ]], + "room_id": "!KrLWMLDnZAyTapqLWW:matrix.org", + "state_key": "@matthew:matrix.org", + "type": "m.room.member", + "user_id": "@matthew:matrix.org" + }], + "end": "s72595_4483_1934", + "start": "t67-41151_4483_1934" + }, + "room_id": "!KrLWMLDnZAyTapqLWW:matrix.org", + "state": [{ "content": { "avatar_url": "https://matrix.org/_matrix/content/QG1hdHRoZXc6bWF0cml4Lm9yZwxaesQWnqdynuXIYaRisFnZdG.aW1hZ2UvanBlZw==.jpeg", "displayname": "Matthew", @@ -154,94 +191,68 @@ Returns: "event_id": "$1417731086506PgoVf:matrix.org", "membership": "join", "origin_server_ts": 1417731086795, - "prev_content": { - "avatar_url": "https://matrix.org/_matrix/content/QG1hdHRoZXc6bWF0cml4Lm9yZwxaesQWnqdynuXIYaRisFnZdG.aW1hZ2UvanBlZw==.jpeg", - "displayname": "Ara4n", - "membership": "join" - } - "prev_state": [["$1416420706925RVAWP:matrix.org", { - "sha256": "zVzi02R5aeO2HQDnybu1XuuyR6yBG8utLE/i1Sv8eyA" - } - ]], "room_id": "!KrLWMLDnZAyTapqLWW:matrix.org", "state_key": "@matthew:matrix.org", "type": "m.room.member", "user_id": "@matthew:matrix.org" }], - "end": "s72595_4483_1934", - "start": "t67-41151_4483_1934" - }, - "room_id": "!KrLWMLDnZAyTapqLWW:matrix.org", - "state": [{ + "visibility": "public" + }] + } + + + // where compact is true: + { + "end": "s72595_4483_1934", + // global presence info + "presence": [{ "content": { - "avatar_url": "https://matrix.org/_matrix/content/QG1hdHRoZXc6bWF0cml4Lm9yZwxaesQWnqdynuXIYaRisFnZdG.aW1hZ2UvanBlZw==.jpeg", - "displayname": "Matthew", - "membership": "join" + "avatar_url": "http://matrix.tp.mu:8008/_matrix/content/QG1hdHRoZXc6dHAubXUOeJQMWFMvUdqdeLovZKsyaOT.aW1hZ2UvanBlZw==.jpeg", + "displayname": "Matthew Hodgson", + "last_active_ago": 368200528, + "presence": "online", + "user_id": "@matthew:tp.mu" }, - "event_id": "$1417731086506PgoVf:matrix.org", - "membership": "join", - "origin_server_ts": 1417731086795, - "room_id": "!KrLWMLDnZAyTapqLWW:matrix.org", - "state_key": "@matthew:matrix.org", - "type": "m.room.member", - "user_id": "@matthew:matrix.org" + "type": "m.presence" }], - "visibility": "public" - }] -} - - -// where compact is true: -{ - "end": "s72595_4483_1934", - // global presence info - "presence": [{ - "content": { - "avatar_url": "http://matrix.tp.mu:8008/_matrix/content/QG1hdHRoZXc6dHAubXUOeJQMWFMvUdqdeLovZKsyaOT.aW1hZ2UvanBlZw==.jpeg", - "displayname": "Matthew Hodgson", - "last_active_ago": 368200528, - "presence": "online", - "user_id": "@matthew:tp.mu" - }, - "type": "m.presence" - }], - "rooms": [{ - "events": { - "$1417731086506PgoVf:matrix.org": { - "content": { - "avatar_url": "https://matrix.org/_matrix/content/QG1hdHRoZXc6bWF0cml4Lm9yZwxaesQWnqdynuXIYaRisFnZdG.aW1hZ2UvanBlZw==.jpeg", - "displayname": "Matthew", - "membership": "join" - }, - "membership": "join", - "origin_server_ts": 1417731086795, - "prev_state": [["$1416420706925RVAWP:matrix.org", { - "sha256": "zVzi02R5aeO2HQDnybu1XuuyR6yBG8utLE/i1Sv8eyA" + "rooms": [{ + "events": { + "$1417731086506PgoVf:matrix.org": { + "content": { + "avatar_url": "https://matrix.org/_matrix/content/QG1hdHRoZXc6bWF0cml4Lm9yZwxaesQWnqdynuXIYaRisFnZdG.aW1hZ2UvanBlZw==.jpeg", + "displayname": "Matthew", + "membership": "join" + }, + "membership": "join", + "origin_server_ts": 1417731086795, + "prev_state": [["$1416420706925RVAWP:matrix.org", { + "sha256": "zVzi02R5aeO2HQDnybu1XuuyR6yBG8utLE/i1Sv8eyA" + } + ]], + "room_id": "!KrLWMLDnZAyTapqLWW:matrix.org", + "state_key": "@matthew:matrix.org", + "type": "m.room.member", + "user_id": "@matthew:matrix.org" } - ]], - "room_id": "!KrLWMLDnZAyTapqLWW:matrix.org", - "state_key": "@matthew:matrix.org", - "type": "m.room.member", - "user_id": "@matthew:matrix.org" - } - }, - "membership": "join", - "eventStream": { // rename messages to eventstream as this is a list of all events, not just messages (non-state events) - "chunk": [ "$1417731086506PgoVf:matrix.org" ], - "end": "s72595_4483_1934", - "start": "t67-41151_4483_1934" // XXX: do we need start? - }, - "room_id": "!KrLWMLDnZAyTapqLWW:matrix.org", - "state": [ "$1417731086506PgoVf:matrix.org" ], - "visibility": "public" - }] -} + }, + "membership": "join", + "eventStream": { // rename messages to eventstream as this is a list of all events, not just messages (non-state events) + "chunk": [ "$1417731086506PgoVf:matrix.org" ], + "end": "s72595_4483_1934", + "start": "t67-41151_4483_1934" // XXX: do we need start? + }, + "room_id": "!KrLWMLDnZAyTapqLWW:matrix.org", + "state": [ "$1417731086506PgoVf:matrix.org" ], + "visibility": "public" + }] + } Event Stream API ---------------- GET /eventStream GET parameters:: + from: chunk token to continue streaming from (e.g. "end" given by initialsync) filter*: as per initialSync (XXX: do we inherit this from the chunk token?) // N.B. there is no limit or sort param here, as we get events in timeline order as fast as they come. @@ -249,38 +260,40 @@ GET parameters:: timeout: maximum time to poll before returning the request presence: "offline" // optional parameter to tell the server not to interpret this as coming online -XXX: this needs to be updated from v1. Presumably s/user_id/sender_id/? + XXX: this needs to be updated from v1. Presumably s/user_id/sender_id/? Returns: 200 OK -// events precisely as per a room's eventStream key as returned by initialSync -// includes non-graph events like presence -{ - "chunk": [{ - "content": { - "avatar_url": "https://matrix.org/_matrix/content/QG1hdHRoZXc6bWF0cml4Lm9yZwxaesQWnqdynuXIYaRisFnZdG.aW1hZ2UvanBlZw==.jpeg", - "displayname": "Matthew", - "last_active_ago": 1241, - "presence": "online", +.. code:: javascript + + // events precisely as per a room's eventStream key as returned by initialSync + // includes non-graph events like presence + { + "chunk": [{ + "content": { + "avatar_url": "https://matrix.org/_matrix/content/QG1hdHRoZXc6bWF0cml4Lm9yZwxaesQWnqdynuXIYaRisFnZdG.aW1hZ2UvanBlZw==.jpeg", + "displayname": "Matthew", + "last_active_ago": 1241, + "presence": "online", + "user_id": "@matthew:matrix.org" + }, + "type": "m.presence" + }, { + "age": 2595, + "content": { + "body": "test", + "msgtype": "m.text" + }, + "event_id": "$14211894201675TMbmz:matrix.org", + "origin_server_ts": 1421189420147, + "room_id": "!cURbafjkfsMDVwdRDQ:matrix.org", + "type": "m.room.message", "user_id": "@matthew:matrix.org" - }, - "type": "m.presence" - }, { - "age": 2595, - "content": { - "body": "test", - "msgtype": "m.text" - }, - "event_id": "$14211894201675TMbmz:matrix.org", - "origin_server_ts": 1421189420147, - "room_id": "!cURbafjkfsMDVwdRDQ:matrix.org", - "type": "m.room.message", - "user_id": "@matthew:matrix.org" - }], - "end": "s75460_2478_981", - "start": "s75459_2477_981" // XXX: do we need start here? -} + }], + "end": "s75460_2478_981", + "start": "s75459_2477_981" // XXX: do we need start here? + } Room Creation API ----------------- @@ -296,84 +309,90 @@ Scrollback API GET /rooms//events GET parameters:: + from: the chunk token to paginate from Otherwise same as initialSync, except "compact", "since" and "presence" are not implemented Returns: 200 OK -// events precisely as per a room's eventStream key as returned by initialSync -{ - "chunk": [{ - "age": 28153452, // XXX: age and origin_server_ts are redundant here surely - "content": { - "body": "but obviously the XSF believes XMPP is the One True Way", - "msgtype": "m.text" - }, - "event_id": "$1421165049511TJpDp:matrix.org", - "origin_server_ts": 1421165049435, - "room_id": "!cURbafjkfsMDVwdRDQ:matrix.org", - "type": "m.room.message", - "user_id": "@irc_Arathorn:matrix.org" - }, { - "age": 28167245, - "content": { - "body": "which is all fair enough", - "msgtype": "m.text" - }, - "event_id": "$1421165035510CBwsU:matrix.org", - "origin_server_ts": 1421165035643, - "room_id": "!cURbafjkfsMDVwdRDQ:matrix.org", - "type": "m.room.message", - "user_id": "@irc_Arathorn:matrix.org" - }], - "end": "t9571-74545_2470_979", - "start": "t9601-75400_2470_979" // XXX: don't we just need end here as we can only paginate one way? -} +.. code:: javascript + + // events precisely as per a room's eventStream key as returned by initialSync + { + "chunk": [{ + "age": 28153452, // XXX: age and origin_server_ts are redundant here surely + "content": { + "body": "but obviously the XSF believes XMPP is the One True Way", + "msgtype": "m.text" + }, + "event_id": "$1421165049511TJpDp:matrix.org", + "origin_server_ts": 1421165049435, + "room_id": "!cURbafjkfsMDVwdRDQ:matrix.org", + "type": "m.room.message", + "user_id": "@irc_Arathorn:matrix.org" + }, { + "age": 28167245, + "content": { + "body": "which is all fair enough", + "msgtype": "m.text" + }, + "event_id": "$1421165035510CBwsU:matrix.org", + "origin_server_ts": 1421165035643, + "room_id": "!cURbafjkfsMDVwdRDQ:matrix.org", + "type": "m.room.message", + "user_id": "@irc_Arathorn:matrix.org" + }], + "end": "t9571-74545_2470_979", + "start": "t9601-75400_2470_979" // XXX: don't we just need end here as we can only paginate one way? + } Contextual windowing API ~~~~~~~~~~~~~~~~~~~~~~~~ GET /events/ -GET parameters: +GET parameters:: + context: "before", "after" or "around" Otherwise same as initialSync, except "since" and "presence" are not implemented Returns: 200 OK -// the room in question, formatted exactly as a room entry returned by /initialSync -// with the event in question present in the list as determined by the context param -{ - "events": { - "$1417731086506PgoVf:matrix.org": { - "content": { - "avatar_url": "https://matrix.org/_matrix/content/QG1hdHRoZXc6bWF0cml4Lm9yZwxaesQWnqdynuXIYaRisFnZdG.aW1hZ2UvanBlZw==.jpeg", - "displayname": "Matthew", - "membership": "join" - }, - "membership": "join", - "origin_server_ts": 1417731086795, - "prev_state": [["$1416420706925RVAWP:matrix.org", { - "sha256": "zVzi02R5aeO2HQDnybu1XuuyR6yBG8utLE/i1Sv8eyA" +.. code:: javascript + + // the room in question, formatted exactly as a room entry returned by /initialSync + // with the event in question present in the list as determined by the context param + { + "events": { + "$1417731086506PgoVf:matrix.org": { + "content": { + "avatar_url": "https://matrix.org/_matrix/content/QG1hdHRoZXc6bWF0cml4Lm9yZwxaesQWnqdynuXIYaRisFnZdG.aW1hZ2UvanBlZw==.jpeg", + "displayname": "Matthew", + "membership": "join" + }, + "membership": "join", + "origin_server_ts": 1417731086795, + "prev_state": [["$1416420706925RVAWP:matrix.org", { + "sha256": "zVzi02R5aeO2HQDnybu1XuuyR6yBG8utLE/i1Sv8eyA" + } + ]], + "room_id": "!KrLWMLDnZAyTapqLWW:matrix.org", + "state_key": "@matthew:matrix.org", + "type": "m.room.member", + "user_id": "@matthew:matrix.org" } - ]], - "room_id": "!KrLWMLDnZAyTapqLWW:matrix.org", - "state_key": "@matthew:matrix.org", - "type": "m.room.member", - "user_id": "@matthew:matrix.org" - } - }, - "membership": "join", - "eventStream": { - "chunk": [ "$1417731086506PgoVf:matrix.org" ], - "end": "s72595_4483_1934", - "start": "t67-41151_4483_1934" - }, - "room_id": "!KrLWMLDnZAyTapqLWW:matrix.org", - "state": [ "$1417731086506PgoVf:matrix.org" ], - "visibility": "public" -} + }, + "membership": "join", + "eventStream": { + "chunk": [ "$1417731086506PgoVf:matrix.org" ], + "end": "s72595_4483_1934", + "start": "t67-41151_4483_1934" + }, + "room_id": "!KrLWMLDnZAyTapqLWW:matrix.org", + "state": [ "$1417731086506PgoVf:matrix.org" ], + "visibility": "public" + } Room Alias API @@ -389,10 +408,12 @@ Provides arbitrary per-user global state JSON storage with namespaced keys, some of which have specific predefined serverside semantics. Keys must be named (we don't support POSTing to anonymous key names) -PUT /user/{userId}/data/m.displayname -PUT /user/{userId}/data/m.avatar_url -PUT /user/{userId}/data/m.contact_vcard -PUT /user/{userId}/data/net.arasphere.client.preferences +:: + + PUT /user/{userId}/data/m.displayname + PUT /user/{userId}/data/m.avatar_url + PUT /user/{userId}/data/m.contact_vcard + PUT /user/{userId}/data/net.arasphere.client.preferences Account Management API ---------------------- @@ -403,17 +424,19 @@ Actions API Presence API ------------ -PUT /user/{userId}/presence/m.status // set DND/asleep/on holiday etc - -// XXX: do we need to distinguish between internationalisable presets like DND -// and free-form textual status messages? -// XXX: should this be in /user/{userId}/data/m.status instead? -// what's actually the difference? surely status is no different to avatar -// updates in terms of needing to be pushed around - -PUT /device/{deviceId}/presence/m.presence // explicitly set online/idle/offline -// or /presence/device/{deviceId} +:: -// XXX: need to remember how to handle activity notifications + PUT /user/{userId}/presence/m.status // set DND/asleep/on holiday etc - + // XXX: do we need to distinguish between internationalisable presets like DND + // and free-form textual status messages? + // XXX: should this be in /user/{userId}/data/m.status instead? + // what's actually the difference? surely status is no different to avatar + // updates in terms of needing to be pushed around + + PUT /device/{deviceId}/presence/m.presence // explicitly set online/idle/offline + // or /presence/device/{deviceId} + + // XXX: need to remember how to handle activity notifications Typing API ---------- From 4b23a16abb4c1c4fec4cc622b3167ff48394f5e0 Mon Sep 17 00:00:00 2001 From: Kegsay Date: Wed, 14 Jan 2015 10:53:49 +0000 Subject: [PATCH 54/95] Few minor RST tweaks --- drafts/cs-v2-http-api.rst | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/drafts/cs-v2-http-api.rst b/drafts/cs-v2-http-api.rst index c95db315..adfd4767 100644 --- a/drafts/cs-v2-http-api.rst +++ b/drafts/cs-v2-http-api.rst @@ -96,9 +96,8 @@ XXX: how do we transition between non-coalesced pagination and coalesced paginat ], } -Returns:: - - 200 OK +Returns ``200 OK``:: + { "filter_id": "583e98c2d983" } @@ -107,7 +106,7 @@ Returns:: Global initial sync API ----------------------- -GET /initialSync +``GET /initialSync`` GET parameters:: @@ -131,11 +130,10 @@ GET parameters:: // FIXME: kegan: how much does the v1 response actually change here? -Returns: +Returns ``200 OK``: .. code:: javascript - - 200 OK + // where compact is false: { "end": "s72595_4483_1934", // the chunk token we pass to from= @@ -250,7 +248,7 @@ Returns: Event Stream API ---------------- -GET /eventStream +GET ``/eventStream`` GET parameters:: from: chunk token to continue streaming from (e.g. "end" given by initialsync) @@ -262,8 +260,7 @@ GET parameters:: XXX: this needs to be updated from v1. Presumably s/user_id/sender_id/? -Returns: -200 OK +Returns ``200 OK``: .. code:: javascript @@ -307,14 +304,16 @@ Room History Scrollback API ~~~~~~~~~~~~~~ -GET /rooms//events +:: + + GET /rooms//events + GET parameters:: from: the chunk token to paginate from Otherwise same as initialSync, except "compact", "since" and "presence" are not implemented -Returns: -200 OK +Returns ``200 OK``: .. code:: javascript @@ -349,15 +348,16 @@ Returns: Contextual windowing API ~~~~~~~~~~~~~~~~~~~~~~~~ +:: + + GET /events/ -GET /events/ GET parameters:: context: "before", "after" or "around" Otherwise same as initialSync, except "since" and "presence" are not implemented -Returns: -200 OK +Returns ``200 OK``: .. code:: javascript From ee4116561001cefc463882920e2d66f848c8a8ab Mon Sep 17 00:00:00 2001 From: Mark Haines Date: Wed, 14 Jan 2015 11:57:07 +0000 Subject: [PATCH 55/95] Remove cs-v2-http-api since it's in the client_server_v2_http_api branch now --- drafts/cs-v2-http-api.rst | 449 -------------------------------------- 1 file changed, 449 deletions(-) delete mode 100644 drafts/cs-v2-http-api.rst diff --git a/drafts/cs-v2-http-api.rst b/drafts/cs-v2-http-api.rst deleted file mode 100644 index adfd4767..00000000 --- a/drafts/cs-v2-http-api.rst +++ /dev/null @@ -1,449 +0,0 @@ -Client-Server v2 HTTP API Draft -=============================== - -Filter API ----------- - -The intersection of the parameters given defines the filter. Unions of filters -can be achieved by querying multiple filters in parallel. - -XXX: Filters describe both what data to query and how to present it, so could -perhaps be better described as 'queries' or even 'subscriptions'? - -XXX: Is this actually sensible - should we instead have ordering & pagination & -limits etc provided per-query rather than lost in the filter? Or you provide the -defaults in the filter definition, and then clobber them with query params. - -You can define a reusable filter with the below POST API. The resulting filter_id -can then be passed to methods which can be filter. - -You can only create filters for your own user (filters are namespaced per user -as they are defined per-user data, like profiles etc) - -TODO: excluding filters (e.g. filter out "org.matrix.neb.*") - -XXX: how do we transition between non-coalesced pagination and coalesced pagination for related_to/updates - -:: - - POST /user/{userId}/filter - -.. code:: javascript - - { - // selectors: (bluntly selecting on the unencrypted fields) - types: [ "m.*", "net.arasphere.*" ], // default: all - rooms: [ "!83wy7whi:matrix.org" ], // default: all (may be aliases or IDs. wildcards supported) - sender_ids: [ "@matthew:matrix.org" ], // default: all (e.g. for narrowing down presence, and stalker mode. wildcards supported) - - // XXX: do we want this per-query; is it valid to reuse it? - // we probably don't need this as querying per-event-ID will be a parameter from a seperate API, - // with its own seperate filter token & pagination semantics. - event_ids: [ "$192318719:matrix.org" ], // default: all - useful for selecting relates_to data for a given event - - // parameters - - // 'format' gives the desired shape of the response - // federation = include the federation layer as well as the raw content of events. - // events = the plain events - format: "federation", - - // select specific specific fields of the event to be returned. - // N.B. you cannot guaranteeing filter content fields as they may be encrypted. - // e.g. selecting just event_id could be useful for doing server-side - // sorting/pagination/threading - select: [ "event_id", "origin_server_ts", "thread_id", "content", "content.body" ], - - // include bundled child-event updates (default false) - bundle_updates: true, - - // include bundled related events - bundle_relates_to: [ - { - relationship: "in_reply_to", - // As this is an optimisation to avoid having to explicitly select/paginate the - // related messages per-message, we have to include a limit here as if we were - // actually executing the query per-message for the initial result set. - // Limit gives the number of related events to bundle; the bundled events return chunk tokens - // to let you seperately paginate on them. - limit: 10, // maximum number of related events to bundle in the results of this filtered result set. - ancestors: true, // include all ancestors (default: true) - descendents: true, // include all descendents (default: true) - - - // need to support a sort criteria which reflects the linearised ordering of the relation graph - }, - ], - - // server-side sorting, so we can paginate serverside on a thin client. - // N.B. we can only order by unencrypted fields. - // N.B. clients will need to handle out-of-order messages intelligently - // N.B. subset of things you're allowed to sort by may be arbitrarily - // restricted by the server impl (XXX: capabilities?) - // Servers must support the "timeline" ordering - which is linearised logical chronological ordering. - // XXX: should this be done per-request rather than per-filter? Given streaming APIs (like eventStream) - // will be limited to sorting via timeline due to causality... - sort: [ - // sort by sender, and then by the timeline - { - type: "sender_id", - dir: "asc", // default asc - }, - { - type: "timeline", - dir: "asc", - }, - ], - } - -Returns ``200 OK``:: - - { - "filter_id": "583e98c2d983" - } - - -Global initial sync API ------------------------ - -``GET /initialSync`` - -GET parameters:: - - limit: maximum number of events per room to return - sort: fieldname, direction (e.g. "sender_id,asc"). // default: "timeline,asc". may appear multiple times. - since: to request an incremental update (*not* pagination) since the specified chunk token - We call this 'since' rather than 'from' because it's not for pagination, - backfill: true/false (default true): do we want to pull in state from federation if we have less than events available for a room? - presence: true/false (default true): return presence info - compact: boolean (default false): factor out common events. - XXX: I *really* think this should be turned on by default --matthew - filter: (XXX: allow different filters per room?) - # filter overrides: - filter_type: wildcard event type match e.g. "m.*": default, all. may appear multiple times. - filter_room: wildcard room id/name match e.g. "!83wy7whi:matrix.org": default, all. may appear multiple times. - filter_sender_id: wildcard sender id match e.g. "@matthew:matrix.org": default, all. may appear multiple times. - filter_event_id: event id to match e.g. "$192318719:matrix.org" // default, all: may appear multiple times - filter_format: "federation" or "events" - filter_select: event fields to return: default, all. may appear multiple times - filter_bundle_updates: true/false: default, false. bundle updates in events. - - // FIXME: kegan: how much does the v1 response actually change here? - -Returns ``200 OK``: - -.. code:: javascript - - // where compact is false: - { - "end": "s72595_4483_1934", // the chunk token we pass to from= - - // global presence info (if presence=true) - "presence": [{ - "content": { - "avatar_url": "http://matrix.tp.mu:8008/_matrix/content/QG1hdHRoZXc6dHAubXUOeJQMWFMvUdqdeLovZKsyaOT.aW1hZ2UvanBlZw==.jpeg", - "displayname": "Matthew Hodgson", - "last_active_ago": 368200528, - "presence": "online", - "user_id": "@matthew:tp.mu" - }, - "type": "m.presence" - }], - - "rooms": [{ - "membership": "join", - "eventStream": { // rename messages to eventstream as this is a list of all events, not just messages (non-state events) - "chunk": [{ - "content": { - "avatar_url": "https://matrix.org/_matrix/content/QG1hdHRoZXc6bWF0cml4Lm9yZwxaesQWnqdynuXIYaRisFnZdG.aW1hZ2UvanBlZw==.jpeg", - "displayname": "Matthew", - "membership": "join" - }, - "event_id": "$1417731086506PgoVf:matrix.org", - "membership": "join", - "origin_server_ts": 1417731086795, - "prev_content": { - "avatar_url": "https://matrix.org/_matrix/content/QG1hdHRoZXc6bWF0cml4Lm9yZwxaesQWnqdynuXIYaRisFnZdG.aW1hZ2UvanBlZw==.jpeg", - "displayname": "Ara4n", - "membership": "join" - } - "prev_state": [["$1416420706925RVAWP:matrix.org", { - "sha256": "zVzi02R5aeO2HQDnybu1XuuyR6yBG8utLE/i1Sv8eyA" - } - ]], - "room_id": "!KrLWMLDnZAyTapqLWW:matrix.org", - "state_key": "@matthew:matrix.org", - "type": "m.room.member", - "user_id": "@matthew:matrix.org" - }], - "end": "s72595_4483_1934", - "start": "t67-41151_4483_1934" - }, - "room_id": "!KrLWMLDnZAyTapqLWW:matrix.org", - "state": [{ - "content": { - "avatar_url": "https://matrix.org/_matrix/content/QG1hdHRoZXc6bWF0cml4Lm9yZwxaesQWnqdynuXIYaRisFnZdG.aW1hZ2UvanBlZw==.jpeg", - "displayname": "Matthew", - "membership": "join" - }, - "event_id": "$1417731086506PgoVf:matrix.org", - "membership": "join", - "origin_server_ts": 1417731086795, - "room_id": "!KrLWMLDnZAyTapqLWW:matrix.org", - "state_key": "@matthew:matrix.org", - "type": "m.room.member", - "user_id": "@matthew:matrix.org" - }], - "visibility": "public" - }] - } - - - // where compact is true: - { - "end": "s72595_4483_1934", - // global presence info - "presence": [{ - "content": { - "avatar_url": "http://matrix.tp.mu:8008/_matrix/content/QG1hdHRoZXc6dHAubXUOeJQMWFMvUdqdeLovZKsyaOT.aW1hZ2UvanBlZw==.jpeg", - "displayname": "Matthew Hodgson", - "last_active_ago": 368200528, - "presence": "online", - "user_id": "@matthew:tp.mu" - }, - "type": "m.presence" - }], - "rooms": [{ - "events": { - "$1417731086506PgoVf:matrix.org": { - "content": { - "avatar_url": "https://matrix.org/_matrix/content/QG1hdHRoZXc6bWF0cml4Lm9yZwxaesQWnqdynuXIYaRisFnZdG.aW1hZ2UvanBlZw==.jpeg", - "displayname": "Matthew", - "membership": "join" - }, - "membership": "join", - "origin_server_ts": 1417731086795, - "prev_state": [["$1416420706925RVAWP:matrix.org", { - "sha256": "zVzi02R5aeO2HQDnybu1XuuyR6yBG8utLE/i1Sv8eyA" - } - ]], - "room_id": "!KrLWMLDnZAyTapqLWW:matrix.org", - "state_key": "@matthew:matrix.org", - "type": "m.room.member", - "user_id": "@matthew:matrix.org" - } - }, - "membership": "join", - "eventStream": { // rename messages to eventstream as this is a list of all events, not just messages (non-state events) - "chunk": [ "$1417731086506PgoVf:matrix.org" ], - "end": "s72595_4483_1934", - "start": "t67-41151_4483_1934" // XXX: do we need start? - }, - "room_id": "!KrLWMLDnZAyTapqLWW:matrix.org", - "state": [ "$1417731086506PgoVf:matrix.org" ], - "visibility": "public" - }] - } - -Event Stream API ----------------- - -GET ``/eventStream`` -GET parameters:: - - from: chunk token to continue streaming from (e.g. "end" given by initialsync) - filter*: as per initialSync (XXX: do we inherit this from the chunk token?) - // N.B. there is no limit or sort param here, as we get events in timeline order as fast as they come. - access_token: identifies both user and device - timeout: maximum time to poll before returning the request - presence: "offline" // optional parameter to tell the server not to interpret this as coming online - - XXX: this needs to be updated from v1. Presumably s/user_id/sender_id/? - -Returns ``200 OK``: - -.. code:: javascript - - // events precisely as per a room's eventStream key as returned by initialSync - // includes non-graph events like presence - { - "chunk": [{ - "content": { - "avatar_url": "https://matrix.org/_matrix/content/QG1hdHRoZXc6bWF0cml4Lm9yZwxaesQWnqdynuXIYaRisFnZdG.aW1hZ2UvanBlZw==.jpeg", - "displayname": "Matthew", - "last_active_ago": 1241, - "presence": "online", - "user_id": "@matthew:matrix.org" - }, - "type": "m.presence" - }, { - "age": 2595, - "content": { - "body": "test", - "msgtype": "m.text" - }, - "event_id": "$14211894201675TMbmz:matrix.org", - "origin_server_ts": 1421189420147, - "room_id": "!cURbafjkfsMDVwdRDQ:matrix.org", - "type": "m.room.message", - "user_id": "@matthew:matrix.org" - }], - "end": "s75460_2478_981", - "start": "s75459_2477_981" // XXX: do we need start here? - } - -Room Creation API ------------------ - -Joining API ------------ - -Room History ------------- - -Scrollback API -~~~~~~~~~~~~~~ - -:: - - GET /rooms//events - -GET parameters:: - - from: the chunk token to paginate from - Otherwise same as initialSync, except "compact", "since" and "presence" are not implemented - -Returns ``200 OK``: - -.. code:: javascript - - // events precisely as per a room's eventStream key as returned by initialSync - { - "chunk": [{ - "age": 28153452, // XXX: age and origin_server_ts are redundant here surely - "content": { - "body": "but obviously the XSF believes XMPP is the One True Way", - "msgtype": "m.text" - }, - "event_id": "$1421165049511TJpDp:matrix.org", - "origin_server_ts": 1421165049435, - "room_id": "!cURbafjkfsMDVwdRDQ:matrix.org", - "type": "m.room.message", - "user_id": "@irc_Arathorn:matrix.org" - }, { - "age": 28167245, - "content": { - "body": "which is all fair enough", - "msgtype": "m.text" - }, - "event_id": "$1421165035510CBwsU:matrix.org", - "origin_server_ts": 1421165035643, - "room_id": "!cURbafjkfsMDVwdRDQ:matrix.org", - "type": "m.room.message", - "user_id": "@irc_Arathorn:matrix.org" - }], - "end": "t9571-74545_2470_979", - "start": "t9601-75400_2470_979" // XXX: don't we just need end here as we can only paginate one way? - } - -Contextual windowing API -~~~~~~~~~~~~~~~~~~~~~~~~ -:: - - GET /events/ - -GET parameters:: - - context: "before", "after" or "around" - Otherwise same as initialSync, except "since" and "presence" are not implemented - -Returns ``200 OK``: - -.. code:: javascript - - // the room in question, formatted exactly as a room entry returned by /initialSync - // with the event in question present in the list as determined by the context param - { - "events": { - "$1417731086506PgoVf:matrix.org": { - "content": { - "avatar_url": "https://matrix.org/_matrix/content/QG1hdHRoZXc6bWF0cml4Lm9yZwxaesQWnqdynuXIYaRisFnZdG.aW1hZ2UvanBlZw==.jpeg", - "displayname": "Matthew", - "membership": "join" - }, - "membership": "join", - "origin_server_ts": 1417731086795, - "prev_state": [["$1416420706925RVAWP:matrix.org", { - "sha256": "zVzi02R5aeO2HQDnybu1XuuyR6yBG8utLE/i1Sv8eyA" - } - ]], - "room_id": "!KrLWMLDnZAyTapqLWW:matrix.org", - "state_key": "@matthew:matrix.org", - "type": "m.room.member", - "user_id": "@matthew:matrix.org" - } - }, - "membership": "join", - "eventStream": { - "chunk": [ "$1417731086506PgoVf:matrix.org" ], - "end": "s72595_4483_1934", - "start": "t67-41151_4483_1934" - }, - "room_id": "!KrLWMLDnZAyTapqLWW:matrix.org", - "state": [ "$1417731086506PgoVf:matrix.org" ], - "visibility": "public" - } - - -Room Alias API --------------- - -Room Directory API ------------------- - -User Profile API ----------------- - -Provides arbitrary per-user global state JSON storage with namespaced keys, -some of which have specific predefined serverside semantics. Keys must be named -(we don't support POSTing to anonymous key names) - -:: - - PUT /user/{userId}/data/m.displayname - PUT /user/{userId}/data/m.avatar_url - PUT /user/{userId}/data/m.contact_vcard - PUT /user/{userId}/data/net.arasphere.client.preferences - -Account Management API ----------------------- - -Actions API ------------ - -Presence API ------------- - -:: - - PUT /user/{userId}/presence/m.status // set DND/asleep/on holiday etc - - // XXX: do we need to distinguish between internationalisable presets like DND - // and free-form textual status messages? - // XXX: should this be in /user/{userId}/data/m.status instead? - // what's actually the difference? surely status is no different to avatar - // updates in terms of needing to be pushed around - - PUT /device/{deviceId}/presence/m.presence // explicitly set online/idle/offline - // or /presence/device/{deviceId} - - // XXX: need to remember how to handle activity notifications - -Typing API ----------- - -Relates_to pagination API -------------------------- - -Capabilities API ----------------- - From 7d32717c9bbf01f9f66aa261fccbf3ebba5720c5 Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Wed, 14 Jan 2015 13:43:59 +0000 Subject: [PATCH 56/95] A couple of more notes on auth resolution. --- drafts/erikj_federation.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drafts/erikj_federation.rst b/drafts/erikj_federation.rst index 094d63e5..c50eb54c 100644 --- a/drafts/erikj_federation.rst +++ b/drafts/erikj_federation.rst @@ -252,8 +252,14 @@ inform the other with suitable proof. The proofs can be: - An *event chain* that shows an auth event is *not* an ancestor of the event. + This can be done by giving the full ancestor chains up to the depth of the + invalid auth event. - Given an event (and event chain?) showing that authorization had been revoked. +If a server discovers it cannot prove the other side is wrong, then it accepts +that the other is correct; i.e. we always accept that the other side is correct +unless we can prove otherwise. + State Resolution From f09e5a676f9311b2fdeb51773831962661c9f216 Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Wed, 14 Jan 2015 14:43:29 +0000 Subject: [PATCH 57/95] Mention state resolution algorithm --- drafts/erikj_federation.rst | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drafts/erikj_federation.rst b/drafts/erikj_federation.rst index c50eb54c..9e38b445 100644 --- a/drafts/erikj_federation.rst +++ b/drafts/erikj_federation.rst @@ -267,6 +267,22 @@ State Resolution **TODO** +When two branches in the event graph merge, the state of those branches might +differ, so a *state resolution* algorithm must be used to determine the current +state of the resultant merge. + +The properties of the state resolution algorithm are: + +- Must only depend on the event graph, and not local server state. +- When two state events are comparable, the descendant one should be picked. +- Must not require the full event graph. + +The following algorithm satisfies these requirements; given two or more events, +pick the one with the greatest: + +#. Depth. +#. Hash of event_id. + Appendix ======== From 232e2675dd0ca2f461268f6deca83c0f38e37061 Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Wed, 14 Jan 2015 14:48:43 +0000 Subject: [PATCH 58/95] Brief mention of state conflict resolution --- drafts/erikj_federation.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drafts/erikj_federation.rst b/drafts/erikj_federation.rst index 9e38b445..c45626ef 100644 --- a/drafts/erikj_federation.rst +++ b/drafts/erikj_federation.rst @@ -284,6 +284,14 @@ pick the one with the greatest: #. Hash of event_id. +State Conflict Resolution +------------------------- + +If a server discovers that it disagrees with another about the current state, +it can follow the same process outlined in *Auth chain resolution* to resolve +these conflicts. + + Appendix ======== From 7108316ea86f6205a519ef705adb8378a894b14d Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Wed, 14 Jan 2015 16:24:25 +0000 Subject: [PATCH 59/95] Mention the fields that the server should compute when creating events --- drafts/erikj_federation.rst | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drafts/erikj_federation.rst b/drafts/erikj_federation.rst index c45626ef..940decde 100644 --- a/drafts/erikj_federation.rst +++ b/drafts/erikj_federation.rst @@ -6,6 +6,18 @@ Constructing a new event **TODO** +When constructing a new event, the server should insert the following fields: + +- ``prev_events``: The list of event ids of what the server believes are the + current leaf nodes of the event graph (i.e., nodes that have been received + but are yet to be referenced by another event). +- ``depth``: An integer one greater than the maximum depth of the event's + previous events. +- ``auth_events``: The list of event ids that authorizes this event. This + should be a subset of the current state. +- ``origin_server_ts``: The time the server created the event. +- ``origin``: The name of the server. + Signing and Hashes ~~~~~~~~~~~~~~~~~~ From b28f4042400b4af33c7d957c0eeb95f3789b65d3 Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Wed, 14 Jan 2015 16:30:25 +0000 Subject: [PATCH 60/95] Add ToC --- drafts/erikj_federation.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drafts/erikj_federation.rst b/drafts/erikj_federation.rst index 940decde..be026871 100644 --- a/drafts/erikj_federation.rst +++ b/drafts/erikj_federation.rst @@ -1,5 +1,7 @@ Federation ========== +.. sectnum +.. contents:: Table of Contents Constructing a new event ------------------------ From 3c16dbd63b557e888d4893484a99896e057654b5 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Wed, 14 Jan 2015 20:54:10 +0000 Subject: [PATCH 61/95] it's 2015 --- scripts/gendoc.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/gendoc.sh b/scripts/gendoc.sh index 38143939..4a92f681 100755 --- a/scripts/gendoc.sh +++ b/scripts/gendoc.sh @@ -18,6 +18,6 @@ perl -pi -e 's###' $MATRIXDO perl -MFile::Slurp -pi -e 'BEGIN { $nav = read_file("'$MATRIXDOTORG'/includes/nav.html") } s##
[matrix]
#' $MATRIXDOTORG/docs/spec/index.html $MATRIXDOTORG/docs/howtos/client-server.html -perl -pi -e 's##
#' $MATRIXDOTORG/docs/spec/index.html $MATRIXDOTORG/docs/howtos/client-server.html +perl -pi -e 's###' $MATRIXDOTORG/docs/spec/index.html $MATRIXDOTORG/docs/howtos/client-server.html -scp -r $MATRIXDOTORG/docs matrix@ldc-prd-matrix-001:/sites/matrix \ No newline at end of file +scp -r $MATRIXDOTORG/docs matrix@ldc-prd-matrix-001:/sites/matrix From 4b76181f740c02f96b1b0f80ddbbaa9f6c4bad56 Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Thu, 15 Jan 2015 13:53:54 +0000 Subject: [PATCH 62/95] Rearrange sections --- drafts/erikj_federation.rst | 255 ++++++++++++++++++------------------ 1 file changed, 127 insertions(+), 128 deletions(-) diff --git a/drafts/erikj_federation.rst b/drafts/erikj_federation.rst index be026871..ab89cfae 100644 --- a/drafts/erikj_federation.rst +++ b/drafts/erikj_federation.rst @@ -1,135 +1,8 @@ Federation ========== -.. sectnum +.. sectnum:: .. contents:: Table of Contents -Constructing a new event ------------------------- - - **TODO** - -When constructing a new event, the server should insert the following fields: - -- ``prev_events``: The list of event ids of what the server believes are the - current leaf nodes of the event graph (i.e., nodes that have been received - but are yet to be referenced by another event). -- ``depth``: An integer one greater than the maximum depth of the event's - previous events. -- ``auth_events``: The list of event ids that authorizes this event. This - should be a subset of the current state. -- ``origin_server_ts``: The time the server created the event. -- ``origin``: The name of the server. - - -Signing and Hashes -~~~~~~~~~~~~~~~~~~ - - **TODO** - -Validation ----------- - - **TODO** - -Domain specific string - A string of the form ``:``, where is a - single character, ```` is an arbitrary string that does not - include a colon, and `` is a valid server name. - -``room_id`` - A domain specific string with prefix ``!`` that is static across all events - in a graph and uniquely identifies it. The ``domain`` should be that of the - home server that created the room (i.e., the server that generated the - first ``m.room.create`` event). - -``sender`` - The entity that logically sent the event. This is usually a user id, but - can also be a server name. - -User Id - A domain specific string with prefix ``@`` representing a user account. The - ``domain`` is the home server of the user and is the server used to contact - the user. - -Joining a room --------------- - -If a user requests to join a room that the server is already in (i.e. the a -user on that server has already joined the room) then the server can simply -generate a join event and send it as normal. - -If the server is not already in the room it needs to will need to join via -another server that is already in the room. This is done as a two step process. - -First, the local server requests from the remote server a skeleton of a join -event. The remote does this as the local server does not have the event graph -to use to fill out the ``prev_events`` key in the new event. Critically, the -remote server does not process the event it responded with. - -Once the local server has this event, it fills it out with any extra data and -signs it. Once ready the local server sends this event to a remote server -(which could be the same or different from the first remote server), this -remote server then processes the event and distributes to all the other -participating servers in that room. The local server is told about the -current state and complete auth chain for the join event. The local server -can then process the join event itself. - - -.. Note:: - Finding which server to use to join any particular room is not specified. - - -Inviting a user ---------------- - -To invite a remote user to a room we need their home server to sign the invite -event. This is done by sending the event to the remote server, which then signs -the event, before distributing the invite to other servers. - -Failures --------- - -A server can notify a remote server about something it thinks it has done -wrong using the failures mechanism. For example, the remote accepted an event -the local think it shouldn't have. - -A failure has a severity level depending on the action taken by the local -server. These levels are: - -``FATAL`` - The local server could not parse the event, for example due to a missing - required field. - -``ERROR`` - The local server *could* parse the event, but it was rejected. For example, - the event may have failed an authorization check. - -``WARN`` - The local server accepted the event, but something was unexpected about it. - For example, the event may have referenced another event the local server - thought should be rejected. - -A failure also includes several other fields: - -``code`` - A numeric code (to be defined later) indicating a particular type of - failure. - -``reason`` - A short string indicating what was wrong, for diagnosis purposes on the - remote server. - -``affected`` - The event id of the event this failure is responding to. For example, if - an accepted event referenced a rejected event, this would point to the - accepted one. - -``source`` - The event id of the event that was the source of this unexpected behaviour. - For example, if an accepted event referenced a rejected event, this would - point to the rejected one. - - Authorization ------------- @@ -305,6 +178,132 @@ If a server discovers that it disagrees with another about the current state, it can follow the same process outlined in *Auth chain resolution* to resolve these conflicts. +Constructing a new event +------------------------ + + **TODO** + +When constructing a new event, the server should insert the following fields: + +- ``prev_events``: The list of event ids of what the server believes are the + current leaf nodes of the event graph (i.e., nodes that have been received + but are yet to be referenced by another event). +- ``depth``: An integer one greater than the maximum depth of the event's + previous events. +- ``auth_events``: The list of event ids that authorizes this event. This + should be a subset of the current state. +- ``origin_server_ts``: The time the server created the event. +- ``origin``: The name of the server. + + +Signing and Hashes +~~~~~~~~~~~~~~~~~~ + + **TODO** + +Validation +---------- + + **TODO** + +Domain specific string + A string of the form ``:``, where is a + single character, ```` is an arbitrary string that does not + include a colon, and `` is a valid server name. + +``room_id`` + A domain specific string with prefix ``!`` that is static across all events + in a graph and uniquely identifies it. The ``domain`` should be that of the + home server that created the room (i.e., the server that generated the + first ``m.room.create`` event). + +``sender`` + The entity that logically sent the event. This is usually a user id, but + can also be a server name. + +User Id + A domain specific string with prefix ``@`` representing a user account. The + ``domain`` is the home server of the user and is the server used to contact + the user. + +Joining a room +-------------- + +If a user requests to join a room that the server is already in (i.e. the a +user on that server has already joined the room) then the server can simply +generate a join event and send it as normal. + +If the server is not already in the room it needs to will need to join via +another server that is already in the room. This is done as a two step process. + +First, the local server requests from the remote server a skeleton of a join +event. The remote does this as the local server does not have the event graph +to use to fill out the ``prev_events`` key in the new event. Critically, the +remote server does not process the event it responded with. + +Once the local server has this event, it fills it out with any extra data and +signs it. Once ready the local server sends this event to a remote server +(which could be the same or different from the first remote server), this +remote server then processes the event and distributes to all the other +participating servers in that room. The local server is told about the +current state and complete auth chain for the join event. The local server +can then process the join event itself. + + +.. Note:: + Finding which server to use to join any particular room is not specified. + + +Inviting a user +--------------- + +To invite a remote user to a room we need their home server to sign the invite +event. This is done by sending the event to the remote server, which then signs +the event, before distributing the invite to other servers. + +Failures +-------- + +A server can notify a remote server about something it thinks it has done +wrong using the failures mechanism. For example, the remote accepted an event +the local think it shouldn't have. + +A failure has a severity level depending on the action taken by the local +server. These levels are: + +``FATAL`` + The local server could not parse the event, for example due to a missing + required field. + +``ERROR`` + The local server *could* parse the event, but it was rejected. For example, + the event may have failed an authorization check. + +``WARN`` + The local server accepted the event, but something was unexpected about it. + For example, the event may have referenced another event the local server + thought should be rejected. + +A failure also includes several other fields: + +``code`` + A numeric code (to be defined later) indicating a particular type of + failure. + +``reason`` + A short string indicating what was wrong, for diagnosis purposes on the + remote server. + +``affected`` + The event id of the event this failure is responding to. For example, if + an accepted event referenced a rejected event, this would point to the + accepted one. + +``source`` + The event id of the event that was the source of this unexpected behaviour. + For example, if an accepted event referenced a rejected event, this would + point to the rejected one. + Appendix ======== From 406742afaa3ea4a95eb04cf924159ac12688f070 Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Fri, 16 Jan 2015 10:14:56 +0000 Subject: [PATCH 63/95] A few notes on how we could do state resolution of auth events --- drafts/erikj_federation.rst | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drafts/erikj_federation.rst b/drafts/erikj_federation.rst index ab89cfae..2859ac86 100644 --- a/drafts/erikj_federation.rst +++ b/drafts/erikj_federation.rst @@ -171,6 +171,21 @@ pick the one with the greatest: #. Hash of event_id. +This works except in the case of auth events, where we need to mitigate against +the attack where servers artificially netsplit to avoid bans or power level +changes. + +We want the following rules to apply: + +- If power levels have been changed on two different branches use the rules + above, ensuring that the one picked is a valid change from the one not picked. +- Similarly handle membership changes (e.g. bans, kicks, etc.) +- If a power level has been changed in a branch, then any state merged from the + other branch *must* be allowed by the power level event change. Otherwise, + use the current one from the branch where the power level event changed. + + + State Conflict Resolution ------------------------- From 78a697c535557c335f02c1edc763acf9d6a5bdf0 Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Fri, 16 Jan 2015 10:24:43 +0000 Subject: [PATCH 64/95] Briefly touch on how to handle incoming events --- drafts/erikj_federation.rst | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/drafts/erikj_federation.rst b/drafts/erikj_federation.rst index 2859ac86..2eebce61 100644 --- a/drafts/erikj_federation.rst +++ b/drafts/erikj_federation.rst @@ -177,12 +177,12 @@ changes. We want the following rules to apply: -- If power levels have been changed on two different branches use the rules - above, ensuring that the one picked is a valid change from the one not picked. -- Similarly handle membership changes (e.g. bans, kicks, etc.) -- If a power level has been changed in a branch, then any state merged from the - other branch *must* be allowed by the power level event change. Otherwise, - use the current one from the branch where the power level event changed. +#. If power levels have been changed on two different branches use the rules + above, ensuring that the one picked is a valid change from the one not picked. +#. Similarly handle membership changes (e.g. bans, kicks, etc.) +#. If a power level has been changed in a branch, then any state merged from the + other branch *must* be allowed by the power level event change. Otherwise, + use the current one from the branch where the power level event changed. @@ -276,6 +276,22 @@ To invite a remote user to a room we need their home server to sign the invite event. This is done by sending the event to the remote server, which then signs the event, before distributing the invite to other servers. + +Handling incoming events +------------------------ + +When a server receives an event, it should: + +#. Check if it knows about the room. If it doesn't, then it should get the + current state and auth events to determine whether the server *should* be in + the room. If so continue, if not drop or reject the event +#. If the server already knew about the room, check the prev events to see if + it is missing any events. If it is, request them. Servers should limit how + far back they will walk the event graph for missing events. +#. If the server does not have all the prev events, then it should request the + current state and auth events from a server. + + Failures -------- From bb968e5bdd1652f28bd652fddaefb24f21c010ce Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Fri, 16 Jan 2015 15:12:34 +0000 Subject: [PATCH 65/95] Clarify m.room.create auth --- drafts/erikj_federation.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drafts/erikj_federation.rst b/drafts/erikj_federation.rst index 2eebce61..6d757288 100644 --- a/drafts/erikj_federation.rst +++ b/drafts/erikj_federation.rst @@ -36,7 +36,7 @@ include validation). **TODO**: What signatures do we expect? -1. If type is ``m.room.create`` allow. +1. If type is ``m.room.create`` allow if and only if it has no prev events. #. If type is ``m.room.member``: a. If ``membership`` is ``join``: From 5f998585750bcf2a3427e904ae5a93fc5627c0fc Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Fri, 16 Jan 2015 15:12:47 +0000 Subject: [PATCH 66/95] Mention possible types for auth events. --- drafts/erikj_federation.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drafts/erikj_federation.rst b/drafts/erikj_federation.rst index 6d757288..6552d053 100644 --- a/drafts/erikj_federation.rst +++ b/drafts/erikj_federation.rst @@ -103,6 +103,12 @@ algorithm to accept the event. These should be a subset of the current state. A server is required to store the complete chain of auth events for all events it serves to remote servers. +All auth events have type: + + - ``m.room.create`` + - ``m.room.power_levels`` + - ``m.room.member`` + .. todo We probably should probably give a lower band of how long auth events should be kept around for. From 91aba39fe721986870b5d54111febefa114f1c17 Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Fri, 16 Jan 2015 15:17:09 +0000 Subject: [PATCH 67/95] Reword note about auth chains --- drafts/erikj_federation.rst | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drafts/erikj_federation.rst b/drafts/erikj_federation.rst index 6552d053..05bcec04 100644 --- a/drafts/erikj_federation.rst +++ b/drafts/erikj_federation.rst @@ -119,11 +119,12 @@ Auth chain The *auth chain* for an event is the recursive list of auth events and the auth chain for those auth events. -The auth chain for event gives all the information a server needs to accept an -event. However, being given an auth chain for an event that appears valid does -not mean that the event might later be rejected. For example if we discover -that the sender had been banned between the join event listed in the auth -events and the event being authed. +.. Note:: The auth chain for an event gives all the information a server needs + to accept an event. However, being given an auth chain for an event + that appears valid does not mean that the event might not later be + rejected. For example if we discover that the sender had been banned + between the join event listed in the auth events and the event being + authed. **TODO**: Clean the above explanations up a bit. From 38efa9c5c27756f96c46179a5dc9f252e22cc42d Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Fri, 16 Jan 2015 16:09:07 +0000 Subject: [PATCH 68/95] Reword auth resolution section --- drafts/erikj_federation.rst | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drafts/erikj_federation.rst b/drafts/erikj_federation.rst index 05bcec04..206735bd 100644 --- a/drafts/erikj_federation.rst +++ b/drafts/erikj_federation.rst @@ -132,12 +132,13 @@ chain for those auth events. Auth chain resolution ~~~~~~~~~~~~~~~~~~~~~ -**TODO**: If an auth check fails, or if we get told something we accepted -should have been rejected, we need to try and determine who is right. +If an auth check fails, or if we get told something we accepted should have +been rejected, we need to try and determine who is right. -Both should inform the other of what they think the current auth chain is. If -either are missing auth events that they know are valid (through authorization -and state resolution) they process the missing events as usual. +If two servers disagree about the validity of the auth events, both should +inform the other of what they think the current auth chain is. If either are +missing auth events that they know are valid (through authorization and state +resolution) they process the missing events as usual. If either side notice that the other has accepted an auth events we think should be rejected (for reasons *not* in their auth chain), that server should From ef0091f3f2a52db725d4cfb1cca62cc9fdb4d92a Mon Sep 17 00:00:00 2001 From: Kegsay Date: Mon, 19 Jan 2015 12:03:52 +0000 Subject: [PATCH 69/95] Remove session section This is no longer required. --- drafts/general_api.rst | 32 ++++---------------------------- 1 file changed, 4 insertions(+), 28 deletions(-) diff --git a/drafts/general_api.rst b/drafts/general_api.rst index d69991fb..4734fc3f 100644 --- a/drafts/general_api.rst +++ b/drafts/general_api.rst @@ -701,8 +701,9 @@ Presence API ``[ONGOING]`` v1 semantics? -When a session starts, the home server can treat the user as "online". When the -session ends, the home server can treat the user as "offline". +When a client hits the event stream, the home server can treat the user as "online" +(unless they override this). When the client has not hit the event stream for a +certain period of time, the home server can treat the user as "offline". Inputs: - Presence state (online, offline, away, busy, do not disturb, etc) @@ -773,7 +774,7 @@ Client ~~~~~~ - e.g. Whether this client supports VoIP -When a session is started, the client needs to provide a capability set. The +When a client app is launched, the client needs to provide a capability set. The server will take the hashes of all the user's connected clients' capability sets and send the list of hashes as part of presence information (not necesarily as a ``m.presence`` event, but it should act like presence @@ -853,31 +854,6 @@ General client changes ---------------------- These are changes which do not introduce new APIs, but are required for the new APIs in order to fix certain issues. - -Sessions ``[Draft]`` -~~~~~~~~~~~~~~~~~~~~ - -A session is a group of requests sent by the same client. Sessions time out -after a short amount of time without any requests. Starting a session is known -as going "online". Its purpose is to wrap up the expiry of presence and typing -notifications into a clearer scope. A session starts when the client makes any -request. A session ends when the client doesn't make a request for a particular -amount of time (times out). A session can also end when explicitly hitting a -particular endpoint. This is known as going "offline". A session can also be -created by explicitly hitting a particular endpoint. - -When a session starts, a session ID is sent in response to the first request the -client makes. This session ID should be sent in *all* subsequent requests. If -the server expires a session and the client uses an old session ID, the server -should fail the request with the old session ID and send a new session ID in -response for the client to use. If the client receives a new session ID -mid-session, it must re-establish its typing status and presence status, as they -are linked to the session ID. - -Lightweight clients who do not wish to manage their session can omit the -session ID on their requests. The home server MUST treat these requests as -coming from the active session in order to ensure that presence works correctly -for these simple clients. Updates (Events) ``[Draft]`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ From a1964b466bd7278c391f7b7d7bb8a8151e7d37e5 Mon Sep 17 00:00:00 2001 From: Kegsay Date: Mon, 19 Jan 2015 17:17:33 +0000 Subject: [PATCH 70/95] Add profile propagation notes After much IRL discussion --- drafts/general_api.rst | 147 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 132 insertions(+), 15 deletions(-) diff --git a/drafts/general_api.rst b/drafts/general_api.rst index 4734fc3f..51db8313 100644 --- a/drafts/general_api.rst +++ b/drafts/general_api.rst @@ -409,27 +409,144 @@ Inputs: Output: - The key/values specified. -Propagation -~~~~~~~~~~~ +Propagation ``[Draft]`` +~~~~~~~~~~~~~~~~~~~~~~~ The goals of propagation are: -- Profile updates should propagate to all rooms the user is in. +- Profile updates should propagate to all rooms the user is in so + rooms can display change events. Due to this, profile propagation + HAS to be in the event graph for the room, in order to place it in + the right position. - We should support different kinds of profiles for different rooms. +- Propagation should avoid flicker between joining a room and getting + profile information. + +In v1, this was achieved by sending ``displayname`` and ``avatar_url`` +keys inside the ``content`` of an ``m.room.member`` event. This event +type was chosen in order to prevent flicker on the client, as all the +information came down in one lump. + +This had a number of problems associated with it: + +- It conflated profile info and membership info, simply to avoid client + flicker. +- Name/avatar changes created more ``m.room.member`` events which meant + they needed to be included in the auth chains for federation. This + created long auth chains which is suboptimal since home servers need + to store the auth chains forever. + +These problems can be resolved by creating an ``m.room.member.profile`` +event which contains profile information. This reduces the number of +``m.room.member`` events over federation, since profile changes are of +a different event type. This also prevents conflation of profile changes +and membership changes. + +However, this introduces its own set of problems, namely flicker. The +client would receive the ``m.room.member`` event first, followed by +the ``m.room.member.profile`` event, which could cause a flicker. In +addition, federation may not send both event types in a single transaction, +resulting in missing information on the receiving home server. + +For federation, these problems can be resolved by sending the +``m.room.member`` event as they are in v1 (with ``displayname`` and +``avatar_url`` in the ``content``). The receiving home server will then +extract these keys and create a server-generated ``m.room.member.profile`` +event. To avoid confusion with duplicate information, the ``avatar_url`` +and ``displayname`` keys should be removed by the receiving home server. +When a client requests these events (either from the event stream +or from an initial sync), the server will send the generated +``m.room.member.profile`` event under the ``unsigned.profile`` key of the +``m.room.member`` event. Subsequent profile updates are just sent as +``m.room.member.profile`` events. + +For clients, profile information is now *entirely* represented in +``m.room.member.profile`` events. To avoid flicker, this event is +combined with the ``m.room.member`` event under an ``unsigned.profile`` +key. -In v1, users have a single profile. This information is duplicated for -every room the user is in. This duplication means that things like -display names *could* change on a room-by-room basis. However, this is -extremely inefficient when updating the display name, as you have to -send ``num_joined_rooms`` events to inform everyone of the update. +:: -There's no easy solution to this. The room needs a record of the name -changes; it's not good enough to send it just to the users (the set of -all users in all rooms the user changing their display name is in), as -new users who join later still need to know about these changes. The -ordering information needs to be preserved as well. + Case #1: @user:domain "User" joins a room + + HS --> HS: + { + content: { + displayname: "User", + membership: "join" + }, + type: "m.room.member", + [...] + } + + Receiving HS transformation: + { + content: { + + membership: "join" + }, + type: "m.room.member", + [...] + } + + Receiving HS creates new server-generated event: + { + content: { + displayname: "User" + }, + type: "m.room.member.profile", + [...] + } + + Client sees: (e.g. from event stream / initial sync) + { + content: { + membership: "join" + }, + type: "m.room.member", + unsigned: { + profile: { + content: { + displayname: "User" + }, + type: "m.room.member.profile", + [...] + } + } + [...] + } + +:: -An improvement would be to allow the client to not automatically share -updates of their profile information to all rooms. + Case #2: @user:domain "User" updates their display name to "User2" + (they are already in the room) + + HS --> HS: + { + content: { + displayname: "User2" + }, + prev_content: { + displayname: "User" + }, + type: "m.room.member.profile", + [...] + } + + Client sees: + { + content: { + displayname: "User2" + }, + prev_content: { + displayname: "User" + }, + type: "m.room.member.profile", + [...] + } + +The removal of the ``displayname`` and ``avatar_url`` keys from ``m.room.member`` +can only be done if the client trusts their HS, as it will break the sending HS's +signature. Requesting the "raw" federation event will have to return these keys. Account Management API ``[ONGOING]`` ------------------------------------ From 61b86642868e79fe9b72c0985089177c71d312af Mon Sep 17 00:00:00 2001 From: Kegsay Date: Mon, 19 Jan 2015 17:24:28 +0000 Subject: [PATCH 71/95] Clarify profile propagation section more --- drafts/general_api.rst | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/drafts/general_api.rst b/drafts/general_api.rst index 51db8313..067abdbd 100644 --- a/drafts/general_api.rst +++ b/drafts/general_api.rst @@ -444,15 +444,17 @@ and membership changes. However, this introduces its own set of problems, namely flicker. The client would receive the ``m.room.member`` event first, followed by the ``m.room.member.profile`` event, which could cause a flicker. In -addition, federation may not send both event types in a single transaction, +addition, federation may not send both events in a single transaction, resulting in missing information on the receiving home server. For federation, these problems can be resolved by sending the ``m.room.member`` event as they are in v1 (with ``displayname`` and -``avatar_url`` in the ``content``). The receiving home server will then -extract these keys and create a server-generated ``m.room.member.profile`` -event. To avoid confusion with duplicate information, the ``avatar_url`` -and ``displayname`` keys should be removed by the receiving home server. +``avatar_url`` in the ``content``). They *have* to be sent in the +``content`` as this information needs to be signed. The receiving home +server will then extract these keys and create a server-generated +``m.room.member.profile`` event. To avoid confusion with duplicate +information, the ``avatar_url`` and ``displayname`` keys should be +removed from the ``m.room.member`` event by the receiving home server. When a client requests these events (either from the event stream or from an initial sync), the server will send the generated ``m.room.member.profile`` event under the ``unsigned.profile`` key of the From 3b35e95630c361b310d9586ef18c9ecf0535b984 Mon Sep 17 00:00:00 2001 From: Kegsay Date: Mon, 19 Jan 2015 17:37:23 +0000 Subject: [PATCH 72/95] Tweaks based on feedback --- drafts/general_api.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drafts/general_api.rst b/drafts/general_api.rst index 067abdbd..894deac0 100644 --- a/drafts/general_api.rst +++ b/drafts/general_api.rst @@ -449,8 +449,8 @@ resulting in missing information on the receiving home server. For federation, these problems can be resolved by sending the ``m.room.member`` event as they are in v1 (with ``displayname`` and -``avatar_url`` in the ``content``). They *have* to be sent in the -``content`` as this information needs to be signed. The receiving home +``avatar_url`` in the ``content``). These keys need to be signed so +they cannot be in the ``unsigned`` part of the event. The receiving home server will then extract these keys and create a server-generated ``m.room.member.profile`` event. To avoid confusion with duplicate information, the ``avatar_url`` and ``displayname`` keys should be From 5279541c28959e40385aca46b79226d008bfa803 Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Tue, 20 Jan 2015 11:08:43 +0000 Subject: [PATCH 73/95] Update erikj_federation.rst Update state resolution rules to indicate what you should do if none of the merged events are allowed by the newly merged auth events. --- drafts/erikj_federation.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drafts/erikj_federation.rst b/drafts/erikj_federation.rst index 206735bd..0644f271 100644 --- a/drafts/erikj_federation.rst +++ b/drafts/erikj_federation.rst @@ -188,9 +188,9 @@ We want the following rules to apply: #. If power levels have been changed on two different branches use the rules above, ensuring that the one picked is a valid change from the one not picked. #. Similarly handle membership changes (e.g. bans, kicks, etc.) -#. If a power level has been changed in a branch, then any state merged from the - other branch *must* be allowed by the power level event change. Otherwise, - use the current one from the branch where the power level event changed. +#. Any state merged must be allowed by the newly merged auth events. If none of + the candidate events for a given state are allowed, we pick the last event + given by the ordering above (i.e. we pick one with the least depth). From 109c8a97a8bd1556d75dacbf668fee17567ca94e Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Tue, 20 Jan 2015 16:36:32 +0000 Subject: [PATCH 74/95] Use title heading sizings of =,-,~,+ since that is what most of our docs use. Inconsistent use breaks when you try to merge them. --- specification/00_basis.rst | 16 ++++++++-------- specification/11_event_signing.rst | 6 +++--- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/specification/00_basis.rst b/specification/00_basis.rst index 8dceac1f..77aec367 100644 --- a/specification/00_basis.rst +++ b/specification/00_basis.rst @@ -134,7 +134,7 @@ federate with other HSes. It is typically responsible for multiple clients. more home servers. Events -++++++ +~~~~~~ Data in Matrix is encapsulated in an "event". An event is an action within the system. Typically each action (e.g. sending a message) correlates with exactly @@ -148,7 +148,7 @@ is the event type for instant messages. Events are usually sent in the context of a "Room". Event Graphs -++++++++++++ +~~~~~~~~~~~~ Each event has a list of zero or more `parent` events. These relations form directed acyclic graphs of events called `event graphs`. Every event graph has a single root event, and each event graph forms the @@ -247,7 +247,7 @@ participating in a room. Room Aliases -~~~~~~~~~~~~ +++++++++++++ Each room can also have multiple "Room Aliases", which looks like:: @@ -282,7 +282,7 @@ that are in the room that can be used to join via. |________________________________| Identity -~~~~~~~~ +++++++++ Users in Matrix are identified via their matrix user ID (MXID). However, existing 3rd party ID namespaces can also be used in order to identify Matrix @@ -303,7 +303,7 @@ the Matrix ecosystem. However, without one clients will not be able to look up user IDs using 3PIDs. Presence -~~~~~~~~ +++++++++ Each user has the concept of presence information. This encodes the "availability" of that user, suitable for display on other user's clients. This @@ -343,7 +343,7 @@ the other direction it will not). This timestamp is presented via a key called message is generated/emitted that the user was last seen active. Presence List -+++++++++++++ +~~~~~~~~~~~~~ Each user's home server stores a "presence list". This stores a list of user IDs whose presence the user wants to follow. @@ -353,7 +353,7 @@ and accept the invitation. Once accepted, both user's HSes track the subscription. Presence and Permissions -++++++++++++++++++++++++ +~~~~~~~~~~~~~~~~~~~~~~~~ For a viewing user to be allowed to see the presence information of a target user, either: @@ -367,7 +367,7 @@ presence information in a user list for a room. Profiles -~~~~~~~~ +++++++++ .. TODO-spec - Metadata extensibility diff --git a/specification/11_event_signing.rst b/specification/11_event_signing.rst index 9d08f996..68f9c178 100644 --- a/specification/11_event_signing.rst +++ b/specification/11_event_signing.rst @@ -38,7 +38,7 @@ using this representation. ).encode("UTF-8") Grammar -+++++++ +~~~~~~~ Adapted from the grammar in http://tools.ietf.org/html/rfc7159 removing insignificant whitespace, fractions, exponents and redundant character escapes @@ -76,7 +76,7 @@ the signature for that sequence and then adding the signature to the original JSON object. Signing Details -+++++++++++++++ +~~~~~~~~~~~~~~~ JSON is signed by encoding the JSON object without ``signatures`` or keys grouped as ``unsigned``, using the canonical encoding described above. The JSON bytes are then signed using the @@ -133,7 +133,7 @@ and additional signatures. return json_object Checking for a Signature -++++++++++++++++++++++++ +~~~~~~~~~~~~~~~~~~~~~~~~ To check if an entity has signed a JSON object a server does the following From 62471348caa78aa64cef5681222818c5d43da824 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Tue, 20 Jan 2015 16:58:22 +0000 Subject: [PATCH 75/95] This is a comment, not a directive. Remove :: --- specification/10_events.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specification/10_events.rst b/specification/10_events.rst index aa9f99b1..2597ce82 100644 --- a/specification/10_events.rst +++ b/specification/10_events.rst @@ -428,7 +428,7 @@ outlined below: "mimetype" : "string (e.g. image/jpeg)", } -.. TODO-spec:: +.. TODO-spec Make the definitions "inherit" from FileInfo where necessary... From 31deddccdf14752c4d117f19bf1e97a4046da146 Mon Sep 17 00:00:00 2001 From: Kegsay Date: Wed, 21 Jan 2015 09:53:47 +0000 Subject: [PATCH 76/95] Add presence API from notes --- drafts/general_api.rst | 81 ++++++++++++++++++++++++++++++++---------- 1 file changed, 63 insertions(+), 18 deletions(-) diff --git a/drafts/general_api.rst b/drafts/general_api.rst index 894deac0..af47aebe 100644 --- a/drafts/general_api.rst +++ b/drafts/general_api.rst @@ -78,13 +78,8 @@ Terminology: navigation. -Filter API ``[ONGOING]`` +Filter API ``[Draft]`` ------------------------ -.. NOTE:: - - Exactly what can be filtered? Which APIs use this? Are we - conflating too much? - - Do we want to specify negative filters (e.g. don't give me - ``event.type.here`` events) Inputs: - Which event types (incl wildcards) @@ -104,6 +99,7 @@ Notes: a delimiter between them. - Omitting the token on APIs results in ALL THE THINGS coming down. - Clients should remember which token they need to use for which API. + - It should be possible to define negative filters (e.g. not presence) - HTTP note: If the filter API is a separate endpoint, then you could easily allow APIs which use filtering to ALSO specifiy query parameters to tweak the filter. @@ -810,24 +806,73 @@ Compact flag notes: Notes: - A batching version of this API needs to be provided. -Presence API ``[ONGOING]`` +Presence API ``[Draft]`` -------------------------- -.. NOTE:: - - Per device presence: how does this work? Union of devices? Priority order for - statuses? E.g. online trumps away trumps offline. So if any device is online, - then the user is online, etc. - - Presence lists / roster? We probably do want this, but are we happy with the - v1 semantics? - +The goals of presence are to: + +- Let other users know if someone is "online". +- Let other users know if someone is likely to respond to any messages. +- Let other users know specific status information (e.g. "In a Meeting"). + +"Online" state can be detected by inspecting when the last time the client made +a request to the server. This could be any request, or a specific kind of request. +For connection-orientated protocols, detecting "online" state can be determined by +the state of this connection stream. For HTTP, this can be detected via requests +to the event stream. + +Online state is separate from letting other users know if someone is *likely to +respond* to messages. This introduces the concept of an "idle" flag, which is +set when the user has not done any "interaction" with the app. The definition of +"interaction" varies based on the app, so it is up to the app to set this "idle" +flag. + +Letting users know specific status information can be achieved via the same method +as v1. Status information should be scoped per *user* and not device as determining +a union algorithm between statuses is nonsensical. Passing status information per +device to all other users just redirects the union problem to the client, which +will commonly be presenting this information as an icon alongside the user. + +When a client hits the event stream, the home server can treat the user as +"online". This behaviour should be able to be overridden to avoid flicker +during connection losses when the client is appear offline (e.g. device is +appear offline > goes into a tunnel > server times out > device regains +connection and hits the event stream forcing the device online before the +"appear offline" state can be set). When the client has not hit the event +stream for a certain period of time, the home server can treat the user as +"offline". + +The user should also be able to set their presence via a direct API, without +having to hit the event stream. The home server will set a timer when the +connection ends, after which it will set that device to offline. + +As the idle flag and online state is determined per device, there needs to be a +union algorithm to merge these into a single state and flag per user, which will +be sent to other users. The algorithm is: + +- If any device is online and not idle, the user is online. +- Else if all online devices are idle, the user is idle. +- Else the user is offline (no online devices). + +Changing presence status: -When a client hits the event stream, the home server can treat the user as "online" -(unless they override this). When the client has not hit the event stream for a -certain period of time, the home server can treat the user as "offline". +Inputs: + - User ID + - Presence status (online, away, busy, do not disturb, etc) +Outputs: + - None. + +Setting the idle flag: Inputs: - - Presence state (online, offline, away, busy, do not disturb, etc) + - User ID + - Is idle Outputs: - None. + +Extra parameters associated with the event stream: + +Inputs: + - Presence state (online, appear offline) Typing API ``[Final]`` From ccf8408e9215c32deba3f73958b14c1ee6015188 Mon Sep 17 00:00:00 2001 From: Kegsay Date: Wed, 21 Jan 2015 10:19:48 +0000 Subject: [PATCH 77/95] More tweaks from comments --- drafts/general_api.rst | 39 ++++++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/drafts/general_api.rst b/drafts/general_api.rst index af47aebe..0a9233e1 100644 --- a/drafts/general_api.rst +++ b/drafts/general_api.rst @@ -87,7 +87,8 @@ Inputs: - Which user IDs (for profile/presence) - Whether you want federation-style event JSON - Whether you want coalesced ``updates`` events - - Whether you want coalesced ``relates_to`` events (and the max # to coalesce) + - Whether you want coalesced ``relates_to`` events (and the max # to coalesce, + and the relationship types, and the sort order) - limit= param? (XXX: probably not; this should be done in the query itself) - Which keys to return for events? e.g. no ``origin_server_ts`` if you don't show timestamps (n.b. encrypted keys can't be filtered out) @@ -711,8 +712,11 @@ Knocking on a room ``[TODO]`` If a room has the right ``join_rule`` e.g. ``knock``, then it should be able to send a special knock event to ask to join the room. -Read-up-to markers ``[Draft]`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Read-up-to markers ``[ONGOING]`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.. NOTE:: + - Convert to EDUs for markers with periodic PDUs to reduce event graph size? + Inputs: - State Event type (``m.room.marker.delivered`` and ``m.room.marker.read``) - Event ID to mark up to. This is inclusive of the event ID specified. @@ -743,6 +747,8 @@ Notes: you fit that in to the message thread if you did so? Would probably have to fall back to the timestamp heuristic. After all, these markers are only ever going to be heuristics given they are not acknowledging each message event. + - These markers also allow unread message counts to be kept in sync for multiple + devices. Kicking a user ``[Final]`` ~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -768,13 +774,6 @@ What data flows does it address: Send a message ``[ONGOING]`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. NOTE:: - Semantics for HTTP ordering. Do we really want to block requests with higher - sequence numbers if the server hasn't received earlier ones? Is this even - practical, given clients have a limit on the number of concurrent connections? - How can this be done in a way which doesn't suck for clients? Could we just - say "it isn't 'Sent' until it comes back down your event stream"? - Inputs: - Room ID - Message contents @@ -787,10 +786,6 @@ Outputs: (if compact=true) What data flows does it address: - Chat Screen: Send a Message -Ordering notes: - - HTTP: When sending a message with a higher seqnum, it will block the request - until it receives earlier seqnums. The block will expire after a timeout and - reject the message stating that it was missing a seqnum. E2E Notes: - For signing: You send the original message to the HS and it will return the full event JSON which will be sent. This full event is then signed and sent @@ -807,7 +802,7 @@ Notes: - A batching version of this API needs to be provided. Presence API ``[Draft]`` --------------------------- +------------------------ The goals of presence are to: - Let other users know if someone is "online". @@ -1062,8 +1057,18 @@ receive the events separately down the event stream. Combining event updates server-side does not make client implementation simpler, as the client still needs to know how to combine the events. -Relates to (Events) ``[Draft]`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Relates to (Events) ``[ONGOING]`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.. NOTE:: + - Should be able to specify more relationship info other than just the event + type. Forcing that m.room.message A "relates_to" another m.room.message B + means that A is a threaded conversation reply to B is needlessly + restrictive. What if A and B relate to each other by some other metric + (e.g. they are part of a group rather than a thread? or we distinguish + mail-style threading from multithreaded-IM threading for the same set of + messages? etc)? E.g. ``"relates_to" : [["in_reply_to", "$event_id1"], + ["another_type_of_relation", "$event_id2"]]`` + Events may be in response to other events, e.g. comments. This is represented by the ``relates_to`` key. This differs from the ``updates`` key as they *do not update the event itself*, and are *not required* in order to display the From fcbdc396749f535be0864a38a764a1d94d50eee1 Mon Sep 17 00:00:00 2001 From: Kegsay Date: Wed, 21 Jan 2015 10:28:01 +0000 Subject: [PATCH 78/95] Add section on device IDs for reg/login Final cleanup before merge --- drafts/general_api.rst | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/drafts/general_api.rst b/drafts/general_api.rst index 0a9233e1..c26e2963 100644 --- a/drafts/general_api.rst +++ b/drafts/general_api.rst @@ -54,8 +54,6 @@ This contains the formal proposal for Matrix Client-Server API v2. This API would completely replace v1. It is a general API, not specific to any particular protocol e.g. HTTP. The following APIs will remain unchanged from v1: -- Registration API -- Login API - Content repository API This version will change the path prefix for HTTP: @@ -547,14 +545,15 @@ The removal of the ``displayname`` and ``avatar_url`` keys from ``m.room.member` can only be done if the client trusts their HS, as it will break the sending HS's signature. Requesting the "raw" federation event will have to return these keys. -Account Management API ``[ONGOING]`` ------------------------------------- -.. NOTE:: - - How do device IDs fit into everything else? Namely, where do we tell the HS - what device ID we are? +Account Management API ``[Draft]`` +---------------------------------- +The registration and login APIs in v2 do not support specifying device IDs. In v2, +this will become *mandatory* when sending your initial request. Access tokens will +be scoped per device, so using the same device ID twice when logging in will +clobber the old access token. -Users may wish to delete their account, revoke access tokens, manage -their devices, etc. This is achieved using an account management API. +In terms of additional APIs, users may wish to delete their account, revoke access +tokens, manage their devices, etc. This is achieved using an account management API. Deleting an account: @@ -772,8 +771,8 @@ Outputs: What data flows does it address: - Chat Screen: Leave a room -Send a message ``[ONGOING]`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Send a message ``[Draft]`` +~~~~~~~~~~~~~~~~~~~~~~~~~~ Inputs: - Room ID - Message contents From 03eaa21508653f2f70beb149d63cd91a8c399462 Mon Sep 17 00:00:00 2001 From: Kegsay Date: Wed, 21 Jan 2015 11:56:55 +0000 Subject: [PATCH 79/95] Add blurb on invites for initialSync --- drafts/general_api.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drafts/general_api.rst b/drafts/general_api.rst index c26e2963..974ed0fe 100644 --- a/drafts/general_api.rst +++ b/drafts/general_api.rst @@ -126,6 +126,10 @@ Outputs: - # members - max of limit= message events - room ID + - For each room the user is invited to: + - The invite event + - Other state info (e.g. room name, topic) + - # members? Notes: - If a chunk token is applied, you will get a delta relative to the last request performed with that streaming token rather than all the rooms. From e309db547a87498196f534e4418b353713e65fcf Mon Sep 17 00:00:00 2001 From: Kegsay Date: Wed, 21 Jan 2015 11:58:45 +0000 Subject: [PATCH 80/95] Specifically reference join_rules In reference to SPEC-63 --- drafts/general_api.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drafts/general_api.rst b/drafts/general_api.rst index 974ed0fe..626b3750 100644 --- a/drafts/general_api.rst +++ b/drafts/general_api.rst @@ -128,7 +128,7 @@ Outputs: - room ID - For each room the user is invited to: - The invite event - - Other state info (e.g. room name, topic) + - Other state info (e.g. room name, topic, join_rules to know if pubilc) - # members? Notes: - If a chunk token is applied, you will get a delta relative to the last request From bd48e4ab6dc21d2c595243a6f61e7f88587a51d2 Mon Sep 17 00:00:00 2001 From: Kegsay Date: Wed, 21 Jan 2015 13:09:16 +0000 Subject: [PATCH 81/95] Move invite data to a note Done in order to clarify that this API "should probably have something like this" rather than "will have this". --- drafts/general_api.rst | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drafts/general_api.rst b/drafts/general_api.rst index 626b3750..5a027d15 100644 --- a/drafts/general_api.rst +++ b/drafts/general_api.rst @@ -106,6 +106,13 @@ Notes: Global initial sync API ``[Draft]`` ------------------------------------- .. NOTE:: + - The output to this should also have something like: + For each room the user is invited to: + - The invite event + - Other state info (e.g. room name, topic, join_rules to know if pubilc) + - # members? + so clients know more information about the room other than the user_id of the + inviter, timestamp and the room ID. v2.1: - Will need some form of state event pagination like we have for message @@ -126,10 +133,6 @@ Outputs: - # members - max of limit= message events - room ID - - For each room the user is invited to: - - The invite event - - Other state info (e.g. room name, topic, join_rules to know if pubilc) - - # members? Notes: - If a chunk token is applied, you will get a delta relative to the last request performed with that streaming token rather than all the rooms. From a9a4c46a57da618ea0d16ddfcdea84bc78d6e2be Mon Sep 17 00:00:00 2001 From: "Paul \"LeoNerd\" Evans" Date: Wed, 21 Jan 2015 18:26:27 +0000 Subject: [PATCH 82/95] Add spec for m.notice (SPEC-18) --- specification/10_events.rst | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/specification/10_events.rst b/specification/10_events.rst index 2597ce82..20dccb6b 100644 --- a/specification/10_events.rst +++ b/specification/10_events.rst @@ -330,6 +330,22 @@ outlined below: Example: ``{ "msgtype": "m.emote", "body": "tries to come up with a witty explanation" }`` +``m.notice`` + Required keys: + - ``body`` : "string" - The body of the message. + Optional keys: + None. + Example: + ``{ "msgype": "m.notice", "body": "some kind of automated announcement" }`` + + A ``m.notice`` message should be considered similar to a plain ``m.text`` + message except that clients should visually distinguish it in some way. It is + intended to be used by automated clients, such as bots, bridges, and other + entities, rather than humans. Additionally, such automated agents which watch + a room for messages and respond to them ought to ignore ``m.notice`` messages. + This helps to prevent infinite-loop situations where two automated clients + continuously exchange messages, as each responds to the other. + ``m.image`` Required keys: - ``url`` : "string" - The URL to the image. From 3dafe4133ccb0a7b295815d9c56432ec6aff31e5 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Tue, 19 Aug 2014 16:39:30 +0100 Subject: [PATCH 83/95] Added registration/login jsfiddle, formatted so it can be loaded directly from jsfiddle. Requires jQuery 1.8.3 --- jsfiddles/register_login/demo.css | 7 +++ jsfiddles/register_login/demo.html | 20 +++++++++ jsfiddles/register_login/demo.js | 69 ++++++++++++++++++++++++++++++ 3 files changed, 96 insertions(+) create mode 100644 jsfiddles/register_login/demo.css create mode 100644 jsfiddles/register_login/demo.html create mode 100644 jsfiddles/register_login/demo.js diff --git a/jsfiddles/register_login/demo.css b/jsfiddles/register_login/demo.css new file mode 100644 index 00000000..11781c25 --- /dev/null +++ b/jsfiddles/register_login/demo.css @@ -0,0 +1,7 @@ +.loggedin { + visibility: hidden; +} + +p { + font-family: monospace; +} diff --git a/jsfiddles/register_login/demo.html b/jsfiddles/register_login/demo.html new file mode 100644 index 00000000..9cdb1613 --- /dev/null +++ b/jsfiddles/register_login/demo.html @@ -0,0 +1,20 @@ +
+

This registration/login demo requires a home server to be running on http://localhost:8080

+
+
+ + + +
+
+ + + +
+
+

+ + +

+
+ diff --git a/jsfiddles/register_login/demo.js b/jsfiddles/register_login/demo.js new file mode 100644 index 00000000..1644f76a --- /dev/null +++ b/jsfiddles/register_login/demo.js @@ -0,0 +1,69 @@ +var accountInfo = {}; + +var showLoggedIn = function(data) { + accountInfo = data; + $(".loggedin").css({visibility: "visible"}); + $("#welcomeText").text("Welcome " + accountInfo.user_id+". Your access token is: " + + accountInfo.access_token); +}; + +$('.register').live('click', function() { + var user = $("#user").val(); + var password = $("#password").val(); + $.ajax({ + url: "http://localhost:8080/matrix/client/api/v1/register", + type: "POST", + contentType: "application/json; charset=utf-8", + data: JSON.stringify({ user_id: user, password: password }), + dataType: "json", + success: function(data) { + showLoggedIn(data); + }, + error: function(err) { + alert(JSON.stringify($.parseJSON(err.responseText))); + } + }); +}); + +var login = function(user, password) { + $.ajax({ + url: "http://localhost:8080/matrix/client/api/v1/login", + type: "POST", + contentType: "application/json; charset=utf-8", + data: JSON.stringify({ user: user, password: password, type: "m.login.password" }), + dataType: "json", + success: function(data) { + showLoggedIn(data); + }, + error: function(err) { + alert(JSON.stringify($.parseJSON(err.responseText))); + } + }); +}; + +$('.login').live('click', function() { + var user = $("#userLogin").val(); + var password = $("#passwordLogin").val(); + $.getJSON("http://localhost:8080/matrix/client/api/v1/login", function(data) { + if (data.type !== "m.login.password") { + alert("I don't know how to login with this type: " + data.type); + return; + } + login(user, password); + }); +}); + +$('.logout').live('click', function() { + accountInfo = {}; + $("#imSyncText").text(""); + $(".loggedin").css({visibility: "hidden"}); +}); + +$('.testToken').live('click', function() { + var url = "http://localhost:8080/matrix/client/api/v1/im/sync?access_token=" + accountInfo.access_token + "&from=END&to=START&limit=1"; + $.getJSON(url, function(data) { + $("#imSyncText").text(JSON.stringify(data, undefined, 2)); + }).fail(function(err) { + $("#imSyncText").text(JSON.stringify($.parseJSON(err.responseText))); + }); +}); From 8fb3367238ecd9779ca64c36a2a40b70dd47454b Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Tue, 19 Aug 2014 17:34:31 +0100 Subject: [PATCH 84/95] More fiddles, more fun! --- jsfiddles/create_room_send_msg/demo.css | 17 ++++ jsfiddles/create_room_send_msg/demo.html | 30 +++++++ jsfiddles/create_room_send_msg/demo.js | 109 +++++++++++++++++++++++ 3 files changed, 156 insertions(+) create mode 100644 jsfiddles/create_room_send_msg/demo.css create mode 100644 jsfiddles/create_room_send_msg/demo.html create mode 100644 jsfiddles/create_room_send_msg/demo.js diff --git a/jsfiddles/create_room_send_msg/demo.css b/jsfiddles/create_room_send_msg/demo.css new file mode 100644 index 00000000..48a55f37 --- /dev/null +++ b/jsfiddles/create_room_send_msg/demo.css @@ -0,0 +1,17 @@ +.loggedin { + visibility: hidden; +} + +p { + font-family: monospace; +} + +table +{ + border-spacing:5px; +} + +th,td +{ + padding:5px; +} diff --git a/jsfiddles/create_room_send_msg/demo.html b/jsfiddles/create_room_send_msg/demo.html new file mode 100644 index 00000000..31c26c76 --- /dev/null +++ b/jsfiddles/create_room_send_msg/demo.html @@ -0,0 +1,30 @@ +
+

This room creation / message sending demo requires a home server to be running on http://localhost:8080

+
+
+ + + +
+
+
+ + +
+
+ + + +
+ + + + + + + + + +
Room IDMy stateRoom AliasLatest message
+
+ diff --git a/jsfiddles/create_room_send_msg/demo.js b/jsfiddles/create_room_send_msg/demo.js new file mode 100644 index 00000000..c17eb26b --- /dev/null +++ b/jsfiddles/create_room_send_msg/demo.js @@ -0,0 +1,109 @@ +var accountInfo = {}; + +var showLoggedIn = function(data) { + accountInfo = data; + getCurrentRoomList(); + $(".loggedin").css({visibility: "visible"}); +}; + +$('.login').live('click', function() { + var user = $("#userLogin").val(); + var password = $("#passwordLogin").val(); + $.ajax({ + url: "http://localhost:8080/matrix/client/api/v1/login", + type: "POST", + contentType: "application/json; charset=utf-8", + data: JSON.stringify({ user: user, password: password, type: "m.login.password" }), + dataType: "json", + success: function(data) { + showLoggedIn(data); + }, + error: function(err) { + alert(JSON.stringify($.parseJSON(err.responseText))); + } + }); +}); + +var getCurrentRoomList = function() { + var url = "http://localhost:8080/matrix/client/api/v1/im/sync?access_token=" + accountInfo.access_token + "&from=END&to=START&limit=1"; + $.getJSON(url, function(data) { + for (var i=0; i 0) { + data.room_alias_name = roomAlias; + } + $.ajax({ + url: "http://localhost:8080/matrix/client/api/v1/rooms?access_token="+accountInfo.access_token, + type: "POST", + contentType: "application/json; charset=utf-8", + data: JSON.stringify(data), + dataType: "json", + success: function(data) { + data.membership = "join"; // you are automatically joined into every room you make. + data.latest_message = ""; + addRoom(data); + }, + error: function(err) { + alert(JSON.stringify($.parseJSON(err.responseText))); + } + }); +}); + +var addRoom = function(data) { + row = "" + + ""+data.room_id+"" + + ""+data.membership+"" + + ""+data.room_alias+"" + + ""+data.latest_message+"" + + ""; + $("#rooms").append(row); +}; + +$('.sendMessage').live('click', function() { + var roomId = $("#roomId").val(); + var body = $("#messageBody").val(); + var msgId = $.now(); + + if (roomId.length === 0 || body.length === 0) { + return; + } + + var url = "http://localhost:8080/matrix/client/api/v1/rooms/$roomid/messages/$user/$msgid?access_token=$token"; + url = url.replace("$token", accountInfo.access_token); + url = url.replace("$roomid", encodeURIComponent(roomId)); + url = url.replace("$user", encodeURIComponent(accountInfo.user_id)); + url = url.replace("$msgid", msgId); + + var data = { + msgtype: "m.text", + body: body + }; + + $.ajax({ + url: url, + type: "PUT", + contentType: "application/json; charset=utf-8", + data: JSON.stringify(data), + dataType: "json", + success: function(data) { + $("#messageBody").val(""); + // wipe the table and reload it. Using the event stream would be the best + // solution but that is out of scope of this fiddle. + $("#rooms").find("tr:gt(0)").remove(); + getCurrentRoomList(); + }, + error: function(err) { + alert(JSON.stringify($.parseJSON(err.responseText))); + } + }); +}); From eee7cd7730c9eadb1f31b6d14acedbddea05e355 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Wed, 20 Aug 2014 13:47:20 +0100 Subject: [PATCH 85/95] Added more jsfiddles. --- jsfiddles/event_stream/demo.css | 17 ++++ jsfiddles/event_stream/demo.html | 23 +++++ jsfiddles/event_stream/demo.js | 142 +++++++++++++++++++++++++++ jsfiddles/room_memberships/demo.css | 17 ++++ jsfiddles/room_memberships/demo.html | 37 +++++++ jsfiddles/room_memberships/demo.js | 139 ++++++++++++++++++++++++++ 6 files changed, 375 insertions(+) create mode 100644 jsfiddles/event_stream/demo.css create mode 100644 jsfiddles/event_stream/demo.html create mode 100644 jsfiddles/event_stream/demo.js create mode 100644 jsfiddles/room_memberships/demo.css create mode 100644 jsfiddles/room_memberships/demo.html create mode 100644 jsfiddles/room_memberships/demo.js diff --git a/jsfiddles/event_stream/demo.css b/jsfiddles/event_stream/demo.css new file mode 100644 index 00000000..48a55f37 --- /dev/null +++ b/jsfiddles/event_stream/demo.css @@ -0,0 +1,17 @@ +.loggedin { + visibility: hidden; +} + +p { + font-family: monospace; +} + +table +{ + border-spacing:5px; +} + +th,td +{ + padding:5px; +} diff --git a/jsfiddles/event_stream/demo.html b/jsfiddles/event_stream/demo.html new file mode 100644 index 00000000..ee4fc3ea --- /dev/null +++ b/jsfiddles/event_stream/demo.html @@ -0,0 +1,23 @@ +
+

This event stream demo requires a home server to be running on http://localhost:8080

+
+
+ + + +
+
+
+ +
+

+ + + + + + + +
Room IDLatest message
+
+ diff --git a/jsfiddles/event_stream/demo.js b/jsfiddles/event_stream/demo.js new file mode 100644 index 00000000..b657b809 --- /dev/null +++ b/jsfiddles/event_stream/demo.js @@ -0,0 +1,142 @@ +var accountInfo = {}; + +var eventStreamInfo = { + from: "END" +}; + +var roomInfo = []; + +var longpollEventStream = function() { + var url = "http://localhost:8080/matrix/client/api/v1/events?access_token=$token&from=$from"; + url = url.replace("$token", accountInfo.access_token); + url = url.replace("$from", eventStreamInfo.from); + + $.getJSON(url, function(data) { + eventStreamInfo.from = data.end; + + var hasNewLatestMessage = false; + for (var i=0; i"+roomList[i].room_id+"" + + ""+roomList[i].latest_message+"" + + ""; + rows += row; + } + + $("#rooms").append(rows); +}; + diff --git a/jsfiddles/room_memberships/demo.css b/jsfiddles/room_memberships/demo.css new file mode 100644 index 00000000..48a55f37 --- /dev/null +++ b/jsfiddles/room_memberships/demo.css @@ -0,0 +1,17 @@ +.loggedin { + visibility: hidden; +} + +p { + font-family: monospace; +} + +table +{ + border-spacing:5px; +} + +th,td +{ + padding:5px; +} diff --git a/jsfiddles/room_memberships/demo.html b/jsfiddles/room_memberships/demo.html new file mode 100644 index 00000000..96232e82 --- /dev/null +++ b/jsfiddles/room_memberships/demo.html @@ -0,0 +1,37 @@ +
+

This room membership demo requires a home server to be running on http://localhost:8080

+
+
+ + + +
+
+
+ +
+
+ + + + +
+
+ + +
+ + + + + + + + +
Room IDMy stateRoom Alias
+
+ diff --git a/jsfiddles/room_memberships/demo.js b/jsfiddles/room_memberships/demo.js new file mode 100644 index 00000000..b3ce0f0e --- /dev/null +++ b/jsfiddles/room_memberships/demo.js @@ -0,0 +1,139 @@ +var accountInfo = {}; + +var showLoggedIn = function(data) { + accountInfo = data; + getCurrentRoomList(); + $(".loggedin").css({visibility: "visible"}); +}; + +$('.login').live('click', function() { + var user = $("#userLogin").val(); + var password = $("#passwordLogin").val(); + $.ajax({ + url: "http://localhost:8080/matrix/client/api/v1/login", + type: "POST", + contentType: "application/json; charset=utf-8", + data: JSON.stringify({ user: user, password: password, type: "m.login.password" }), + dataType: "json", + success: function(data) { + $("#rooms").find("tr:gt(0)").remove(); + showLoggedIn(data); + }, + error: function(err) { + alert(JSON.stringify($.parseJSON(err.responseText))); + } + }); +}); + +var getCurrentRoomList = function() { + $("#roomId").val(""); + // wipe the table and reload it. Using the event stream would be the best + // solution but that is out of scope of this fiddle. + $("#rooms").find("tr:gt(0)").remove(); + + var url = "http://localhost:8080/matrix/client/api/v1/im/sync?access_token=" + accountInfo.access_token + "&from=END&to=START&limit=1"; + $.getJSON(url, function(data) { + for (var i=0; i"+data.room_id+"" + + ""+data.membership+"" + + ""+data.room_alias+"" + + ""; + $("#rooms").append(row); +}; + +$('.changeMembership').live('click', function() { + var roomId = $("#roomId").val(); + var member = $("#targetUser").val(); + var membership = $("#membership").val(); + + if (roomId.length === 0) { + return; + } + + var url = "http://localhost:8080/matrix/client/api/v1/rooms/$roomid/members/$user/state?access_token=$token"; + url = url.replace("$token", accountInfo.access_token); + url = url.replace("$roomid", encodeURIComponent(roomId)); + url = url.replace("$user", encodeURIComponent(member)); + + if (membership === "leave") { + $.ajax({ + url: url, + type: "DELETE", + contentType: "application/json; charset=utf-8", + dataType: "json", + success: function(data) { + getCurrentRoomList(); + }, + error: function(err) { + alert(JSON.stringify($.parseJSON(err.responseText))); + } + }); + } + else { + var data = { + membership: membership + }; + + $.ajax({ + url: url, + type: "PUT", + contentType: "application/json; charset=utf-8", + data: JSON.stringify(data), + dataType: "json", + success: function(data) { + getCurrentRoomList(); + }, + error: function(err) { + alert(JSON.stringify($.parseJSON(err.responseText))); + } + }); + } +}); + +$('.joinAlias').live('click', function() { + var roomAlias = $("#roomAlias").val(); + var url = "http://localhost:8080/matrix/client/api/v1/join/$roomalias?access_token=$token"; + url = url.replace("$token", accountInfo.access_token); + url = url.replace("$roomalias", encodeURIComponent(roomAlias)); + $.ajax({ + url: url, + type: "PUT", + contentType: "application/json; charset=utf-8", + data: JSON.stringify({}), + dataType: "json", + success: function(data) { + getCurrentRoomList(); + }, + error: function(err) { + alert(JSON.stringify($.parseJSON(err.responseText))); + } + }); +}); From d5da30422ce56a6c777e2f7b9f14c23e4474805e Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Wed, 20 Aug 2014 16:45:59 +0100 Subject: [PATCH 86/95] Added final jsfiddle: an example app demonstrating most of the c2s api. --- jsfiddles/example_app/demo.css | 43 +++++ jsfiddles/example_app/demo.html | 56 ++++++ jsfiddles/example_app/demo.js | 303 ++++++++++++++++++++++++++++++++ 3 files changed, 402 insertions(+) create mode 100644 jsfiddles/example_app/demo.css create mode 100644 jsfiddles/example_app/demo.html create mode 100644 jsfiddles/example_app/demo.js diff --git a/jsfiddles/example_app/demo.css b/jsfiddles/example_app/demo.css new file mode 100644 index 00000000..4c1e157c --- /dev/null +++ b/jsfiddles/example_app/demo.css @@ -0,0 +1,43 @@ +.roomListDashboard, .roomContents, .sendMessageForm { + visibility: hidden; +} + +.roomList { + background-color: #909090; +} + +.messageWrapper { + background-color: #EEEEEE; + height: 400px; + overflow: scroll; +} + +.membersWrapper { + background-color: #EEEEEE; + height: 200px; + width: 50%; + overflow: scroll; +} + +.textEntry { + width: 100% +} + +p { + font-family: monospace; +} + +table +{ + border-spacing:5px; +} + +th,td +{ + padding:5px; +} + +.roomList tr:not(:first-child):hover { + background-color: orange; + cursor: pointer; +} diff --git a/jsfiddles/example_app/demo.html b/jsfiddles/example_app/demo.html new file mode 100644 index 00000000..0af946f6 --- /dev/null +++ b/jsfiddles/example_app/demo.html @@ -0,0 +1,56 @@ + + +
+
+ + +
+ + + + + + + + +
RoomMy stateLatest message
+
+ +
+

Select a room

+
+ + + +
+
+
+ + +
+
+ +
+

Member list:

+
+ + + +
+
+
+ diff --git a/jsfiddles/example_app/demo.js b/jsfiddles/example_app/demo.js new file mode 100644 index 00000000..295597f0 --- /dev/null +++ b/jsfiddles/example_app/demo.js @@ -0,0 +1,303 @@ +var accountInfo = {}; + +var eventStreamInfo = { + from: "END" +}; + +var roomInfo = []; +var memberInfo = []; +var viewingRoomId; + +// ************** Event Streaming ************** +var longpollEventStream = function() { + var url = "http://localhost:8080/matrix/client/api/v1/events?access_token=$token&from=$from"; + url = url.replace("$token", accountInfo.access_token); + url = url.replace("$from", eventStreamInfo.from); + + $.getJSON(url, function(data) { + eventStreamInfo.from = data.end; + + var hasNewLatestMessage = false; + var updatedMemberList = false; + var i=0; + var j=0; + for (i=0; i 0) { + data.room_alias_name = roomAlias; + } + $.ajax({ + url: "http://localhost:8080/matrix/client/api/v1/rooms?access_token="+accountInfo.access_token, + type: "POST", + contentType: "application/json; charset=utf-8", + data: JSON.stringify(data), + dataType: "json", + success: function(response) { + $("#roomAlias").val(""); + response.membership = "join"; // you are automatically joined into every room you make. + response.latest_message = ""; + + roomInfo.push(response); + setRooms(roomInfo); + }, + error: function(err) { + alert(JSON.stringify($.parseJSON(err.responseText))); + } + }); +}); + +// ************** Getting current state ************** +var getCurrentRoomList = function() { + var url = "http://localhost:8080/matrix/client/api/v1/im/sync?access_token=" + accountInfo.access_token + "&from=END&to=START&limit=1"; + $.getJSON(url, function(data) { + for (var i=0; i=0; --i) { + addMessage(data.chunk[i]); + } + }); +}; + +var getMemberList = function(roomId) { + $("#members").empty(); + memberInfo = []; + var url = "http://localhost:8080/matrix/client/api/v1/rooms/" + roomId + "/members/list?access_token=" + accountInfo.access_token; + $.getJSON(url, function(data) { + for (var i=0; i"+roomList[i].room_id+"" + + ""+roomList[i].membership+"" + + ""+roomList[i].latest_message+"" + + ""; + rows += row; + } + + $("#rooms").append(rows); + + $('#rooms').find("tr").click(function(){ + var roomId = $(this).find('td:eq(0)').text(); + var membership = $(this).find('td:eq(1)').text(); + if (membership !== "join") { + console.log("Joining room " + roomId); + var url = "http://localhost:8080/matrix/client/api/v1/rooms/$roomid/members/$user/state?access_token=$token"; + url = url.replace("$token", accountInfo.access_token); + url = url.replace("$roomid", encodeURIComponent(roomId)); + url = url.replace("$user", encodeURIComponent(accountInfo.user_id)); + $.ajax({ + url: url, + type: "PUT", + contentType: "application/json; charset=utf-8", + data: JSON.stringify({membership: "join"}), + dataType: "json", + success: function(data) { + loadRoomContent(roomId); + getCurrentRoomList(); + }, + error: function(err) { + alert(JSON.stringify($.parseJSON(err.responseText))); + } + }); + } + else { + loadRoomContent(roomId); + } + }); +}; + +var addMessage = function(data) { + var row = "" + + ""+data.user_id+"" + + ""+data.content.body+"" + + ""; + $("#messages").append(row); +}; + +var addMember = function(data) { + var row = "" + + ""+data.target_user_id+"" + + ""+data.content.membership+"" + + ""; + $("#members").append(row); +}; + From 1f3811b1b8f4742c07cb07f0bc56038ce15a8231 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Fri, 29 Aug 2014 15:01:46 +0100 Subject: [PATCH 87/95] Fix JSFiddles to work with the new C-S API. --- jsfiddles/create_room_send_msg/demo.js | 17 +++--- jsfiddles/event_stream/demo.js | 18 +++---- jsfiddles/example_app/demo.js | 56 +++++++++++++------- jsfiddles/register_login/demo.js | 4 +- jsfiddles/room_memberships/demo.html | 6 +-- jsfiddles/room_memberships/demo.js | 73 ++++++++++++-------------- 6 files changed, 93 insertions(+), 81 deletions(-) diff --git a/jsfiddles/create_room_send_msg/demo.js b/jsfiddles/create_room_send_msg/demo.js index c17eb26b..db2ae2d6 100644 --- a/jsfiddles/create_room_send_msg/demo.js +++ b/jsfiddles/create_room_send_msg/demo.js @@ -25,11 +25,12 @@ $('.login').live('click', function() { }); var getCurrentRoomList = function() { - var url = "http://localhost:8080/matrix/client/api/v1/im/sync?access_token=" + accountInfo.access_token + "&from=END&to=START&limit=1"; + var url = "http://localhost:8080/matrix/client/api/v1/initialSync?access_token=" + accountInfo.access_token + "&limit=1"; $.getJSON(url, function(data) { - for (var i=0; i=0; --i) { addMessage(data.chunk[i]); @@ -190,7 +193,8 @@ var getMessages = function(roomId) { var getMemberList = function(roomId) { $("#members").empty(); memberInfo = []; - var url = "http://localhost:8080/matrix/client/api/v1/rooms/" + roomId + "/members/list?access_token=" + accountInfo.access_token; + var url = "http://localhost:8080/matrix/client/api/v1/rooms/" + + encodeURIComponent(roomId) + "/members?access_token=" + accountInfo.access_token; $.getJSON(url, function(data) { for (var i=0; i"; + } + else if (data.content.membership === "join") { + msg = "joined the room"; + } + else if (data.content.membership === "leave") { + msg = "left the room"; + } + else { + msg = "" + data.content.membership + ""; + } + } + var row = "" + ""+data.user_id+"" + - ""+data.content.body+"" + + ""+msg+"" + ""; $("#messages").append(row); }; var addMember = function(data) { var row = "" + - ""+data.target_user_id+"" + + ""+data.state_key+"" + ""+data.content.membership+"" + ""; $("#members").append(row); diff --git a/jsfiddles/register_login/demo.js b/jsfiddles/register_login/demo.js index 1644f76a..270a9678 100644 --- a/jsfiddles/register_login/demo.js +++ b/jsfiddles/register_login/demo.js @@ -45,7 +45,7 @@ $('.login').live('click', function() { var user = $("#userLogin").val(); var password = $("#passwordLogin").val(); $.getJSON("http://localhost:8080/matrix/client/api/v1/login", function(data) { - if (data.type !== "m.login.password") { + if (data.flows[0].type !== "m.login.password") { alert("I don't know how to login with this type: " + data.type); return; } @@ -60,7 +60,7 @@ $('.logout').live('click', function() { }); $('.testToken').live('click', function() { - var url = "http://localhost:8080/matrix/client/api/v1/im/sync?access_token=" + accountInfo.access_token + "&from=END&to=START&limit=1"; + var url = "http://localhost:8080/matrix/client/api/v1/initialSync?access_token=" + accountInfo.access_token + "&limit=1"; $.getJSON(url, function(data) { $("#imSyncText").text(JSON.stringify(data, undefined, 2)); }).fail(function(err) { diff --git a/jsfiddles/room_memberships/demo.html b/jsfiddles/room_memberships/demo.html index 96232e82..4c1bf6b4 100644 --- a/jsfiddles/room_memberships/demo.html +++ b/jsfiddles/room_memberships/demo.html @@ -14,9 +14,9 @@ diff --git a/jsfiddles/room_memberships/demo.js b/jsfiddles/room_memberships/demo.js index b3ce0f0e..91cc96ab 100644 --- a/jsfiddles/room_memberships/demo.js +++ b/jsfiddles/room_memberships/demo.js @@ -4,6 +4,14 @@ var showLoggedIn = function(data) { accountInfo = data; getCurrentRoomList(); $(".loggedin").css({visibility: "visible"}); + $("#membership").change(function() { + if ($("#membership").val() === "invite") { + $("#targetUser").css({visibility: "visible"}); + } + else { + $("#targetUser").css({visibility: "hidden"}); + } +}); }; $('.login').live('click', function() { @@ -31,10 +39,11 @@ var getCurrentRoomList = function() { // solution but that is out of scope of this fiddle. $("#rooms").find("tr:gt(0)").remove(); - var url = "http://localhost:8080/matrix/client/api/v1/im/sync?access_token=" + accountInfo.access_token + "&from=END&to=START&limit=1"; + var url = "http://localhost:8080/matrix/client/api/v1/initialSync?access_token=" + accountInfo.access_token + "&limit=1"; $.getJSON(url, function(data) { - for (var i=0; i Date: Sun, 31 Aug 2014 14:51:37 +0100 Subject: [PATCH 88/95] change the world: make the default matrix API URL prefix /_matrix rather than /matrix to make it easier for existing websites to mount a HS in their namespace without collisions. perl -pi -e 's#/matrix#/_matrix#g' ./cmdclient/console.py ./docs/client-server/howto.rst ./docs/client-server/specification.rst ./docs/client-server/swagger_matrix/directory ./docs/client-server/swagger_matrix/events ./docs/client-server/swagger_matrix/login ./docs/client-server/swagger_matrix/presence ./docs/client-server/swagger_matrix/profile ./docs/client-server/swagger_matrix/registration ./docs/client-server/swagger_matrix/rooms ./docs/server-server/specification.rst ./graph/graph.py ./jsfiddles/create_room_send_msg/demo.js ./jsfiddles/event_stream/demo.js ./jsfiddles/example_app/demo.js ./jsfiddles/register_login/demo.js ./jsfiddles/room_memberships/demo.js ./synapse/api/urls.py ./tests/federation/test_federation.py ./tests/handlers/test_presence.py ./tests/handlers/test_typing.py ./tests/rest/test_events.py ./tests/rest/test_presence.py ./tests/rest/test_profile.py ./tests/rest/test_rooms.py ./webclient/components/fileUpload/file-upload-service.js ./webclient/components/matrix/matrix-service.js --- jsfiddles/create_room_send_msg/demo.js | 8 ++++---- jsfiddles/event_stream/demo.js | 8 ++++---- jsfiddles/example_app/demo.js | 18 +++++++++--------- jsfiddles/register_login/demo.js | 8 ++++---- jsfiddles/room_memberships/demo.js | 10 +++++----- 5 files changed, 26 insertions(+), 26 deletions(-) diff --git a/jsfiddles/create_room_send_msg/demo.js b/jsfiddles/create_room_send_msg/demo.js index db2ae2d6..61044da7 100644 --- a/jsfiddles/create_room_send_msg/demo.js +++ b/jsfiddles/create_room_send_msg/demo.js @@ -10,7 +10,7 @@ $('.login').live('click', function() { var user = $("#userLogin").val(); var password = $("#passwordLogin").val(); $.ajax({ - url: "http://localhost:8080/matrix/client/api/v1/login", + url: "http://localhost:8080/_matrix/client/api/v1/login", type: "POST", contentType: "application/json; charset=utf-8", data: JSON.stringify({ user: user, password: password, type: "m.login.password" }), @@ -25,7 +25,7 @@ $('.login').live('click', function() { }); var getCurrentRoomList = function() { - var url = "http://localhost:8080/matrix/client/api/v1/initialSync?access_token=" + accountInfo.access_token + "&limit=1"; + var url = "http://localhost:8080/_matrix/client/api/v1/initialSync?access_token=" + accountInfo.access_token + "&limit=1"; $.getJSON(url, function(data) { var rooms = data.rooms; for (var i=0; i=0; --i) { @@ -193,7 +193,7 @@ var getMessages = function(roomId) { var getMemberList = function(roomId) { $("#members").empty(); memberInfo = []; - var url = "http://localhost:8080/matrix/client/api/v1/rooms/" + + var url = "http://localhost:8080/_matrix/client/api/v1/rooms/" + encodeURIComponent(roomId) + "/members?access_token=" + accountInfo.access_token; $.getJSON(url, function(data) { for (var i=0; i Date: Tue, 2 Sep 2014 15:29:38 +0100 Subject: [PATCH 89/95] Updated howto.rst to use the new APIs. Updated JSFiddles to use 8008. Linked new fiddles with howto.rst. Added more explanations. --- jsfiddles/create_room_send_msg/demo.html | 2 +- jsfiddles/create_room_send_msg/demo.js | 8 +++---- jsfiddles/event_stream/demo.html | 2 +- jsfiddles/event_stream/demo.js | 8 +++---- jsfiddles/example_app/demo.html | 2 +- jsfiddles/example_app/demo.js | 28 ++++++++++++++---------- jsfiddles/register_login/demo.html | 2 +- jsfiddles/register_login/demo.js | 8 +++---- jsfiddles/room_memberships/demo.html | 2 +- jsfiddles/room_memberships/demo.js | 10 ++++----- 10 files changed, 39 insertions(+), 33 deletions(-) diff --git a/jsfiddles/create_room_send_msg/demo.html b/jsfiddles/create_room_send_msg/demo.html index 31c26c76..088ff7ac 100644 --- a/jsfiddles/create_room_send_msg/demo.html +++ b/jsfiddles/create_room_send_msg/demo.html @@ -1,5 +1,5 @@
-

This room creation / message sending demo requires a home server to be running on http://localhost:8080

+

This room creation / message sending demo requires a home server to be running on http://localhost:8008

diff --git a/jsfiddles/create_room_send_msg/demo.js b/jsfiddles/create_room_send_msg/demo.js index 61044da7..3dc72638 100644 --- a/jsfiddles/create_room_send_msg/demo.js +++ b/jsfiddles/create_room_send_msg/demo.js @@ -10,7 +10,7 @@ $('.login').live('click', function() { var user = $("#userLogin").val(); var password = $("#passwordLogin").val(); $.ajax({ - url: "http://localhost:8080/_matrix/client/api/v1/login", + url: "http://localhost:8008/_matrix/client/api/v1/login", type: "POST", contentType: "application/json; charset=utf-8", data: JSON.stringify({ user: user, password: password, type: "m.login.password" }), @@ -25,7 +25,7 @@ $('.login').live('click', function() { }); var getCurrentRoomList = function() { - var url = "http://localhost:8080/_matrix/client/api/v1/initialSync?access_token=" + accountInfo.access_token + "&limit=1"; + var url = "http://localhost:8008/_matrix/client/api/v1/initialSync?access_token=" + accountInfo.access_token + "&limit=1"; $.getJSON(url, function(data) { var rooms = data.rooms; for (var i=0; i -

This event stream demo requires a home server to be running on http://localhost:8080

+

This event stream demo requires a home server to be running on http://localhost:8008

diff --git a/jsfiddles/event_stream/demo.js b/jsfiddles/event_stream/demo.js index 997d1a22..5c81e08c 100644 --- a/jsfiddles/event_stream/demo.js +++ b/jsfiddles/event_stream/demo.js @@ -7,7 +7,7 @@ var eventStreamInfo = { var roomInfo = []; var longpollEventStream = function() { - var url = "http://localhost:8080/_matrix/client/api/v1/events?access_token=$token&from=$from"; + var url = "http://localhost:8008/_matrix/client/api/v1/events?access_token=$token&from=$from"; url = url.replace("$token", accountInfo.access_token); url = url.replace("$from", eventStreamInfo.from); @@ -48,7 +48,7 @@ $('.login').live('click', function() { var user = $("#userLogin").val(); var password = $("#passwordLogin").val(); $.ajax({ - url: "http://localhost:8080/_matrix/client/api/v1/login", + url: "http://localhost:8008/_matrix/client/api/v1/login", type: "POST", contentType: "application/json; charset=utf-8", data: JSON.stringify({ user: user, password: password, type: "m.login.password" }), @@ -65,7 +65,7 @@ $('.login').live('click', function() { var getCurrentRoomList = function() { $("#roomId").val(""); - var url = "http://localhost:8080/_matrix/client/api/v1/initialSync?access_token=" + accountInfo.access_token + "&limit=1"; + var url = "http://localhost:8008/_matrix/client/api/v1/initialSync?access_token=" + accountInfo.access_token + "&limit=1"; $.getJSON(url, function(data) { var rooms = data.rooms; for (var i=0; i