Technical Overview

The goal of this page is to explain the Jami network clearly and concisely without going into details like implementation, libraries, and APIs. For more in-depth information, see the Technical Reference.

Before reading

Distributed systems have many moving parts. You may have to research some of these terms and draw your own diagrams. You should have a basic understanding of public-key cryptography and computing concepts like software libraries.

If you are still struggling to understand part of this document, please reach out to us and we can try to help you understand it or make improvements, if needed.

Jami account

A Jami account is an x.509 certificate as defined by RFC 5280 with a 4096-bit RSA key pair.

Jami uses the GnuTLS library to generate and manage RSA keys and certificates.

Account certificate

An account certificate is generated at account creation and represents the identity of a Jami user. The certificate contains:

  • the public key

  • its 160 bit fingerprint (the Jami Id, usually displayed as 40 hex digits)

  • the fingerprint of the certificate issuer, which can be a certificate authority, but most likely is just same as the Jami ID, indicating a self-signature

  • the signature: a signed hash of the certificate

Device certificate

Each device has its own certificate. Device certificates look like account certificates except that instead of being self-signed they are signed by the RSA key associated with the account certificate.

For more information about Jami certificates, see Certificates.

The fingerprint of the device’s public key (analogous to a Jami Id) is called the Device Id.

Account storage and backup

Jami serializes account data using JSON, including contacts, daemon settings, certificates, revoked devices, display name, and almost anything else besides message history. The file is compressed using Gzip and stored in the account files folder as archive.gz. If you use an account password, this file will also be encrypted.

Linking a device entails simply copying this file to another device over the DHT.

The DHT

Jami uses a distributed hash table library, OpenDHT (also led by Savoir-faire Linux), to find peers, connect new devices, and even temporarily store messages. Understanding how a DHT works is out of the scope of this overview, but if you want to learn more, this blog post is a good introduction.

For now, think of it as a distributed way to store key-value pairs on the Jami network.

You can think of the key k as a “location” on the DHT. put(k, v) stores the value v on the DHT at location k, while get(k) gets the value(s) stored at k.

The key, in this case, is always a Jami Id or Device Id. Jami checks that values are signed by a device key belonging to the correct Jami ID.

DHT example: announcing and checking presence

To announce your presence to the Jami network, simply put(<Jami ID>, <Device ID>) on the DHT. To check for the presence of your contacts, check the DHT for devices announced at the contact’s Jami ID: get(<Jami ID>).

Doing more with the DHT

It should be clear by now that the DHT is a useful data store that serves as an intermediary for peers. Clients exchange IP addresses, device and account certificates, text messages, device revocations, and more over the DHT.

Once accounts have exchanged their certificates, they can encrypt and sign messages before storing them on the DHT. This means that your IP address and messages are always encrypted to a 4096-bit RSA key while on the public DHT.

The OpenDHT library takes care of encrypting and signing values with your certificate. OpenDHT also handles certificate exchange with an “identity layer” for publishing certificates. See OpenDHT’s documentation for more information.

OpenDHT has other useful features, like the ability to “listen” on a DHT location (rather than polling every X seconds), but we will not cover all of them.

Significance of the Jami Id

Jami Ids are a fundamental way to “bootstrap” secure communication between two peers. As long as you have the correct Jami Id for your friend, nobody can impersonate them or read messages you’ve sent them. The Jami Id is used to verify certificates during the initial certificate exchange.

Calls and file transfers

The DHT’s bandwidth and latency are not good enough for real-time communications (RTC) and file tranfers. For calls and file transfers, we must make direct (peer-to-peer) connections between devices.

Once our devices are connected, Jami uses the IETF SIP standard to share files and stream audio and video with the PJSIP library. The hard part is establishing a connection, and then securing it. Jami does the former with ICE and the latter with GnuTLS.

Connecting devices with ICE

We’ve seen that securely exchanging IP addresses over the DHT is easy, but how do we actually connect two devices? We need to navigate firewalls and other network conditions.

RFC 5245 defines ICE (Interactive Connectivity Establishment), a protocol for NAT traversal. Jami uses ICE to establish a peer-to-peer communication between two devices.

As a quick summary, each client gathers a list of potential IP:port addresses called ICE candidates. The peer initiating the call or tranfer stores these candidates on the DHT (encrypted to the recipient, of course). Once the recipient accepts the connection by storing their candidate list, the devices each build a list of candidate pairs and the devices negotiate a connection using rules specified by ICE.

Encrypting the connection with GnuTLS

Once a peer-to-peer communication channel has been established, the recipient device listens on it for incoming DTLS connections (acting as a DTLS server) while the caller initiates an outgoing DTLS connection (acting as a DTLS client).

The DTLS communication must be RFC6347 compliant.

Peers must only support PFS (perfect forward secrecy) cypher suites. The set of supported cypher suites is implementation defined but should include at least ECDHE-AES-GCM

TODO: specify the exact suites recommended to support.

During the DTLS handshake, both peers must provide their respective device certificate chain and must authenticate the other peer. Jami checks that its public key is the same key used during the DHT ICE exchange.

This connection is similar to the connection your browser makes with a web server (web browsers actually use DTLS for WebRTC). Jami uses GnuTLS instead of implementing its own crypto. This keeps connections up-to-date with the latest algorithms and security fixes, and helps us avoid the pitfalls of DIY crypto.

TODO: Someone please review the wording and correctness of this section^^^^

Swarms

Swarms are a new generation of group conversations.

Here is the January 2021 announcement about the feature.

The design is still changing frequently, so we will defer to the Technical Reference for a full explanation.

However, as a quick summary: Swarms use Git to store and sync conversations between participants over a p2p connection. Each swarm is a single distributed Git repository, and each message is a commit.

Participants include all linked devices, so swarms will bring much-requested full conversation sync between devices. This also fixes a few other things like contacts not seeing avatars.

Group conference calls

Jami manages audio and video conferences between more than two participants by treating the host as an ad-hoc server. The meeting host receives video and audio feeds from every participant, remixes them into a single stream, and sends the final version back to every participant.

At any time, a Jami account can be a rendezvous point, which allows people to call in and join the group conference whenever the account is online. It also adds the ability to set default moderators. This feature can be enabled and disabled in the account settings for any Jami account.

TODO: this section needs better explanation

Optional servers

In most circumstances, all of the core features work without any sort of central server. However, there are some optional servers in the Jami ecosystem which help with things like reliability and efficiency.

All of these servers are completely optional, they are Free/Libre software, and users may choose to host their own, change the default ones, or not use them at all.

DHT proxy and push notifications

OpenDHT provides a HTTP API that allows users to indirectly talk with the DHT. This is called a DHT proxy because all get, put, and listen operations are relayed through the node instead of the rest of the DHT.

The purpose of this feature is to enable push notifications and reduce energy consumption on mobile phones.

Instead of leaving the Jami daemon to crawl and monitor the DHT in the background, devices can ask the DHT proxy to watch the DHT and notify them through the official iOS and Android push services when new values appear.

A DHT proxy sends a wake up event through the device vendor’s push provider when it notices new values at the subscribed DHT locations. Then the daemon on the iOS or Android device wakes up and downloads the values directly from the DHTproxy.

Message contents, recipients, and other metadata are never sent through Google or Apple servers during this process, only wakeup events.

For more information about this feature, see OpenDHT’s documentation.

If you want to host your own DHT proxy, see our guide.

TURN and STUN

Name servers and ns.jami.net

DHT bootstrap servers

JAMS: Managed Jami networks for organizations