+ 56/STATUS-COMMUNITIES + # +
+ + ++ Status Communities that run over Waku v2 + # +
+ + + + + + + + + + + +-
+
- Status: draft +
- Editor: Aaryamann Challani aaryamann@status.im + +
- Contributors: + + + Andrea Piana andreap@status.im + + + +
+ Abstract + # +
+This document describes the design of Status Communities over Waku v2, allowing for multiple users to communicate in a group chat. +This is a key feature for the Status messaging app.
++ Background and Motivation + # +
+The purpose of Status communities, as specified in this document, is allowing for large group chats. +Communities can have further substructure, e.g. specific channels.
+Smaller group chats, on the other hand, are out of scope for this document and can be built over 55/STATUS-1TO1-CHAT. +We refer to these smaller group chats simply as “group chats”, to differentiate them from Communities.
+For group chats based on 55/STATUS-1TO1-CHAT, the key exchange mechanism MUST be X3DH, as described in 53/WAKU2-X3DH.
+However, this method does not scale as the number of participants increases, for the following reasons -
+-
+
- The number of messages sent over the network increases with the number of participants. +
- Handling the X3DH key exchange for each participant is computationally expensive. +
Having multicast channels reduces the overhead of a group chat based on 1:1 chat. +Additionally, if all the participants of the group chat have a shared key, then the number of messages sent over the network is reduced to one per message.
++ Terminology + # +
+-
+
- Community: A group of peers that can communicate with each other. +
- Member: A peer that is part of a community. +
- Admin: A member that has administrative privileges. Used interchangeably with “owner”. +
- Channel: A designated subtopic for a community. Used interchangeably with “chat”. +
+ Design Requirements + # +
+Due to the nature of communities, the following requirements are necessary for the design of communities -
+-
+
- The creator of the Community is the owner of the Community. +
- The Community owner is trusted. +
- The Community owner can add or remove members from the Community. +This extends to banning and kicking members. +
- The Community owner can add, edit and remove channels. +
- Community members can send/receive messages to the channels which they have access to. +
- Communities may be encrypted (private) or unencrypted (public). +
- A Community is uniquely identified by a public key. +
- The public key of the Community is shared out of band. +
- The metadata of the Community can be found by listening on a content topic derived from the public key of the Community. +
- Community members run their own Waku nodes, with the configuration described in #Waku-Protocols. +Light nodes solely implementing 19/WAKU2-LIGHTPUSH may not be able to run their own Waku node with the configuration described. +
+ Design + # +
++ Cryptographic Primitives + # +
+The following cryptographic primitives are used in the design -
+-
+
- X3DH +
- Single Ratchet
+
-
+
- The single ratchet is used to encrypt the messages sent to the Community. +
- The single ratchet is re-keyed when a member is added/removed from the Community. +
+
+ Wire format + # +
+ + +syntax = "proto3";
+
+message IdentityImage {
+ // payload is a context based payload for the profile image data,
+ // context is determined by the `source_type`
+ bytes payload = 1;
+ // source_type signals the image payload source
+ SourceType source_type = 2;
+ // image_type signals the image type and method of parsing the payload
+ ImageType image_type = 3;
+ // encryption_keys is a list of encrypted keys that can be used to decrypt an encrypted payload
+ repeated bytes encryption_keys = 4;
+ // encrypted signals the encryption state of the payload, default is false.
+ bool encrypted = 5;
+ // SourceType are the predefined types of image source allowed
+ enum SourceType {
+ UNKNOWN_SOURCE_TYPE = 0;
+
+ // RAW_PAYLOAD image byte data
+ RAW_PAYLOAD = 1;
+
+ // ENS_AVATAR uses the ENS record's resolver get-text-data.avatar data
+ // The `payload` field will be ignored if ENS_AVATAR is selected
+ // The application will read and parse the ENS avatar data as image payload data, URLs will be ignored
+ // The parent `ChatMessageIdentity` must have a valid `ens_name` set
+ ENS_AVATAR = 2;
+ }
+}
+
+// SocialLinks represents social link associated with given chat identity (personal/community)
+message SocialLink {
+ // Type of the social link
+ string text = 1;
+ // URL of the social link
+ string url = 2;
+}
+// ChatIdentity represents identity of a community/chat
+message ChatIdentity {
+ // Lamport timestamp of the message
+ uint64 clock = 1;
+ // ens_name is the valid ENS name associated with the chat key
+ string ens_name = 2;
+ // images is a string indexed mapping of images associated with an identity
+ map<string, IdentityImage> images = 3;
+ // display name is the user set identity
+ string display_name = 4;
+ // description is the user set description
+ string description = 5;
+ string color = 6;
+ string emoji = 7;
+ repeated SocialLink social_links = 8;
+ // first known message timestamp in seconds (valid only for community chats for now)
+ // 0 - unknown
+ // 1 - no messages
+ uint32 first_message_timestamp = 9;
+}
+
+message Grant {
+ // Community ID (The public key of the community)
+ bytes community_id = 1;
+ // The member ID (The public key of the member)
+ bytes member_id = 2;
+ // The chat for which the grant is given
+ string chat_id = 3;
+ // The Lamport timestamp of the grant
+ uint64 clock = 4;
+}
+
+message CommunityMember {
+ // The roles a community member MAY have
+ enum Roles {
+ UNKNOWN_ROLE = 0;
+ ROLE_ALL = 1;
+ ROLE_MANAGE_USERS = 2;
+ ROLE_MODERATE_CONTENT = 3;
+ }
+ repeated Roles roles = 1;
+}
+
+message CommunityPermissions {
+ // The type of access a community MAY have
+ enum Access {
+ UNKNOWN_ACCESS = 0;
+ NO_MEMBERSHIP = 1;
+ INVITATION_ONLY = 2;
+ ON_REQUEST = 3;
+ }
+
+ // If the community should be available only to ens users
+ bool ens_only = 1;
+ // If the community is private
+ bool private = 2;
+ Access access = 3;
+}
+
+message CommunityAdminSettings {
+ // If the Community admin may pin messages
+ bool pin_message_all_members_enabled = 1;
+}
+
+message CommunityChat {
+ // A map of members in the community to their roles in a chat
+ map<string,CommunityMember> members = 1;
+ // The permissions of the chat
+ CommunityPermissions permissions = 2;
+ // The metadata of the chat
+ ChatIdentity identity = 3;
+ // The category of the chat
+ string category_id = 4;
+ // The position of chat in the display
+ int32 position = 5;
+}
+
+message CommunityCategory {
+ // The category id
+ string category_id = 1;
+ // The name of the category
+ string name = 2;
+ // The position of the category in the display
+ int32 position = 3;
+}
+
+message CommunityInvitation {
+ // Encrypted/unencrypted community description
+ bytes community_description = 1;
+ // The grant offered by the community
+ bytes grant = 2;
+ // The chat id requested to join
+ string chat_id = 3;
+ // The public key of the community
+ bytes public_key = 4;
+}
+
+message CommunityRequestToJoin {
+ // The Lamport timestamp of the request
+ uint64 clock = 1;
+ // The ENS name of the requester
+ string ens_name = 2;
+ // The chat id requested to join
+ string chat_id = 3;
+ // The public key of the community
+ bytes community_id = 4;
+ // The display name of the requester
+ string display_name = 5;
+}
+
+message CommunityCancelRequestToJoin {
+ // The Lamport timestamp of the request
+ uint64 clock = 1;
+ // The ENS name of the requester
+ string ens_name = 2;
+ // The chat id requested to join
+ string chat_id = 3;
+ // The public key of the community
+ bytes community_id = 4;
+ // The display name of the requester
+ string display_name = 5;
+ // Magnet uri for community history protocol
+ string magnet_uri = 6;
+}
+
+message CommunityRequestToJoinResponse {
+ // The Lamport timestamp of the request
+ uint64 clock = 1;
+ // The community description
+ CommunityDescription community = 2;
+ // If the request was accepted
+ bool accepted = 3;
+ // The grant offered by the community
+ bytes grant = 4;
+ // The community public key
+ bytes community_id = 5;
+}
+
+message CommunityRequestToLeave {
+ // The Lamport timestamp of the request
+ uint64 clock = 1;
+ // The community public key
+ bytes community_id = 2;
+}
+
+message CommunityDescription {
+ // The Lamport timestamp of the message
+ uint64 clock = 1;
+ // A mapping of members in the community to their roles
+ map<string,CommunityMember> members = 2;
+ // The permissions of the Community
+ CommunityPermissions permissions = 3;
+ // The metadata of the Community
+ ChatIdentity identity = 5;
+ // A mapping of chats to their details
+ map<string,CommunityChat> chats = 6;
+ // A list of banned members
+ repeated string ban_list = 7;
+ // A mapping of categories to their details
+ map<string,CommunityCategory> categories = 8;
+ // The admin settings of the Community
+ CommunityAdminSettings admin_settings = 10;
+ // If the community is encrypted
+ bool encrypted = 13;
+ // The list of tags
+ repeated string tags = 14;
+}
+
Note: The usage of the clock is described in the Clock section.
++ Content topic usage + # +
+“Content topic” refers to the field in 14/WAKU2-MESSAGE, further elaborated in 10/WAKU2.
++ Advertising a Community + # +
+The content topic that the community is advertised on MUST be derived from the public key of the community. +The content topic MUST be the first four bytes of the keccak-256 hash of the compressed (33 bytes) public key of the community encoded into a hex string.
+hash = hex(keccak256(encodeToHex(compressedPublicKey)))
+
+topicLen = 4
+if len(hash) < topicLen {
+ topicLen = len(hash)
+}
+
+var topic [4]byte
+for i = 0; i < topicLen; i++ {
+ topic[i] = hash[i]
+}
+
+contentTopic = "/waku/1/0x" + topic + "/rfc26"
+
+ Community channels/chats + # +
+The unique identifier for a community channel/chat is the chat id.
+ +The content topic that Community channels/chats use MUST be the hex-encoded keccak-256 hash of the public key of the community concatenated with the chat id.
+hash = hex(keccak256(encodeToHex(compressedPublicKey + chatId)))
+
+topicLen = 4
+if len(hash) < topicLen {
+ topicLen = len(hash)
+}
+var topic [4]byte
+for i = 0; i < topicLen; i++ {
+ topic[i] = hash[i]
+}
+
+contentTopic = "/waku/1/0x" + topic + "/rfc26"
+
+ Community event messages + # +
+Requests to leave, join, kick and ban, as well as key exchange messages, MUST be sent to the content topic derived from the public key of the community. +The content topic MUST be the hex-encoded keccak-256 hash of the public key of the community.
+hash = hex(keccak256(encodeToHex(publicKey)))
+
+topicLen = 4
+if len(hash) < topicLen {
+ topicLen = len(hash)
+}
+var topic [4]byte
+for i = 0; i < topicLen; i++ {
+ topic[i] = hash[i]
+}
+
+contentTopic = "/waku/1/0x" + topic + "/rfc26"
+
+ Community Management + # +
+The flows for Community management are as described below.
++ Community Creation Flow + # +
+-
+
- The Community owner generates a public/private key pair. +
- The Community owner configures the Community metadata, according to the wire format “CommunityDescription”. +
- The Community owner publishes the Community metadata on a content topic derived from the public key of the Community. +the Community metadata SHOULD be encrypted with the public key of the Community. +The Community metadata MAY be sent during fixed intervals, to ensure that the Community metadata is available to members. +The Community metadata SHOULD be sent every time the Community metadata is updated. +
- The Community owner MAY advertise the Community out of band, by sharing the public key of the Community on other mediums of communication. +
+ Community Join Flow (peer requests to join a Community) + # +
+-
+
- A peer and the Community owner establish a 1:1 chat as described in 55/STATUS-1TO1-CHAT. +
- The peer requests to join a Community by sending a “CommunityRequestToJoin” message to the Community. +At this point, the peer MAY send a “CommunityCancelRequestToJoin” message to cancel the request. +
- The Community owner MAY accept or reject the request. +
- If the request is accepted, the Community owner sends a “CommunityRequestToJoinResponse” message to the peer. +
- The Community owner then adds the member to the Community metadata, and publishes the updated Community metadata. +
+ Community Join Flow (peer is invited to join a Community) + # +
+-
+
- The Community owner and peer establish a 1:1 chat as described in 55/STATUS-1TO1-CHAT. +
- The peer is invited to join a Community by the Community owner, by sending a “CommunityInvitation” message. +
- The peer decrypts the “CommunityInvitation” message, and verifies the signature. +
- The peer requests to join a Community by sending a “CommunityRequestToJoin” message to the Community. +
- The Community owner MAY accept or reject the request. +
- If the request is accepted, the Community owner sends a “CommunityRequestToJoinResponse” message to the peer. +
- The Community owner then adds the member to the Community metadata, and publishes the updated Community metadata. +
+ Community Leave Flow + # +
+-
+
- A member requests to leave a Community by sending a “CommunityRequestToLeave” message to the Community. +
- The Community owner MAY accept or reject the request. +
- If the request is accepted, the Community owner removes the member from the Community metadata, and publishes the updated Community metadata. +
+ Community Ban Flow + # +
+-
+
- The Community owner adds a member to the ban list, revokes their grants, and publishes the updated Community metadata. +
- If the Community is Private, Re-keying is performed between the members of the Community, to ensure that the banned member is unable to decrypt any messages. +
+ Waku protocols + # +
+The following Waku protocols SHOULD be used to implement Status Communities -
+-
+
- 11/WAKU2-RELAY - To send and receive messages +
- 53/WAKU2-X3DH - To encrypt and decrypt messages +
- 53/WAKU2-X3DH-SESSIONS - To handle session keys +
- 14/WAKU2-MESSAGE - To wrap community messages in a Waku message +
- 13/WAKU2-STORE - To store and retrieve messages for offline devices +
The following Waku protocols MAY be used to implement Status Communities -
+-
+
- 12/WAKU2-FILTER - Content filtering for resource restricted devices +
- 19/WAKU2-LIGHTPUSH - Allows Light clients to participate in the network +
+ Backups + # +
+The member MAY back up their local settings, by encrypting it with their public key, and sending it to a given content topic. +The member MAY then rely on this backup to restore their local settings, in case of a data loss. +This feature relies on 13/WAKU2-STORE for storing and retrieving messages.
++ Clock + # +
+The clock used in the wire format refers to the Lamport timestamp of the message. +The Lamport timestamp is a logical clock that is used to determine the order of events in a distributed system. +This allows ordering of messages in an asynchronous network where messages may be received out of order.
++ Security Considerations + # +
+-
+
-
+
The Community owner is a single point of failure. If the Community owner is compromised, the Community is compromised.
+
+ -
+
Follows the same security considerations as the 53/WAKU2-X3DH protocol.
+
+
+ Future work + # +
+-
+
-
+
To scale and optimize the Community management, the Community metadata should be stored on a decentralized storage system, and only the references to the Community metadata should be broadcasted. The following document describes this method in more detail - Optimizing the
+CommunityDescription
dissemination
+ -
+
Token gating for communities
+
+ -
+
Sharding the content topic used for #Community Event Messages, since members of the community don’t need to receive all the control messages.
+
+
+ Copyright + # +
+Copyright and related rights waived via CC0.
+