CLEMA protocol

Address Book

A device can give the server its address book, so that the people in it who use the service appear among the contacts — at once, and whenever one of them signs up later. Only numbers that belong to accounts ever come back; the rest wait on the server, unseen, to be matched when their owners join.

An address book belongs to the device, that is, to the session that uploaded it. Two phones of one account keep a book each, and the people found are those of both. A book is deleted when its session ends.

Upload Changes

The client sends what changed since it last synchronized:

{"id":"book-1","type":"phonebook.update","data":{"add":[{"phone":"+79161234567","name":"Ivan Petrov"},{"phone":"+79031234567","name":"Mom"}],"remove":["+79261234567"]}}
{"id":"book-1","type":"phonebook.update","ok":true,"data":{"hash":"9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08","count":212,"matches":[{"phone":"+79161234567","added":true,"user":{"user_id":67890,"nick":"Johnny","first_name":"John","last_name":"Doe","about":"Hello!","gender":"male","avatar_hash":"a1b2c3d4"}}]}}
Field Type Description
reset boolean Optional: empty the device's book before the rest is applied
add array Entries to add, or to update when the number is there already: phone in E.164 form and the name the book shows for it
remove array of strings Numbers to take out
hash string Digest of the device's book after the change, see Keeping in Step
count number Entries in the device's book
matches array Entries of add whose numbers belong to accounts the caller may find: the phone, the account's public card in user — the fields of a user.search result — and added when this request put the person in the contact list

A request carries at most 500 entries, add and remove together; a larger book is uploaded in several requests, the first of them with reset. A request with a number not in E.164 form, with a number twice, or with a name longer than 128 characters or holding control characters returns invalid_format.

A person found is added to the contact list under the entry's name — or their own nick when the entry has none — the first time any of the account's books finds them, and the account's devices receive contact.added, with the phone that found them, in their account stream. Someone already in the list keeps the name they have there. Someone the user removes from the list is not brought back by an address book; adding them by hand still works.

A number is not matched when its account does not want to be found by the caller (its phone_discovery setting), when the two are separated by a block, and when it is the caller's own. A response never contains a number the request did not.

Keeping in Step

hash lets the client check that the server holds the book the client thinks it uploaded. It is the lowercase hex SHA-256 of the entries sorted by phone, each written in UTF-8 as the number, a line feed, the name and a line feed; an empty book hashes the empty string. The client computes the same over what it believes it uploaded:

phonebook.get returns the same hash and count without changing anything, for a check at startup:

{"id":"book-2","type":"phonebook.get"}
{"id":"book-2","type":"phonebook.get","ok":true,"data":{"hash":"9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08","count":212}}

The client normalizes numbers to E.164 before hashing or sending them, leaves out those it cannot, and keeps one entry per number.

Clear

{"id":"book-3","type":"phonebook.clear"}
{"id":"book-3","type":"phonebook.clear","ok":true}

Deletes the device's book, as when the person withdraws the app's access to it. Contacts the book added stay in the list.

People Who Join Later

When a number in a book comes to belong to an account — its owner signs up, or sets it with phone.set — and that account lets the book's owner find it, the person is added to the book owner's contact list as above. The book owner's devices receive contact.added with the phone that matched, and sessions without an active connection get a joined push.

Storage and Limits

The server keeps book entries — numbers and names — including those of people who have no account, because finding them when they join is what the book is for. Entries are used for nothing else and are deleted with the entry, the book or its session.

A device's book holds at most 5000 entries; adding past that returns invalid_state. An account adds at most 10000 entries a day across its devices, so a reinstall or two do not run out, while trying numbers at random would take very long; past the limit phonebook.update returns rate_limited with retry_after. Numbers are sent as they are: there are few enough phone numbers to hash every one of them, so hashing would hide nothing, and the protection lies in these limits.