diff --git a/docs/draft/6-payloads.md b/docs/draft/6-payloads.md index 01a3c0f4..a2b524ce 100644 --- a/docs/draft/6-payloads.md +++ b/docs/draft/6-payloads.md @@ -39,6 +39,7 @@ as various clients created using different technologies. - [Message types](#message-types) - [Clock vs Timestamp and message ordering](#clock-vs-timestamp-and-message-ordering) - [Chats](#chats) + - [Chat Identity](#chat-identity) - [Contact Update](#contact-update) - [Payload](#payload-2) - [Contact update](#contact-update-1) @@ -50,6 +51,8 @@ as various clients created using different technologies. - [PairInstallation](#pairinstallation) - [Payload](#payload-5) - [MembershipUpdateMessage and MembershipUpdateEvent](#membershipupdatemessage-and-membershipupdateevent) + - [Enums](#enums) + - [ImageType](#imagetype) - [Upgradability](#upgradability) - [Security Considerations](#security-considerations) - [Changelog](#changelog) @@ -100,7 +103,7 @@ message ChatMessage { string text = 3; // Id of the message that we are replying to string response_to = 4; - // Ens name of the sender + // DEPRECATED. Ens name of the sender string ens_name = 5; // Chat id, this field is symmetric for public-chats and private group chats, // but asymmetric in case of one-to-ones, as the sender will use the chat-id @@ -141,7 +144,7 @@ message ChatMessage { | 2 | timestamp | `uint64` | The sender timestamp at message creation | | 3 | text | `string` | The content of the message | | 4 | response_to | `string` | The ID of the message replied to | -| 5 | ens_name | `string` | The ENS name of the user sending the message | +| 5 | ens_name | `string` | DEPRECATED - [See Chat Identity](#chat-identity). The ENS name of the user sending the message | | 6 | chat_id | `string` | The local ID of the chat the message is sent to | | 7 | message_type | `MessageType` | The type of message, different for one-to-one, public or group chats | | 8 | content_type | `ContentType` | The type of the content of the message | @@ -186,7 +189,7 @@ message StickerMessage { ##### Image content type A `ChatMessage` with `IMAGE` `Content/Type` MUST also specify the `payload` of the image -and the `type`. +and the `type`. Also see [ImageType](#imagetype) Clients MUST sanitize the payload before accessing its content, in particular: - Clients MUST choose a secure decoder @@ -199,13 +202,6 @@ Clients MUST sanitize the payload before accessing its content, in particular: message ImageMessage { bytes payload = 1; ImageType type = 2; - enum ImageType { - UNKNOWN_IMAGE_TYPE = 0; - PNG = 1; - JPEG = 2; - WEBP = 3; - GIF = 4; - } } ``` @@ -229,6 +225,8 @@ message AudioMessage { UNKNOWN_AUDIO_TYPE = 0; AAC = 1; AMR = 2; + } +} ``` #### Message types @@ -261,7 +259,7 @@ message is supposed to be displayed last in a chat. This is where the basic algo as it's only meant to order causally related events. The status client therefore makes a "bid", speculating that it will beat the current chat-timestamp, s.t. the status client's -Lamport timestamp format is: `clock = `max({timestamp}, chat_clock + 1)` +Lamport timestamp format is: `clock = max({timestamp}, chat_clock + 1)` This will satisfy the Lamport requirement, namely: a -> b then T(a) < T(b) @@ -288,6 +286,111 @@ All incoming messages can be matched against a chat. The below table describes h |user-message|let `P` be a public key of message's signature; `hex-encode(P)` is a chat ID; discard `chat-id` from message|Incoming|if there is no matched chat, it might be the first message from public key `P`; the node MAY discard the message or MAY create a new chat; Status official clients create a new chat| |PRIVATE_GROUP|use `chatId` from the message|Incoming/Outgoing|find an existing chat by `chatId`; if none is found, the user is not a member of that chat or the user hasn't joined that chat, the message MUST be discarded | +### Chat Identity + +The `ChatIdentity` allows a user to OPTIONALLY broadcast an identity to be associated with their messages. + +The main components of the `ChatIdentity` are: + +| Field | Name | Type | Description | +| ----- | --------------- | ---------------------------- | --- | +| 1 | `clock` | `uint64` | A lamport timestamp of the message | +| 2 | `ens_name` | `string` | A valid registered ENS name for the user. Deprecates the `ens_name` field in `ChatMessage` | +| 3 | `images` | `map` | A string indexed mapping of images associated with an identity | + +#### Profile Image + +The `ProfileImage` data struct describes the mechanisms by which the application parses and presents the user's chosen visual representation. + +The main components of the `ProfileImage` are: + +| Field | Name | Type | Description | +| ----- | ------------- | ------------------ | --- | +| 1 | `payload` | `bytes` | A context based payload for the profile image data. Context is determined by the `source_type` | +| 2 | `source_type` | `SourceType` | Enum, signals the image payload source | +| 3 | `image_type` | `enums.ImageType` | Enum, signals the image type and method of parsing the payload. See [ImageType](#imagetype) | + +#### Payload + +```protobuf +syntax = "proto3"; + +package protobuf; + +// ChatIdentity represents the user defined identity associated with their public chat key +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 images = 3; +} + +// ProfileImage represents data associated with a user's profile image +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; + + // 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; + } +} + +``` + +#### Implementation Recommendations + +##### ChatMessage ens_name + +The application MUST handle `ChatMessage.ens_name` as well as `ChatMessageIdentity.ens_name` to maintain backwards compatibility. The `ChatMessage.ens_name` field has been marked as DEPRECATED in this version of the specification to highlight that this field should be removed as part of any upgrade to a major version. + +##### Identity Update + +An `IdentityUpdate` is a concept representing the event of the application sending a `ChatMessageIdentity` in response to either: + +- sending the user's first message in a chat topic +- sending a message to a chat topic after a change to the user identity +- sending a message to a chat topic after the expiry of the chat type `ChatMessageIdentity TTL` period + +##### One-To-One Chat + +This specification RECOMMENDS that the application only sends one `IdentityUpdate`, with no `ChatMessageIdentity TTL`. + +##### Private Group Chat + +This specification RECOMMENDS that the application only sends one `IdentityUpdate`, with no `ChatMessageIdentity TTL`, and once per new user joining the chat group. + +##### Public Chat + +This specification RECOMMENDS that the application only sends one `IdentityUpdate`, with a `ChatMessageIdentity TTL` of 24 hours. + +##### Security + +To preserve the privacy of the user, the `ProfileImage.payload` or ENS `get-text-data.avatar` field SHOULD NOT be or be parsed as a URL, an IPFS address or an IPNS address. Malicious actors could set their payload to an image URL and force all users that parse their `ProfileImage.payload` and log the IP address of users of selected topics. + +An additional step to maintaining user privacy is to adhere to the `IdentityUpdate` event triggering, `IdentityUpdate` MUST only be sent after a user sends a `ChatMessage` to a topic. This will ensure that users who wish to only read topic messages do not "leak" their identity data into topics they have not actively participated in. + ### Contact Update `ContactUpdate` is a message exchange to notify peers that either the @@ -436,6 +539,28 @@ message PairInstallation { `MembershipUpdateEvent` is a message used to propagate information about group membership changes in a group chat. The details are in the [Group chats specs](./7-group-chat.md). +## Enums + +### ImageType + +```protobuf +enum ImageType { + UNKNOWN_IMAGE_TYPE = 0; + + // Raster image files is payload data that can be read as a raster image + PNG = 1; + JPEG = 2; + WEBP = 3; + GIF = 4; + + // Vector image files is payload data that can be interpreted as a vector image + SVG = 101; + + // AVATAR is payload data that can be parsed as avatar compilation instructions + AVATAR = 201; +} +``` + ## Upgradability There are two ways to upgrade the protocol without breaking compatibility: @@ -449,6 +574,13 @@ There are two ways to upgrade the protocol without breaking compatibility: ## Changelog +### Version 0.7 + +Released //TODO + +- Added `ChatMessageIdentity` payload +- Marks `ChatMessage.ens_name` as DEPRECATED + ### Version 0.5 Released [August 25, 2020](https://github.com/status-im/specs/commit/968fafff23cdfc67589b34dd64015de29aaf41f0) diff --git a/docs/stable/6-payloads.md b/docs/stable/6-payloads.md index 1039abb7..4ae1891b 100644 --- a/docs/stable/6-payloads.md +++ b/docs/stable/6-payloads.md @@ -6,11 +6,11 @@ title: 6/PAYLOADS # 6/PAYLOADS -> Version: 0.3 +> Version: 0.6 > > Status: Stable > -> Authors: Adam Babik , Andrea Maria Piana , Oskar Thorén (alphabetical order) +> Authors: Adam Babik , Andrea Maria Piana , Oskar Thorén , Samuel Hawksby-Robinson (alphabetical order) ## Abstract @@ -34,12 +34,15 @@ as various clients created using different technologies. - [Payload](#payload-1) - [Content types](#content-types) - [Sticker content type](#sticker-content-type) + - [Image content type](#image-content-type) + - [Audio content type](#audio-content-type) - [Message types](#message-types) - [Clock vs Timestamp and message ordering](#clock-vs-timestamp-and-message-ordering) - [Chats](#chats) - [Contact Update](#contact-update) - [Payload](#payload-2) - [Contact update](#contact-update-1) + - [EmojiReaction](#emojireaction) - [SyncInstallationContact](#syncinstallationcontact) - [Payload](#payload-3) - [SyncInstallationPublicChat](#syncinstallationpublicchat) @@ -50,7 +53,7 @@ as various clients created using different technologies. - [Upgradability](#upgradability) - [Security Considerations](#security-considerations) - [Changelog](#changelog) - - [Version 0.3](#version-03) + - [Copyright](#copyright) ## Introduction @@ -62,36 +65,9 @@ The node wraps all payloads in a [protobuf record](https://developers.google.com record: ```protobuf -message ApplicationMetadataMessage { - bytes signature = 1; - bytes payload = 2; - - Type type = 3; - - enum Type { - UNKNOWN = 0; - CHAT_MESSAGE = 1; - CONTACT_UPDATE = 2; - MEMBERSHIP_UPDATE_MESSAGE = 3; - PAIR_INSTALLATION = 4; - SYNC_INSTALLATION = 5; - REQUEST_ADDRESS_FOR_TRANSACTION = 6; - ACCEPT_REQUEST_ADDRESS_FOR_TRANSACTION = 7; - DECLINE_REQUEST_ADDRESS_FOR_TRANSACTION = 8; - REQUEST_TRANSACTION = 9; - SEND_TRANSACTION = 10; - DECLINE_REQUEST_TRANSACTION = 11; - SYNC_INSTALLATION_CONTACT = 12; - SYNC_INSTALLATION_ACCOUNT = 13; - SYNC_INSTALLATION_PUBLIC_CHAT = 14; - CONTACT_CODE_ADVERTISEMENT = 15; - PUSH_NOTIFICATION_REGISTRATION = 16; - PUSH_NOTIFICATION_REGISTRATION_RESPONSE = 17; - PUSH_NOTIFICATION_QUERY = 18; - PUSH_NOTIFICATION_QUERY_RESPONSE = 19; - PUSH_NOTIFICATION_REQUEST = 20; - PUSH_NOTIFICATION_RESPONSE = 21; - } +message StatusProtocolMessage { + bytes signature = 4001; + bytes payload = 4002; } ``` @@ -99,8 +75,6 @@ message ApplicationMetadataMessage { The node needs the signature to validate authorship of the message, so that the message can be relayed to third parties. If a signature is not present, but an author is provided by a layer below, the message is not to be relayed to third parties, and it is considered plausibly deniable. -`payload` is the protobuf encoded content of the message, with the corresponding `type` set. - ## Encoding The node encodes the payload using [Protobuf](https://developers.google.com/protocol-buffers) @@ -131,7 +105,6 @@ message ChatMessage { // Chat id, this field is symmetric for public-chats and private group chats, // but asymmetric in case of one-to-ones, as the sender will use the chat-id // of the received, while the receiver will use the chat-id of the sender. - // Probably should be the concatenation of sender-pk & receiver-pk in alphabetical order string chat_id = 6; // The type of message (public/one-to-one/private-group-chat) @@ -141,15 +114,10 @@ message ChatMessage { oneof payload { StickerMessage sticker = 9; + ImageMessage image = 10; + AudioMessage audio = 11; } - enum MessageType { - UNKNOWN_MESSAGE_TYPE = 0; - ONE_TO_ONE = 1; - PUBLIC_GROUP = 2; - PRIVATE_GROUP = 3; - // Only local - SYSTEM_MESSAGE_PRIVATE_GROUP = 4;} enum ContentType { UNKNOWN_CONTENT_TYPE = 0; TEXT_PLAIN = 1; @@ -159,6 +127,8 @@ message ChatMessage { TRANSACTION_COMMAND = 5; // Only local SYSTEM_MESSAGE_CONTENT_PRIVATE_GROUP = 6; + IMAGE = 7; + AUDIO = 8; } } ``` @@ -175,7 +145,7 @@ message ChatMessage { | 6 | chat_id | `string` | The local ID of the chat the message is sent to | | 7 | message_type | `MessageType` | The type of message, different for one-to-one, public or group chats | | 8 | content_type | `ContentType` | The type of the content of the message | -| 9 | payload | `Sticker|nil` | The payload of the message based on the content type | +| 9 | payload | `Sticker` I `Image` I `Audio` I `nil` | The payload of the message based on the content type | #### Content types @@ -189,6 +159,8 @@ There are other content types that MAY be implemented by the client: * `STATUS` * `EMOJI` * `TRANSACTION_COMMAND` +* `IMAGE` +* `AUDIO` ##### Mentions @@ -211,9 +183,59 @@ message StickerMessage { } ``` +##### Image content type + +A `ChatMessage` with `IMAGE` `Content/Type` MUST also specify the `payload` of the image +and the `type`. + +Clients MUST sanitize the payload before accessing its content, in particular: +- Clients MUST choose a secure decoder +- Clients SHOULD strip metadata if present without parsing/decoding it +- Clients SHOULD NOT add metadata/exif when sending an image file for privacy and security reasons +- Clients MUST make sure that the transport layer constraints the size of the payload to limit they are able to handle securely + + +```protobuf +message ImageMessage { + bytes payload = 1; + ImageType type = 2; + enum ImageType { + UNKNOWN_IMAGE_TYPE = 0; + PNG = 1; + JPEG = 2; + WEBP = 3; + GIF = 4; + } +} +``` + +##### Audio content type + +A `ChatMessage` with `AUDIO` `Content/Type` MUST also specify the `payload` of the audio, +the `type` and the duration in milliseconds (`duration_ms`). + +Clients MUST sanitize the payload before accessing its content, in particular: +- Clients MUST choose a secure decoder +- Clients SHOULD strip metadata if present without parsing/decoding it +- Clients SHOULD NOT add metadata/exif when sending an audio file for privacy and security reasons +- Clients MUST make sure that the transport layer constraints the size of the payload to limit they are able to handle securely + +```protobuf +message AudioMessage { + bytes payload = 1; + AudioType type = 2; + uint64 duration_ms = 3; + enum AudioType { + UNKNOWN_AUDIO_TYPE = 0; + AAC = 1; + AMR = 2; + } +} +``` + #### Message types -A node requires message types to decide how to encrypt a particular message and what metadata needs to be attached when passing a message to the transport layer. For more on this, see [3/WHISPER-USAGE](3-whisper-usage.md) and [10/WAKU-USAGE](10-waku-usage.md). +A node requires message types to decide how to encrypt a particular message and what metadata needs to be attached when passing a message to the transport layer. For more on this, see [3/WHISPER-USAGE](./3-whisper-usage.md) and [10/WAKU-USAGE](./10-waku-usage.md). @@ -223,6 +245,17 @@ The following messages types MUST be supported: * `PUBLIC_GROUP` is a private message * `PRIVATE_GROUP` is a message to the private group. +```protobuf + enum MessageType { + UNKNOWN_MESSAGE_TYPE = 0; + ONE_TO_ONE = 1; + PUBLIC_GROUP = 2; + PRIVATE_GROUP = 3; + // Only local + SYSTEM_MESSAGE_PRIVATE_GROUP = 4; +} +``` + #### Clock vs Timestamp and message ordering If a user sends a new message before the messages sent while the user was offline are received, the new @@ -288,6 +321,52 @@ A client SHOULD send a `ContactUpdate` to all the contacts each time: A client SHOULD also periodically send a `ContactUpdate` to all the contacts, the interval is up to the client, the Status official client sends these updates every 48 hours. +### EmojiReaction + +`EmojiReaction`s represents a user's "reaction" to a specific chat message. For more information about the concept of +emoji reactions see [Facebook Reactions](https://en.wikipedia.org/wiki/Facebook_like_button#Use_on_Facebook). + +This specification RECOMMENDS that the UI/UX implementation of sending `EmojiReactions` requires only a single click +operation, as users have an expectation that emoji reactions are effortless and simple to perform. + +```protobuf +message EmojiReaction { + // clock Lamport timestamp of the chat message + uint64 clock = 1; + + // chat_id the ID of the chat the message belongs to, for query efficiency the chat_id is stored in the db even though the + // target message also stores the chat_id + string chat_id = 2; + + // message_id the ID of the target message that the user wishes to react to + string message_id = 3; + + // message_type is (somewhat confusingly) the ID of the type of chat the message belongs to + MessageType message_type = 4; + + // type the ID of the emoji the user wishes to react with + Type type = 5; + + enum Type { + UNKNOWN_EMOJI_REACTION_TYPE = 0; + LOVE = 1; + THUMBS_UP = 2; + THUMBS_DOWN = 3; + LAUGH = 4; + SAD = 5; + ANGRY = 6; + } + + // whether this is a retraction of a previously sent emoji + bool retracted = 6; +} +``` + +Clients MUST specify `clock`, `chat_id`, `message_id`, `type` and `message_type`. + +This specification RECOMMENDS that the UI/UX implementation of retracting an `EmojiReaction`s requires only a single +click operation, as users have an expectation that emoji reaction removals are effortless and simple to perform. + ### SyncInstallationContact The node uses `SyncInstallationContact` messages to synchronize in a best-effort the contacts to other devices. @@ -372,8 +451,31 @@ There are two ways to upgrade the protocol without breaking compatibility: ## Changelog +### Version 0.6 + +Release //TODO + +- Updated draft spec to stable status + +### Version 0.5 + +Released [August 25, 2020](https://github.com/status-im/specs/commit/968fafff23cdfc67589b34dd64015de29aaf41f0) + +- Added support for emoji reactions + +### Version 0.4 + +Released [July 16, 2020](https://github.com/status-im/specs/commit/ad45cd5fed3c0f79dfa472253a404f670dd47396) + +- Added support for images +- Added support for audio + ### Version 0.3 Released [May 22, 2020](https://github.com/status-im/specs/commit/664dd1c9df6ad409e4c007fefc8c8945b8d324e8) - Added language to include Waku in all relevant places + +## Copyright + +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). diff --git a/wordlist.txt b/wordlist.txt index e2bfb323..9cbd998f 100644 --- a/wordlist.txt +++ b/wordlist.txt @@ -30,6 +30,7 @@ Changelog chatId chatID chatid +ChatIdentity ChatMessage contactCode contactcode @@ -85,6 +86,7 @@ EncodeToString enode enr enum +Enums exif ERC ErrorType @@ -120,13 +122,16 @@ html http HTTPS identicon +IdentityImage IK im ImageMessage +ImageType ImportECDSA ImportECDSAPublic infura IPFS +IPNS IPs Iubenda Jacek @@ -143,6 +148,7 @@ keypair keypairs Kozieiev Lamport +lamport legislations len libp @@ -187,6 +193,7 @@ pin Pinzaru plaintext Pluggable +PNG Pombeiro PoW pre @@ -196,6 +203,8 @@ prepend prepended prepending privkey +ProfileImage +proto protobuf ProtocolMessage PSS @@ -237,14 +246,17 @@ SIP SIPs SNT Sourcecode +SourceType SPK StickerMessage stickerpack strconv +struct suboptimal subprotocol subprotocols SuggestGasPrice +SVG SyncInstallationContact SyncInstallationPublicChat TCP @@ -281,6 +293,7 @@ Waku Waku's waku wakuext +WEBP webview Webview wei