XMPP Component Protocol
Information about the protocol for the OLPC-specific XMPP server component.
See also the current protocol
Use Cases
We intend to be support the following use cases with this solution:
- Idle: The user has switched the laptop on for the first time, or the user is currently not doing anything else, and the mesh view is selected. A reasonable number of different activities should be displayed, along with the users for the user to select
- Buddy Search: The user is looking for a particular individual on the same school and searches for them by one of a number of properties, such as nickname or potentially full name or e-mail address. The mesh view shows just these individuals who can be added as friends or invited to activities.
- Activity Search: The user is looking to take part in a particular activity and searches for instances by properties such as name, type or tags. The mesh view shows a reasonable number of matching activities for the user to join.
- Friends: The user has a number of buddies that they have marked as friends. The laptop should always be able to show the activities these people are currently doing.
Current behaviour which we don't intend to support any longer:
- Lone Buddies: When idle, the mesh view shows a lot of buddies who are neither friends nor currently in an activity. These individuals are not only uninteresting to the user, but it represents a privacy violation to propogate their presence information if they are not currently engaged in a public activity or have made themselves searchable.
Design Goals
- It can be easily deployed without needing anything more specific than a Jabber server that has support for PEP and MUC.
- It doesn't rely on any abnormal configuration/behaviour on the Jabber server that breach the normal XMPP approaches to privacy.
- It doesn't rely on being able to access information from the server's MUC or PEP implementations using any non-standard mechanisms.
- It can scale better than the current protocol, possibly even to 50,000 laptops if we consider the G1G1 programme, given a suitably scalable server.
- It adds the ability to do server-side searching which we currently lack (except by dint of the fact that we currently see everything so we can implement filtering on the client side).
Data Acquisition
Currently buddy properties and activity properties and are implemented using PEP nodes. It's intended that the buddy properties, buddies activities and current activity continue using PEP nodes. Activity properties are changed to drop PEP and just use the current <message> based protocol inside the muc.
This server component shall appear as a JID which can be found using service discovery, which when subscribed to, will subscribe to your presence (and hence PEP notifications), and therefore be privy to your buddy properties and make them available for others to search and be notified of changes. If this is undesirable for privacy reasons, the component JID should simply be removed from the roster.
The component JID can also be invited into activities in order to be made aware of the activity's existence, properties and current membership (by observing the <presence> stanzas of the MUC members). This information is then made available for others to search and be notified of changes.
Laptops still receive information about their buddies' current activity (FIXME: maybe?) and properties directly from them using PEP notifications.
Protocol Overview
Laptops query the server component about activities and buddies. For both kinds of query, there is at most one result set active at any given time. Result sets are managed using the Result Set Management (RSM) XMPP extension, as described in XEP-0059.
The component sends notifications to clients based on their currently visible result sets.
Service Discovery
Disco response:
FIXME: choose the right category/type
<iq from='gadget.jabber.laptop.org' to='joed@jabber.laptop.org' id='111' type='result'> <query xmlns='http://jabber.org/protocol/disco#info'> <identity category='collaboration' type='gadget' name='OLPC Gadget'/> <feature var='http://laptop.org/xmpp/activity'/> <feature var='http://laptop.org/xmpp/buddy'/> </query> </iq>
Activity Protocol
Namespace: http://laptop.org/xmpp/activity
Query
Return all activities matching the properties given.
Example: XO searches for all instances of the "Connect" activity.
<iq type="get" from="joe@jabber.laptop.org" to="gadget.jabber.laptop.org"> <query xmlns="..." id="query1"> <activity> <properties xmlns="..."> <property type="str" name="type">org.laptop.Connect</property> </properties> </activity> </query> </iq>
Example: XO searches for an activity by muc jid.
<iq type="get" from="joe@jabber.laptop.org" to="gadget.jabber.laptop.org"> <query xmlns="..." id="query1"> <activity room="abcde123@conference.example.org" /> </query> </iq>
Example: XO can search for a union of activities matching different search criteria (to be subscribed to changes of multiple activities)
<iq type="get" from="joe@jabber.laptop.org" to="gadget.jabber.laptop.org"> <query xmlns="..." id="query1"> <activity> <properties xmlns="..."> <property type="str" name="type">org.laptop.Connect</property> </properties> </activity> <activity room="abcde123@conference.example.org" /> </query> </iq>
Search results contain information about the matching activities, including their properties and their current members.
Example: component replies to query:
<iq type="result" from="joe@jabber.laptop.org" to="gadget.jabber.laptop.org"> <query xmlns="..." id="query1"> <activity room="connect4@conference.jabber.laptop.org"> <properties xmlns="..."> <property type="str" name="type">org.laptop.Connect</property> </properties> <buddy jid="alice@jabber.laptop.org" /> <buddy jid="bob@jabber.laptop.org" /> ... </activity> <activity room="abcde123@conference.example.org"> <properties xmlns="..."> ... </properties> ... </activity> ... </query> </iq>
Any buddies who are known by the component, and not currently visible in the XO's buddy result set, will have their properties included in full.
Example: component replies to query with supplemental buddy information.
<iq type="result" from="joe@jabber.laptop.org" to="gadget.jabber.laptop.org"> <query xmlns="..." id="query1"> <activity room="..."> <properties xmlns="..."> <property type="str" name="type">org.laptop.Connect</property> </properties> <buddy jid="alice@jabber.laptop.org" /> <buddy jid="bob@jabber.laptop.org"> <properties xmlns="..."> <property type="str" name="color">#005FE4,#00A0FF</property> ... </properties> </buddy> ... </activity> ... </query> </iq>
Example: XO requests 20 random activities
<iq type="get" from="joe@jabber.laptop.org" to="gadget.jabber.laptop.org"> <query xmlns="..." id="query1"> <random max="20"> </query> </iq>
Change properties
Example: Joe defines activity tags
<message type="groupchat" from='abcde123@conference.jabber.laptop.org/joe', to='abcd123@conference.jabber.laptop.org'> <properties xmlns="http://laptop.org/xmpp/activity-properties" activity="abcd123" room="abcd123@conference.jabber.laptop.org"> <property type="str" name="tags">news, france</property> </properties> <amp xmlns='http://jabber.org/protocol/amp'> <rule condition='deliver-at' value='stored' action='error'/> </amp> </message>
Change announcement
Example: component notifies client about change in activity properties:
<message type="notice" from="gadget.jabber.laptop.org" to="joe@jabber.laptop.org"> <change xmlns="http://laptop.org/xmpp/activity"> <properties xmlns="http://laptop.org/xmpp/activity-properties" activity="abcde123" room="activity123@conference.laptop.org"> <property type="str" name="tags">news, france</property> </properties> </change> <amp xmlns='http://jabber.org/protocol/amp'> <rule condition='deliver-at' value='stored' action='error'/> </amp> </message>
FIXME: This is a bit weird. Why not add a <activity room=...> node inside <change> ? What's the point of this activity attribute? Maybe we could drop <change> and use <activity> instead to be more coherent with the join/departure messages?
Join announcement
Component tells XO that somebody has joined an activity.
<message type="notice" from="gadget.jabber.laptop.org" to="joe@jabber.laptop.org"> <activity xmlns="http://laptop.org/xmpp/activity" activity="abcde123""> <joined jid="bob@jabber.laptop.org" /> </activity> <amp xmlns='http://jabber.org/protocol/amp'> <rule condition='deliver-at' value='stored' action='error'/> </amp> </message>
Departure announcement
Component tells XO that someone has departed an actvitiy.
<message type="notice" from="gadget.jabber.laptop.org" to="joe@jabber.laptop.org"> <activity xmlns="http://laptop.org/xmpp/activity" activity="abcde123""> <left jid="bob@jabber.laptop.org" /> </activity> <amp xmlns='http://jabber.org/protocol/amp'> <rule condition='deliver-at' value='stored' action='error'/> </amp> </message>
Closed
Component tells XO that all participants have departed an activity.
<message type="notice" from="gadget.jabber.laptop.org" to="joe@jabber.laptop.org"> <activity xmlns="http://laptop.org/xmpp/activity" activity="abcde123""> <closed/> </activity> <amp xmlns='http://jabber.org/protocol/amp'> <rule condition='deliver-at' value='stored' action='error'/> </amp> </message>
To share an activity, one of its participant have to invite the indexer following the same protocol as when inviting a buddy
First Joe sends a pseudo-invitation directly to the indexer
<message from='joe@jabber.laptop.org' to='gadget.jabber.laptop.org'> <properties xmlns='http://laptop.org/xmpp/activity-properties' activity='abcde123' room='abcde123@conference.jabber.laptop.org'> <property type='str' name='type'>org.laptop.Connect</property> <property type='str' name='name'>Connect Activity</property> <property type='str' name='tags'/> <property type='str' name='color'>#0d1c38,#49bce4</property> </properties> </message>
Joe sends invitation to the indexer via MUC service
<message to='abcde123@conference.jabber.laptop.org'> <x xmlns="http://jabber.org/protocol/muc#user"> <invite to="gadget.jabber.laptop.org"/> </x> </message>
MUC service relays invitation to the indexer
<message type='normal' from='abcde123@conference.jabber.laptop.org' to='gadget.jabber.laptop.org'> <x xmlns='http://jabber.org/protocol/muc#user'> <invite from='joe@jabber.laptop.org'> <reason/> </invite> </x> <x xmlns='jabber:x:conference' jid='abcde123@conference.jabber.laptop.org'/> </message>
When it joins the muc, the indexer announce its presence as follow:
<presence to='abcde123@conference.jabber.laptop.org', from='abcde123@conference.jabber.laptop.org/inspector'> <inspector xmlns='http://laptop.org/xmpp/activity' /> <x xmlns="http://jabber.org/protocol/muc" /> </presence>
Close query
Client is not interested anymore by change notifications for this query
<message from="joe@jabber.laptop.org" to="gadget.jabber.laptop.org"> <close xmlns='http://laptop.org/xmpp/activity' id="query1"/> </message>
Buddy Protocol
Namespace: http://laptop.org/xmpp/buddy
Query
Fetch information about buddies. Similar to activity querying.
By JID:
<iq type="get" from="joe@jabber.laptop.org" to="gadget.jabber.laptop.org"> <query xmlns="http://laptop.org/xmpp/buddy" id="query1"> <buddy jid="bob@jabber.laptop.org" /> </query> </iq>
Component replies to query:
<iq type="result" from="gadget.jabber.laptop.org" to="joe@jabber.laptop.org"> <query xmlns="http://laptop.org/xmpp/buddy" id="query1"> <buddy jid="bob@jabber.laptop.org"> <properties xmlns="http://laptop.org/xmpp/buddy-properties"> <property type="str" name="color">#005FE4,#00A0FF</property> </properties> </buddy> </query> </iq>
XO requests 30 random buddies
<iq type="get" from="joe@jabber.laptop.org" to="gadget.jabber.laptop.org"> <query xmlns="http://laptop.org/xmpp/buddy" id="query1"> <random max="30" /> </query> </iq>
Changed
Buddy properties change:
<message type="notice" from="gadget.jabber.laptop.org" to="joe@jabber.laptop.org"> <change xmlns="http://laptop.org/xmpp/buddy" jid="bob@jabber.laptop.org"> <properties xmlns="http://laptop.org/xmpp/buddy-properties"> <property type="str" name="color">#005FE4,#00A0FF</property> </properties> </change> <amp xmlns='http://jabber.org/protocol/amp'> <rule condition='deliver-at' value='stored' action='error'/> </amp> </message>
Current activity change:
FIXME: Do we want to keep the same protocol as http://people.collabora.co.uk/~smcv/olpc.html#sect-id2251362 ? The "type" attribue is badly named as it's actually the activity-id
<message type="notice" from="gadget.jabber.laptop.org" to="joe@jabber.laptop.org"> <change xmlns="http://laptop.org/xmpp/buddy" jid="bob@jabber.laptop.org"> <activity xmlns="http://laptop.org/xmpp/current-activity", room="abcde123@conference.example.org", type="abcde123" /> </change> <amp xmlns='http://jabber.org/protocol/amp'> <rule condition='deliver-at' value='stored' action='error'/> </amp> </message>
Close query
Client is not interested anymore by change notifications for this query
<message from="joe@jabber.laptop.org" to="gadget.jabber.laptop.org"> <close xmlns='http://laptop.org/xmpp/buddy' id="query1"/> </message>
Unresolved issues and TODO
- Do we need to continue to expose activity ID's to the network?
- RM: Don't know; what are the pros and cons? I'd like it if we could dissociate the JID from any magical meaning conferred by the PS, so you can turn a normal MUC room on an arbitrary server into an activity by interacting with the indexer. Also, if rooms span servers, the local part of the room name is no longer unique.
- Buddy current activity. We still use PEP (as for Buddy properties) so our friend know when we switch activity. Component should also notify us when a buddy in our current search frame change his current activity.
- RM: Can we check where in the UI that current activity is used versus just knowing which buddy is in which activity? Daf seemed to think it wasn't really used. I'd like the indexer to not get involved with this ideally. The impression I got from Eben was that the point of the mesh view was more about showing you activities rather than buddies. So it's more important to just show activities with the relevant people around them, rather than one-activity-per-buddy.
- GD: current-activity is used to group buddies around activities in the mesh view. Activities list is used to create activity in the PS: a buddy announce he's in activity "foo", if PS doesn't know this activity yet it creates it and requests its properties. When it received foo's properties, foo is displayed in the mesh view
- Are we still using PEP for activity properties? If not, we'll have to search for activities each time a friend create/join an activity to fetch its properties before be able to display it in the GUI. That implies more D-Bus and networks round-trips. Basically we are always interested about friends activities so PS will have to manually handle them to be sure they are always in our "activity search frame" to be sure to receive activity notifications.
- RM: No, I'd rather do this by making the indexer push the relevant information to us somehow. I don't want to have to publish activity properties redundantly in PEP any more, and publishing activity data per buddy is wrong. A better alternative should be sought... I'd like to know what the intention of the friends view is - do we want to show activities floating around buddies, or is there some way to see "the" activity per buddy? Also, when you search for buddies, do we need to care about showing activities at all?
- GD: NEW If we implement a multi-views based solution, we could solve this by requesting a view containing all our friends and always keeping it open
- What about buddy search using not OLPC specific keys (alias, name, mail, ...)? Could be cool to have a UserSearch interface but maybe not very convenient to have to deal with 2 search API's
- RM: I spoke to Eben about this... currently the UI doesn't have a lot in the way of other search keys anyway. If the OLPC UI adds more user info then we can add them to the OLPC properties. The benefit of storing this information in the OLPC component is that we can place a clear privacy policy on it - if you subscribe to the indexer, people can search whatever info is in the OLPC properties. Arguably we could also just use XEP-0154: User Profile and make the indexer spy on/seach those, but it seems to just make life more complicated at the moment. I don't think we want to try and use two search APIs on the server though, because then we get into awkward "unioning" of search results.
- Design buddy/activity search TP API
- We need a way to specify the matching type (substring, equality, case insensitive?) in the <property> things we specify in a search request.
- DH: Suggest: make it exact by default, and give a different default match type to properties where that doesn't make sense.
- GD: According to Sugar ML discussion a good default would be accent insensitive, case insensitive. Not sure about equality vs substring
- We need a way to remove the indexer from the room when the activity goes private. Or should it just leave when it sees the private flag?
- NEW Do we really want to use XEP-0059? It doesn't seem designed to handle multi queries (no query ID) and long living queries
D-Bus API
org.laptop.Telepathy.Buddy
- SearchByProperties (a{sv}) -> o
- RequestRandom (uint: max) -> o
org.laptop.Telepathy.BuddyView
- Close()
Changes are notified on the Connection object using the BuddyInfo interface while the view is open.
Implement the group interface to track connected buddies.
If the view is "random" buddies could be added and removed during view's lifetime. If not, do we want to re-execute queries when new buddies turn online?
org.laptop.Telepathy.Activity
- SearchByProperties (a{sv}) -> o
- RequestRandom (uint: max) -> o
- SearchByParticipants (a(u)) -> o
org.laptop.Telepathy.ActivityView
- Close()
Implement the group interface to track activities
Same question as for buddies, can activities appear in an existing view?