Skip to content
Snippets Groups Projects
Commit c7456b59 authored by Nico's avatar Nico Committed by akwizgran
Browse files

Move BSP clients specs from wiki to this repo

parent 3231cb17
No related branches found
No related tags found
1 merge request!7Move BSP clients specs from wiki to this repo
# Forum Sharing Client
The forum sharing client is a [BSP client](https://code.briarproject.org/briar/briar-spec/blob/master/protocols/BSP.md) that allows users to share forums with their contacts, who may accept or decline the invitations. It is used in conjunction with the [forum client](forum client).
### Identifier
The client's identifier is `org.briarproject.briar.forum.sharing`. The major version is 0.
### Groups
The client uses a separate BSP group for communicating with each contact. The [group descriptor](https://code.briarproject.org/briar/briar-spec/blob/master/protocols/BSP.md#23-groups) is a [BDF list](https://code.briarproject.org/briar/briar-spec/blob/master/BDF.md) containing the unique IDs of the contacts' identities, sorted in ascending order as byte strings.
### Roles
All communication happens between two contacts, who have symmetrical roles.
### Sessions
The messages exchanged between two contacts referring to a given forum constitute a session. The forum's unique ID is used as the session ID.
### Message types
The protocol uses five message types.
**0: INVITE** - Sent by either party to start or restart a session. The message body is a BDF list with four elements: `messageType` (int), `previousMessageId` (unique ID or null), `descriptor` (list), and `text` (string or null).
`previousMessageId` is the ID of the previous message in this session, if any. `descriptor` is the [descriptor of the forum](Forum-Client#group-identifiers) being shared. The forum ID must be calculated from the descriptor, as it is used by subsequent messages in the session. `text` is an optional message from the inviter to the invitee explaining the invitation.
The sender sets the forum's visibility to VISIBLE when sending an invite message.
**1: ACCEPT** - Sent in response to an invite. The message body is a BDF list with three elements: `messageType` (int), `forumId` (unique ID), and `previousMessageId` (unique ID).
`previousMessageId` is the ID of the previous message in this session.
The sender sets the forum's visibility to SHARED when sending an accept message. The recipient sets the forum's visibility to SHARED when receiving an accept message.
**2: DECLINE** - Sent in response to an invite. The message body is a BDF list with three elements: `messageType` (int), `forumId` (unique ID), and `previousMessageId` (unique ID).
`previousMessageId` is the ID of the previous message in this session.
The recipient sets the forum's visibility to INVISIBLE when receiving a decline message.
**3: LEAVE** - Sent by either party when unsubscribing from the forum, if the forum's visibility is VISIBLE or SHARED. The message body is a BDF list with three elements: `messageType` (int), `forumId` (unique ID), and `previousMessageId` (unique ID).
`previousMessageId` is the ID of the previous message in this session.
The sender sets the forum's visibility to INVISIBLE when sending a leave message. The recipient sets the forum's visibility to INVISIBLE when receiving a leave message.
**4: ABORT** - Sent by either party when recieving a message that is valid but unexpected in the current state. The message body is a BDF list with three elements: `messageType` (int), `forumId` (unique ID), and `previousMessageId` (unique ID).
`previousMessageId` is the ID of the previous message in this session.
The sender sets the forum's visibility to INVISIBLE when sending an abort message. The recipient sets the forum's visibility to INVISIBLE when receiving an abort message.
### State machine
![state-machine-4](/assets/clients/Forum-Sharing-Client/state-machine-4.png)
[state-machine-4.odg](/assets/clients/Forum-Sharing-Client/state-machine-4.odg)
Aborting from any state returns the session to the START state.
### Validity policy
* A message is valid if it is well-formed and its previous message (if any) is a valid message in the same session.
### Storage policy
* All messages are stored.
### Sharing policy
* All local messages are shared.
# Introduction Client
The introduction client is a [BSP client](https://code.briarproject.org/briar/briar-spec/blob/master/protocols/BSP.md) that allows a user to introduce two contacts to each other. Each contact may accept or decline the introduction. If both contacts accept, they become each other's contacts.
### Identifier
The client's identifier is `org.briarproject.briar.introduction`. The major version is 1.
### Groups
The client uses a separate BSP group for communicating with each contact. The [group descriptor](https://code.briarproject.org/briar/briar-spec/blob/master/protocols/BSP.md#23-groups) is a [BDF list](https://code.briarproject.org/briar/briar-spec/blob/master/BDF.md) containing the unique IDs of the contacts' identities, sorted in ascending order as byte strings.
### Roles
All communication happens between two contacts, who may take different roles with respect to each introduction. The contact who initiated the introduction takes the role of **introducer**. The other contact takes the role of **introducee**. Each introduction involves two introducees, who are referred to as **Alice** and **Bob**. Alice is the introducee whose unique ID is lower, comparing the IDs as byte strings.
### Sessions
The messages exchanged between a given introducer and (unordered) pair of introducees constitute a session. For the introducer, the session uses the BSP groups shared with the two introducees. For each introducee, the session uses the BSP group shared with the introducer. The introducees do not communicate directly.
The session ID is the hash of a BDF list containing the unique ID of the introducer, followed by the unique IDs of the introducees sorted in ascending order as byte strings. The hash label is `org.briarproject.briar.introduction/SESSION_ID`.
### Message types
#### Message types:
The protocol uses six message types.
**0: REQUEST** - Sent by the introducer to each introducee. The message body is a BDF list with four elements: `messageType` (int), `previousMessageId` (unique ID or null), `contact` (list), and `text` (string or null).
`previousMessageId` is the unique ID of the previous message sent by the sender in this session, if any, which is a dependency.
`contact` is the identity of the other introducee, which is a list with three elements: `formatVersion` (int), `nickname` (string), and `publicKey` (raw).
`text` is an optional message from the introducer to the introducee explaining the introduction.
**1: ACCEPT** - Sent by an introducee to the introducer if the sender accepts the introduction. Forwarded by the introducer to the other introducee. The message body is a BDF list with six elements: `messageType` (int), `sessionId` (unique ID), `previousMessageId` (unique ID), `ephemeralPublicKey` (raw), `timestamp` (int), and `transportProperties` (dictionary).
Each key of the `transportProperties` dictionary is a transport ID. The value is a dictionary containing properties for the respective transport, where the keys and values are strings. The `transportProperties` dictionary may not be empty.
**2: DECLINE** - Sent by an introducee to the introducer if the sender declines the introduction. Forwarded by the introducer to the other introducee. The message body is a BDF list with three elements: `messageType` (int), `sessionId` (unique ID), and `previousMessageId` (unique ID).
**3: AUTH** - Sent by an introducee to the introducer if accept messages have been sent and received. Forwarded by the introducer to the other introducee. The message body is a BDF list with five elements: `messageType` (int), `sessionId` (unique ID), `previousMessageId` (unique ID), `mac` (raw), and `signature` (raw).
Alice calculates the master key as `SHARED_SECRET("org.briarproject.briar.introduction/MASTER_KEY", bobEphemeralPublicKey, aliceEphemeralPrivateKey, clientMajorVersion, aliceEphemeralPublicKey, bobEphemeralPublicKey)`, where `clientMajorVersion` is the byte string `0x01`.
Bob calculates the master key as `SHARED_SECRET("org.briarproject.briar.introduction/MASTER_KEY", aliceEphemeralPublicKey, bobEphemeralPrivateKey, clientMajorVersion, aliceEphemeralPublicKey, bobEphemeralPublicKey)`. If the introducer forwards the ephemeral public keys without modifying them, this is the same value calculated by Alice.
The MAC sent by Alice covers a BDF list with three elements: `introducerId` (unique ID), `aliceInfo` (list), and `bobInfo` (list). The MAC label is `org.briarproject.briar.introduction/AUTH_MAC`. Alice's MAC key is `KDF("org.briarproject.briar.introduction/ALICE_MAC_KEY", masterKey)`.
The `aliceInfo` and `bobInfo` lists contain the information sent by Alice and Bob, respectively. Each list has four elements: `authorId` (unique ID), `timestamp` (int), `ephemeralPublicKey` (raw), and `transportProperties` (dictionary).
The signature sent by Alice is `SIGN("org.briarproject.briar.introduction/AUTH_SIGN", aliceIdentityPrivateKey, aliceNonce)`, where `aliceNonce` is `MAC("org.briarproject.briar.introduction/AUTH_NONCE", aliceMacKey)`.
The MAC sent by Bob covers a BDF list with three elements: `introducerId` (unique ID), `bobInfo` (list), and `aliceInfo` (list). The MAC label is `org.briarproject.briar.introduction/AUTH_MAC`. Bob's MAC key is `KDF("org.briarproject.briar.introduction/BOB_MAC_KEY", masterKey)`.
The signature sent by Bob is `SIGN("org.briarproject.briar.introduction/AUTH_SIGN", bobIdentityPrivateKey, bobNonce)`, where `bobNonce` is `MAC("org.briarproject.briar.introduction/AUTH_NONCE", bobMacKey)`.
Each introducee derives transport keys from the master key before sending an auth message, then deletes her ephemeral private key and the master key. The MAC keys are retained for sending and receiving activate messages.
**4: ACTIVATE** - Sent by an introducee to the introducer if auth messages have been sent and received. Forwarded by the introducer to the other introducee. The message body is a BDF list with four elements: `messageType` (int), `sessionId` (unique ID), `previousMessageId` (unique ID), and `mac` (raw).
The MAC sent by Alice is `MAC("org.briarproject.briar.introduction/ACTIVATE_MAC", aliceMacKey)`. The MAC sent by Bob is `MAC("org.briarproject.briar.introduction/ACTIVATE_MAC", bobMacKey)`.
Each introducee deletes the MAC keys after sending and receiving activate messages.
**5: ABORT** - Sent by any party to abort the protocol. The message body is a BDF list with three elements: `messageType` (int), `sessionId` (unique ID), and `previousMessageId` (unique ID).
Each introducee deletes her ephemeral private key, the master key, and the MAC keys before sending an abort message, or after receiving an abort message.
### State machines
Introducer state machine:
![introducer-state-machine-3a](/assets/clients/Introduction-Client/introducer-state-machine-3a.png)
[introducer-state-machine-3a.odg](/assets/clients/Introduction-Client/introducer-state-machine-3a.odg)
Introducee state machine:
![introducee-state-machine-3a](/assets/clients/Introduction-Client/introducee-state-machine-3a.png)
[introducee-state-machine-3a.odg](/assets/clients/Introduction-Client/introducee-state-machine-3a.odg)
### Validity policy
* A request, accept or decline message is valid if it is well-formed and its previous message (if any) is a valid message from the same sender in the same session.
* An auth message is valid if it is well-formed and its previous message (if any) is a valid message from the same sender in the same session. An introducee receiving an auth message must also validate the MAC using the other introducee's MAC key and the signature using the other introducee's identity key.
* An activate message is valid if it is well-formed and its previous message (if any) is a valid message from the same sender in the same session. An introducee receiving an activate message must also validate the MAC using the other introducee's MAC key.
### Storage policy
* All messages are stored.
### Sharing policy
* All local messages are shared.
# Messaging Client
The messaging client is a [BSP client](https://code.briarproject.org/briar/briar-spec/blob/master/protocols/BSP.md) that synchronises private messages between pairs of devices.
### Identifier
The client's identifier is `org.briarproject.briar.messaging`. The major version is 0.
### Groups
The client uses a separate BSP group for communicating with each contact. The [group descriptor](https://code.briarproject.org/briar/briar-spec/blob/master/protocols/BSP.md#23-groups) is a [BDF list](https://code.briarproject.org/briar/briar-spec/blob/master/BDF.md) containing the unique IDs of the contacts' identities, sorted in ascending order as byte strings.
### Message types
**PRIVATE_MESSAGE** - The message body is a BDF list with one element: `text` (string).
### Validity policy
* A private message is valid if it is well-formed.
### Storage policy
* All messages are stored.
### Sharing policy
* All local messages are shared.
# Private Group Client
The private group client is a [BSP client](https://code.briarproject.org/briar/briar-spec/blob/master/protocols/BSP.md) that synchronises messages among groups of devices. It is used in conjunction with the [private group sharing client](private group sharing client).
The creator of each private group is the only user who can invite other members. Any member can post messages to a group. Messages are signed by their authors.
### Identifier
The client's identifier is `org.briarproject.briar.privategroup`. The major version is 0.
### Groups
Each private group is represented by a separate BSP group. The [group descriptor](https://code.briarproject.org/briar/briar-spec/blob/master/protocols/BSP.md#23-groups) is a [BDF list](https://code.briarproject.org/briar/briar-spec/blob/master/BDF.md) with three elements: `creator` (list), `name` (string), and `salt` (raw).
`creator` is a list with three elements: `formatVersion` (int), `nickname` (string), and `publicKey` (raw). This identifies the user who created the private group. The salt is 32 random bytes, to prevent collisions between private groups with the same creator and name.
### Message types
**0: JOIN** - Sent by a new member to announce that she has joined the private group. The message body is a BDF list with four elements: `messageType` (int), `member` (list), `invite` (list or null), and `memberSignature` (raw).
`member` is a list with three elements: `formatVersion` (int), `nickname` (string), and `publicKey` (raw). This identifies the user who has joined the private group.
`invite` is null if the member created the private group. Otherwise it is a list with two elements: `inviteTimestamp` (int) and `inviteSignature` (raw). These are copied from the [invitation](Private-Group-Sharing-Client#message-types) sent by the creator.
`inviteSignature` covers a BDF list with three elements: `inviteTimestamp` (int), `contactGroupId` (unique ID), and `privateGroupId` (unique ID). `contactGroupId` is the ID of the group used by the creator and the member for [private group invitations](Private-Group-Sharing-Client#group-identifiers) - it can be calculated from the identities of the creator and the member. The public key from the private group descriptor is used to verify `inviteSignature`. The signature label is `org.briarproject.briar.privategroup.invitation/INVITE`.
`memberSignature` covers a BDF list with four elements: `privateGroupId` (unique ID), `timestamp` (int), `member` (list), and `invite` (list or null). The group ID and timestamp are taken from the message header. The public key from `member` is used to verify `memberSignature`. The signature label is `org.briarproject.briar.privategroup/JOIN`.
**1: POST** - The message body is a BDF list with six elements: `messageType` (int), `member` (list), `parentId` (unique ID or null), `previousMessageId` (unique ID), `text` (string), and `signature` (raw).
`member` is a list with three elements: `formatVersion` (int), `nickname` (string), and `publicKey` (raw). This identifies the author of the message.
`parentId` is the optional ID of a message in the same private group to which this message replies. `previousMessageId` is the ID of the previous join or post message sent by this member to this private group.
The signature covers a BDF list with six elements: `groupId` (unique ID), `timestamp` (int), `member` (list), `parentId` (unique ID or null), `previousMessageId` (unique ID), and `text` (string). The group ID and timestamp are taken from the message header. The public key from `member` is used to verify the signature. The signature label is `org.briarproject.briar.privategroup/POST`.
### Validity policy
* A join is valid if it is well-formed, has a valid signature from the new member, and has a valid signature from the creator unless the new member is the creator.
* A post is valid if it is well-formed, has a valid signature, its parent (if any) is a valid post, and its previous message is a valid join or post by the same member.
### Storage policy
* All messages are stored.
### Sharing policy
* All messages are shared.
# Private Group Sharing Client
The private group sharing client is a [BSP client](https://code.briarproject.org/briar/briar-spec/blob/master/protocols/BSP.md) that allows users to share private groups with their contacts, who may accept or decline the invitations. It is used in conjunction with the [private group client](private group client).
### Identifier
The client's identifier is `org.briarproject.briar.privategroup.invitation`. The major version is 0.
### Groups
The client uses a separate BSP group for communicating with each contact. The [group descriptor](https://code.briarproject.org/briar/briar-spec/blob/master/protocols/BSP.md#23-groups) is a [BDF list](https://code.briarproject.org/briar/briar-spec/blob/master/BDF.md) containing the unique IDs of the contacts' identities, sorted in ascending order as byte strings.
### Roles
All communication happens between two contacts, who may take different roles with respect to each private group. If one of the contacts created the private group, that contact takes the role of **creator** and the other takes the role of **invitee**. Otherwise both contacts take the role of **peer**.
### Sessions
The messages exchanged between two contacts referring to a given private group constitute a session. The private group's unique ID is used as the session ID.
### Message types
The protocol uses four message types.
**0: INVITE** - Sent by the creator to the invitee. The message body is a BDF list with six elements: `messageType` (int), `creator` (list), `groupName` (string), `salt` (raw), `text` (string or null), and `signature` (raw).
`creator`, `groupName` and `salt` are taken from the private group descriptor. `creator` must be the identity of the contact who sent the message.
`text` is an optional message from the creator to the invitee explaining the invitation.
The signature covers a BDF list with three elements: `timestamp` (int), `contactGroupId` (unique ID), and `privateGroupId` (unique ID). The timestamp and contact group ID are taken from the message header. The private group ID is calculated from the private group descriptor. The public key from the private group descriptor is used to validate the signature. The signature label is `org.briarproject.briar.privategroup.invitation/INVITE`.
The creator sets the private group's visibility to VISIBLE when sending an invite message.
**1: JOIN** - Sent by an invitee in response to an invite, or by a peer when sharing the private group with a contact who is also a member. The message body is a BDF list with three elements: `messageType` (int), `privateGroupId` (unique ID), and `previousMessageId` (unique ID).
`previousMessageId` is the ID of the previous message in this session.
An invitee sets the private groups's visibility to SHARED when sending a join message. The creator sets the private group's visibility to SHARED when receiving a join message.
A peer sets the private group's visibility to VISIBLE or SHARED when sending or receiving a join message, depending on the state machine.
**2: LEAVE** - Sent by an invitee in response to an invite, or by an invitee or a peer when leaving the private group, or by the creator when dissolving the private group. The message body is a BDF list with three elements: `messageType` (int), `privateGroupId` (unique ID), and `previousMessageId` (unique ID).
`previousMessageId` is the ID of the previous message in this session.
The sender sets the private group's visibility to INVISIBLE when sending a leave message. The recipient sets the private group's visibility to INVISIBLE when receiving a leave message.
**3: ABORT** - Sent by either party when recieving a message that is valid but unexpected in the current state. The message body is a BDF list with two elements: `messageType` (int) and `privateGroupId` (unique ID).
The sender sets the private group's visibility to INVISIBLE when sending an abort message. The recipient sets the private group's visibility to INVISIBLE when receiving an abort message.
### State machines
Creator state machine:
![creator-state-machine](/assets/clients/Private-Group-Sharing-Client/creator-state-machine.png)
[creator-state-machine.odg](/assets/clients/Private-Group-Sharing-Client/creator-state-machine.odg)
Invitee state machine:
![invitee-state-machine](/assets/clients/Private-Group-Sharing-Client/invitee-state-machine.png)
[invitee-state-machine.odg](/assets/clients/Private-Group-Sharing-Client/invitee-state-machine.odg)
Peer state machine:
![peer-state-machine](/assets/clients/Private-Group-Sharing-Client/peer-state-machine.png)
[peer-state-machine.odg](/assets/clients/Private-Group-Sharing-Client/peer-state-machine.odg)
"Member announcement" in the peer state machine indicates receipt of the contact's [join message in the private group](Private-Group-Client#message-types), demonstrating that the contact is a member.
### Validity policy
* An invite message is valid if it is well-formed, is sent by the group creator, and has a valid signature from the group creator.
* A join, leave or abort message is valid if it is well-formed and its previous message (if any) is a valid message in the same session.
### Storage policy
* All messages are stored.
### Sharing policy
* All local messages are shared.
# Transport Key Agreement Client
The transport key agreement client is a [BSP client](https://code.briarproject.org/briar/briar-spec/-/blob/master/protocols/BSP.md) that establishes keys for newly added transports.
The client establishes transport keys with each contact for any transports that were added more recently than the contact was added.
A key agreement session may be started between two peers, X and Y, in the following cases:
1. Neither of the peers supported the transport when they added each other as contacts. In this case, each peer will send a key message when it adds support for the transport. If a peer receives a key message for a transport it doesn't support, it will defer handling the message until it supports the transport. The session will complete when both peers support the transport.
2. X did not support the transport when the peers added each other as contacts, but Y did. In this case, when X later adds support for the transport, X will start a key agreement session. Although Y already has keys for the transport, it will complete the session in order to establish a new set of keys that X also possesses.
### Identifier
The client's identifier is `org.briarproject.bramble.transport.agreement`. The major version is 0.
### Groups
The client uses a separate BSP group for communicating with each contact. The [group descriptor](https://code.briarproject.org/briar/briar-spec/-/blob/master/protocols/BSP.md#23-groups) is a [BDF list](https://code.briarproject.org/briar/briar-spec/-/blob/master/BDF.md) containing the unique IDs of the contacts' identities, sorted in ascending order as byte strings.
### Message types
The protocol uses two message types.
**0: KEY** - The message body is a BDF list with three elements: `messageType` (int), `transportId` (string), and `publicKey` (raw). `publicKey` is an X25519 public key.
**1: ACTIVATE** - The message body is a BDF list with three elements: `messageType` (int), `transportId` (string), and `previousMessageId` (unique ID). `previousMessageId` is the ID of the sender's key message in this session, which is a dependency.
### Validity policy
* A key message is valid if it is well-formed and the public key is valid, i.e. the X25519 raw shared secret is not all zeroes.
* An activate message is valid if it is well-formed and its dependency is a valid incoming key message with the same transport ID.
### Storage policy
* A key message for an unknown transport is deferred.
* All other messages are stored.
### Sharing policy
* All local messages are shared.
### State machine
![key-agreement-client](/assets/clients/Transport-Key-Agreement-Client/key-agreement-client.png)
[key-agreement-client.odg](/assets/clients/Transport-Key-Agreement-Client/key-agreement-client.odg)
The `ACTIVATED` state lets us know that we already completed a session, so that we don't create a new session if we receive another key message from the contact. Otherwise a malicious contact could cause us to create an unlimited number of key sets and run out of memory.
### Key management
For each session, each peer generates an ephemeral X25519 key pair. The public key is sent in the peer's key message and the private key is stored until the peer receives the other peer's key message. Each peer then uses its own private key and the other peer's public key to derive the [root key](https://code.briarproject.org/briar/briar-spec/blob/master/protocols/BTP.md#2-key-management-protocol) for a set of rotation mode transport keys.
Each peer stores the transport keys, starts rotating them, and deletes its own private key before sending its activate message. A peer does not start using the new transport keys until it either receives the other peer's activate message or receives a BTP stream from the other peer that uses the new keys.
#### Roles
The peers assign themselves the roles of Alice and Bob by comparing their ephemeral public keys as byte strings. The peer whose ephemeral public key comes first is Alice, the other is Bob.
#### Notation
* || denotes concatenation
* Double quotes denote an ASCII string
* len(x) denotes the length of x in bytes
* int\_n(x) denotes x represented as an unsigned, big-endian, n-bit integer
#### Cryptographic primitives
The protocol uses two primitives:
1. **A cryptographic hash function**, H(m). In the current version of the protocol this is BLAKE2b.
2. **A key agreement function**, DH(pri, pub), where pri is one party's private key and pub is the other party's public key. In the current version of the protocol this is X25519.
We use H(m) to define a multi-argument hash function:
* HASH(x\_1, ..., x\_n) = H(int\_32(len(x\_1)) || x\_1 || ... || int\_32(len(x\_n)) || x\_n)
#### Key derivation
Alice calculates the raw shared secret as follows:
* raw = DH(pri\_a, pub\_b)
Bob calculates the raw shared secret as follows:
* raw = DH(pri\_b, pub\_a)
(*Note:* If a peer calculates an X25519 raw shared secret that is all zeroes, the peer must reject the incoming key message and not continue with the session.)
Both peers then derive the root key as follows:
* root\_key = HASH("org.briarproject.bramble.transport.agreement/ROOT\_KEY", raw, pub\_a, pub_b)
# Transport Properties Client
The transport properties client is a [BSP client](https://code.briarproject.org/briar/briar-spec/blob/master/protocols/BSP.md) that synchronises transport properties between pairs of devices. Transport properties describe how to connect to a device over various transports.
### Identifier
The client's identifier is `org.briarproject.bramble.properties`. The major version is 0.
### Groups
The client uses a separate BSP group for communicating with each contact. The [group descriptor](https://code.briarproject.org/briar/briar-spec/blob/master/protocols/BSP.md#23-groups) is a [BDF list](https://code.briarproject.org/briar/briar-spec/blob/master/BDF.md) containing the unique IDs of the contacts' identities, sorted in ascending order as byte strings.
The client also uses a group with an empty descriptor for storing local transport properties. This group is not shared with any contacts.
### Message types
**UPDATE** - The message body is a BDF list with three elements: `transportId` (string), `version` (int), and `properties` (dictionary). `transportId` and `properties` are supplied by the transport plugin. The keys and values of `properties` are strings. `version` is incremented whenever the properties change.
### Validity policy
* An update is valid if it is well-formed.
### Storage policy
* In the groups shared with contacts:
* For each transport, the local message with the highest version is stored.
* For each transport, the remote message with the highest version is stored.
* In the unshared group, the local message with the highest version is stored.
### Sharing policy
* In the groups shared with contacts, all local messages are shared.
* In the unshared group, no messages are shared.
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment