Introducing Relation ONE IM
Last updated
Last updated
Relation ONE IM is a decentralized private messaging protocol.
It implements Relation Protocol, manages identity via Relation Name Service, and manages contacts and groups with Relationship.
It uses Lit Protocol to encrypt and decrypt messages.
It uses Arweave as its decentralized storage solution.
Relation ONE IM realizes true decentralization and privacy.
It consists of four parts: (see Fig. 1-1)
Client: This is a front-end application for users to encrypt and decrypt messages and to send such messages via Proxy. It uses the standard interface provided by Relation Protocol to manage contacts and groups.
Proxy: This is a simple back-end program that accepts messages from "Client" only. It stores the messages to Arweave and pushes them to their receivers.
Decentralized Storage(Arweave): To store encrypted messages permanently.
Contracts: A set of contracts (Relation Name Service, Follow, DAO) conforming to Relation Protocol to store user information, Follow relationships, and information on DAOs.
See Fig. 1-1
A sender connects to "Client" using a wallet.
The "Client" uses Lit Protocol to encrypt the message.
"Client" sends the encrypted message to a "Proxy".
The "Proxy" stores the message permanently.
The "Proxy" sends the message to the receiver's "Client" via Websocket.
The receiver decrypts the message on the "Client" using Lit Protocol.
Each pair of Ethereum public and private keys constitutes an identity. Users can use wallets like MetaMask to connect to "Client".
An ethereum address is like an ID for a user. When messaging a user, the message body specifies the receiver's address, and the "Proxy" will push the message to the "Client" logged in using the target address.
The private key signs the message body to ensure the message is sent by that address and cannot be modified by anybody.
The identity is also used to login to Lit Protocol to encrypt or decrypt messages in the "Client".
The identity needs to register a SBT Token in [Relation Name Service](#2.1. Relation Name Service) as a prerequisite for private messaging.
Relation Name Service contract conforms to the Name Service standard published by Relation Protocol.
Before a user can use private messaging, a SBT Token shall be acquired by the user via registering with Relation Name Service
contract.
In a chat, we can see other users' information ,such as Name and Avatar, from Relation Name Service
.
Relation Name Service contract provides the following interfaces:
register: register(address owner, string calldata name, bool reverseRecord) external returns (uint)
set an avatar: setProfileURI(string memory profileURI) external
Query a user name: nameOf(address addr) external view returns (string memory)
Query an avatar: profileURI(address addr) external view returns (string memory)
"Client" is a front-end application. It can be a Web application, Chrome plugin, desktop or mobile application. In Relation ONE, it is a Chrome plugin.
When users are logged in their "Client"s, the "Client" will connect to Proxy via WebSocket. When someone sends messages to said users, the Proxy will push the messages to "Client", and the "Client" can decrypt the messages using Lit Protocol.
Before sending messages, the "Client" composites Messages according to the [Messages structure](#7.1. Message structure) , encrypts the Messages using Lit. Finally, the encrypted Messages are sent to Proxy.
Data privacy is secured this way because messages are encrypted and decrypted on the "Client" side.
The "Client" also manages [contacts](#5.1. Manage contacts) and [groups](#5.2. Manage groups) by calling respective contracts.
Relation ONE Proxy is a back-end application which provides a set of REST API (to receive messages from "Client" and to modify states) and a WebSocket interface (to push messages to "Client".)
"Proxy" will store the messages sent from "Client" to Arweave. Because the messages are stored in a decentralized manner, "Client" does not rely on any single "Proxy". Even if one "Proxy" fails, "Client" can change to another "Proxy" with the message data intact.
Client sends encrypted messages to Proxy via REST API. The Proxy will store the messages to Arweave according to the Message Persistence strategies and push the messages to the Client logged in by the receiver.
Data concerning states, such as unread messages, muted group messages, sticky messages, are stored by a "Proxy" in its own database. These data are not shared among different Proxies when a Client switches between them.
[Proxy](#4. Proxy) uploads [Messages](#7.2. Messages) containing one or multiple [Message(s)](#7.3. Payload). When uploading to Arweave, each Transaction has a tag pointing to the Hash of the previous Transaction.
There are three strategies for Message Persistence:
Pack and upload the messages to Arweave every 12 hours.
If there is no message within 12 hours, then the pack will not be conducted, and the count for "12 hours" restarts.
When the messages exceeded 100MB in size, they will be packed and uploaded to Arweave even if the scheduled task is not yet triggered.
A "Proxy" should provide the following interfaces:
Send messages
POST
/message/send
Query messages
GET
/message/list?limit=${limit}&offset=${offset}
Mark as read
POST
/message/read?messageId=${messageId}
Unread message count
GET
/message/unreadCount
Long
Chat lists
GET
/chats/list?limit=${limit}&offset=${offset}
Users manage contacts using Follow contract conforming to Relation Protocol.
Each [identity](#2. Identity) creates a Follow contract using FollowRegister
(see Fig. 1-4). When Alice follow
Bob, the former needs to call the follow()
method of Bob's Follow contract
.
Friend list: List all Follow contracts with FollowRegister
and list each user's follow information.
Follow: call the follow()
method from the other person's contract.
Unfollow: call the unfollow()
method from the other person's contract.
Group chats are managed using DAO contracts. The addresses listed in the contract which hold Semantic SBTs are considered group members.
"Client" manages groups by calling standard DAO interface.
Example: A creator creates a DAO contract via DaoRegister
. The isFreeJoin()
method is used to set whether people are free to join the group. With the join()
method, one can join a group chat. With the remove(address addr)
method, one can leave a group chat. (see Fig. 1-5)
Create a group chat: The Client calls the deployDaoContract(address owner,string memory name) external returns (uint256)
method of the DaoRegister
contract to create a new group chat.(See Fig. 1-5).
Join a group chat: If the isFreeJoin()
is set to True
, then one can use the join()
method to freely join a group. It is set to False
, then the administrator needs to add members via the method addMember(address[] memory addr) external
.
Leave a group chat : One can call the method remove(address addr)
to leave a group chat. Or, the administrator can call the method remove(address addr)
to remove a member.
Set a group announcement: The administrator of a group sends a special [message](#7.1. Message structure) in the group to realize this, with the kind
set to 3
.
Group name: Query a group's name via the method name()
.
Group avatar: Query the group avatar using the method daoURI()
.
Group members: The addresses listed in the contract holding its SBT Tokens are group members.
Users can chat with their [contacts](#5.1. Manage contacts) or send messages to Ethereum addresses not owned by their contacts.
A Client(A)encrypts payload
of the message using Lit, with accessCondition as the receiver's Ethereum address. The receiver
should also be the receiver's Ethereum address. Then, the procedure composites "Messages" according to the [Message Structure](#7.1. Message structure) and sends it to Proxy. The Proxy will push the messages to the Client (B) logged in with the receiver's address.
When sending group messages, a user A logs in a Client(A)and encrypts the message's payload
with Lit, with the accessCondition set to be visible to group members.) The receiver
should be set to the group chat's contract address. Then, the procedure composites "Messages" according to the [Message Structure](#7.1. Message structure) and sends it to Proxy. The Proxy will then push the messages to each Client of the users in the group, such as Client(C)、Client(D)、Client(E)...
[Client](#3. Client) implements Lit Protocol SDK. Each message is encrypted with the Client on the sender's side and decrypted with the Client on the receiver's side.
As with Fig. 1-6, Alice
wants to send an encrypted message to Bob
: Hi
.
Alice's "Client" will use the Lit SDK to encrypt Hi
locally. Subsequently, it generates the encrypted message encryptedString
and key symmetricKey
:
Alice's "Client" sets accessControlConditions
to be visible to Bob's address, and uploads accessControlConditions
and key symmetricKey
to Lit's node.
Alice's "Client" composites Message
and send it to "Proxy".
The Proxy forwards the message to Bob's "Client".
After Bob's "Client" received Message
pushed by the "Proxy", it acquires the key _symmetricKey
after it passed the verification conducted by the Lit node:
Bob decrypts the message locally:
How to generate messageId
: Composite a json string with the following format and calculate its Keccak256:
kind
0: p2p message。
1: group message。
2: dao message。
3: announcement。
4: hide message。
Session Key:
A pair of public and private keys are generated on the Client side as the Session Key. The Session Key is used to composite delegationMessage
. Then, the user's private key will be used to sign against delegationMessage
, resulting in delegationSig
.
When users send a message, they sign against the message using the private key of the Session Key pair, and attach the delegationMessage
and delegationSig
. With this delegationChain, it can be verified that the message is indeed signed by the said user.
How to generate sig
:
Sign messageId
using the private key of Session Key, and you will have a sig
.
"Messages" is an array with one or multiple "Message" with the following format:
Text message
CARD message
NFT
Local IMAGE
EMOJI
Batch transfer
ANNOUNCEMENT
The application uses Arweave to store encrypted messages permanently to ensure reliability and decentralization.
A series of [Message](#7.1. Message structure) is grouped into [Messages](#7.2. Messages) by [Proxy](#4. Proxy) and uploaded to Arweave. Each Transaction has a tag pointing to the Hash of the previous Transaction, so it is easy to find all history messages with the latest Hash.
Relation Name Service
0x04C23dDfE813d8D208bc8120b73F8EF30f423850
0xe4AA1f1E5be0A4E7F80CAC26C2Db1611C3E70c41
-
FollowRegister
0xA0cECa90d7A2033f5162dc73391cdB72272456E7
-
0x734dE4f1b3a1c0619ECB80e3a676a6cFabe72Adc
DaoRegister
0x153C3eF051C7665c9A9c1a718bF12bC8EE6b5115
-
0x7229Dc0117E8484D61137B7304b02b163beC912c
[]
[]