IE retrospectiveResearch • Social Plan

this document was made to investigate and plan the social profile service before it was created


Social Profile connects users to real-time multiplayer and voice environments using third-party services to provide...

Matrix Standard Messaging Protocol

To bypass innovating our own custom message solutions, we refer to parts of the "Matrix" messaging spec as a guideline,

Matrix defines a set of open APIs for decentralised communication, suitable for securely publishing, persisting and subscribing to data over a global open federation of servers with no single point of control. Uses include Instant Messaging (IM), Voice over IP (VoIP) signalling, Internet of Things (IoT) communication, and bridging together existing communication silos - providing the basis of a new open real-time communication ecosystem.

This "client_server" page is notably useful. Our own messaging needs are comparatively simpler. Concepts from the spec we follow, loosely, include,

Social Profile Data

Database, Room.id and RoomMembership.id

Room.id and RoomMembership.id are compound indexes, composed of type and id values. For example,

There are three main benefits to this: 1) Details can be parsed from the id without queryng the database, for example the room type "DIRECT" is found in the id string. 2) One memberhip id can be used to derive related membership ids, dramatically simplifying some queries. 3) RethnkDB can optimally return the range of documents whose id value begins with the matching sub string, using parallel distributed processes or shards, so easy to query all membership documents associated with a room.

Database, Matrix-like Events (OLD)

[...] Typically each client action (e.g. sending a message) correlates with exactly one event. Each event has a type which is used to differentiate different kinds of data. type values MUST be uniquely globally namespaced [...] Events are usually sent in the context of a "Room".

{
  id: 'eventMessageStub-1234',
  type_name: 'RoomEventStub',
  event_type_name: 'message.text',
  party_sender_id: 'senderPartyId-1234',
  party_target_id: 'targetPartyId-1234',
  time: 1606768985127,
  content: {
    messageType: 'Message|Video|Call'
  }
}

Initially, we'll record two events: RoomEventState and RoomEventMessageStub. These correspond with two event categories described in the Matrix spec,

  1. Message events describe transient 'once-off' activity in a room such as instant messages, VoIP call setups, file transfers, etc. They generally describe communication activity. If message contents were being recorded (they're not), multiple message-event data would be used such as RoomEventMessageCall and RoomEventMessageText. Social-profile does not store message content and one RoomEventMessageStub is used for all message events.

  2. State events describe updates to a given piece of persistent information ('state') related to a room, such as the room's name, topic, membership, participating servers, etc. State is modelled as a lookup table of key/value pairs per room, with each key being a tuple of state_key and event type. Each state event updates the value of a given key. Ex, 'room.topic': 'VR Games' State can allow for things as a member being online or if a user is already in multiplayer room

Database, Matrix-like Event Graph (OLD)

Events exchanged in the context of a room are stored in a directed acyclic graph (DAG) called an 'event graph'. The partial ordering of this graph gives the chronological ordering of events within the room. [...]_

{
  roomEventBId_1234: {
    id: 'roomEventBId_1234',
    type_name: 'RoomEvents_ROOMID',
    room_id: 'roomAId_1234',
    event_type: 'RoomEventState',
    event_id: 'eventStateAId_1234',
    prev_event_id: 'roomEventAId_1234'
  },
  roomEventCId_1234: {
    id: 'roomEventCId_1234',
    type_name: 'RoomEvents_ROOMID',
    room_id: 'roomAId_1234',
    event_type: 'RoomEventMessage',
    event_id: 'eventMessageTextBId_1234',
    prev_event_id: 'roomEventBId_1234'
  }
}

The DAG only provides a partially-ordered sequence because race-conditions create event nodes with the same parent. This is a non-issue for us and we are not exactly recreating message histories anyway. I asked the maintainer of a Matrix client named "Fractal" about the DAG and he recommends ignoring it.

If we decided to use a depth field for sorting, we could follow the reference implemention here, see the mainline_sort function.

Database, Matrix-Like Rooms (OLD)

A room is a conceptual place where users can send and receive events. Events are sent to a room, and all participants in that room [...] receive the event.

The state of the room at a given point is calculated by considering all events preceding and including a given event in the graph. Where events describe the same state, a merge conflict algorithm is applied.

{
  id: 'roomId-1234',
  type_name: 'Room',
  numeric_id: '654321',
  time_deactivated: 1606768985127,
  time_registered: 1606768985127,
  'room.group_type': 'group',
  'room.name': 'Friday Night',
  'room.topic': 'VR Games',
  'room.url_avatar': 'https://iconicengine.com/avatar.png',
  'room.user_owner_id': 'userId-1234',
  'room.user_creator_id': 'userId-1234',
  'membership.user_invited_id_list': [ 'userAId-1234', 'userBId-1234' ],
  'membership.user_banned_id_list': [ 'userCId-1234', 'userDId-1234' ],
  'membership.user_joined_id_list': [ 'userEId-1234', 'userFId-1234' ],
  'userstate.$user.muted': true,
  'userstate.$user.status': 'busy',
}

State is modelled as a lookup table of key/value pairs per room, with each key being a tuple of state_key and event type. Each state event updates the value of a given key.

The dot-syntax used for property names imitates the dot-syntax used by matrix. Values parsed from the dot-syntax could be used for locating namespace-specific tables or services. Its un-decided if we will use the dot-sytax.

const getUserRoomsTypeGroup = async user => r.table('Room')
  .getAll(...user.room_joined_id_list.filter(id => /^friend/.test(id)))
  .run();

const getUserFriendIds = async user => {
  const membershipIdLists = await r.table('Room')
    .getAll(...user.room_joined_id_list)
    .pluck('membership.user_joined_id_list')
    .run();

  return membershipIdLists.flat().filter(id => id !== user.id);
};

Database, Matrix-like Devices

The Device data found in friendgraph suit the description of Davices found in the Matrix spec,

Devices are used primarily to manage the keys used for end-to-end encryption (each device gets its own copy of the decryption keys), but they also help users manage their access - for instance, by revoking access to particular devices.

When a user first uses a client, it registers itself as a new device. The longevity of devices might depend on the type of client. A web client will probably drop all of its state on logout, and create a new device every time you log in, to ensure that cryptography keys are not leaked to a new user. In a mobile client, it might be acceptable to reuse the device if a login session expires, provided the user is the same.

Devices are identified by a device_id, which is unique within the scope of a given user.

A user may assign a human-readable display name to a device, to help them manage their devices.

Device data do not need to be changed. Matrix-style devices manage keys for decrypting encrypted chats, a use-case we won't need to suppoert.

Third-party Integrations, Vivox and Photon

query {
  user {
    userVivoxTokenLogin
  }
}

Vivox services are used to handle direct messaging and VOIP calls. The social-profile service generates signed vivox-tokens and the xr-application uses the vivox-tokens with room and user data, to construct Vivox requests.

Photon is the multiplayer syncing piece. Photon requests are also constructed from room and user data, but do not require tokens or any other special thing from social-profile.

Questions

Q. Are direct-messages to friends applied in a special way.

No. Friends are rooms. Room documents are designated for "friends" using group_type: "friend".

Q. What about GDPR? Do we have a privacy policy?

Details are being worked out. Google-searching this topic up this law.stackexchange.com thread and these Livechat documents. Livechat connects customers to companies via chat widget.

Q. If I am in MagentaTV and you are in MagentaXR, what happens if they're separate keys/issuers? Can they still talk to each other or are they now locked to their own application only?

Vivox does not support messaging between different "apps".

Q. If a user has multiple apps, which one gets notified?

We could use an algorithm to select the app. For example, notifications might go to a preferred app, a recently used app, the app used most often over the past week.

Q. How will federated sign-on work? Will signin from one DT App provide access to other DT Apps?

Each AppUser will be "childed" to a UserSocial document that "parents" many AppUsers.

How will social-profile identify related "sibling" AppUsers? Linking AppUsers by email is in-sufficient because different emails could be used for sibling AppUser and because some external SSO logins do not return a user's email on signin.

Note: for GDPR reasons, we must ask the User for permission to link separate profiles. Sample response data from the OpenID spec implemented by the Telekom service do include an email.

Q. How do Matrix clients get room updates? Do they use sockets or Server Sent Events?

Matrix clients use long-polling. Social-profile uses Server Sent Events.

Q. Am I be able to discreetly "un-friend" people?

Yes. Un-friending using the old SocialFriend system resulted in ghosting the other party, because un-friending deleted a single, shared SocialFriend document for both "friends". The social-profile room system doesn't have this short-coming.

bumblehead.com