Jami
Today we deviate from centralized or federated messaging and get closer to peer-to-peer, meaning the kind where messages don't rely on a server but start from one client and go directly to the other client.
Jami
Jami is a peer-to-peer messaging app in which user recognition (which we will call host, even though in reality each host can have multiple accounts) is entrusted to a DHT (see below what that is).
Typically, P2P clients have advantages and disadvantages, including:
- PRO: they cannot be censored by shutting down a centralized server (and there is therefore no central server that, if it goes down, blocks all communications)
- PRO: they are therefore independent of a "controlling" entity
- CON: they expose your connecting IP address
- CON: they need to be constantly listening: since there is no centralized server, if two users want to contact each other they must be online at the same time (and on a phone this is a problem because it means not letting it go into deep sleep, consuming TONNES of battery)
As far as I'm concerned, point 3 is an acceptable compromise. Under mobile networks you are behind double NAT anyway (which reduces direct reachability), while in the case of desktop clients it is possible to use a VPN.
In Jami's case, point 4 was solved by providing the possibility of (without forcing you to) relying on a DHT proxy + FCM notifications: the client relies on servers (official or on-premise) that listen for you: when a message arrives, they send you a push notification and allow your Jami process to take charge of the message.
If the recipient is unreachable at the moment, the message is not guaranteed to be delivered and can be lost; delivery can only be retried based on the behavior of the client and the P2P network, not through persistent centralized storage. In this case, the "advice" is to install a Jami server, which is basically a clone process of your client with the same keys on something that stays on permanently (like a Raspberry Pi): in practice, it's not a server in the classic sense: it's just a companion bound to your account that listens when you can't and tells you the news when you get back online.
Furthermore, Jami allows multi-device support (something other competitors don't offer).
Bird's-eye view of how it works
┌─────────────┐ ┌─────────────┐ ┌────────────────┐
│ DHT │ │ ICE/SIP │ │ TLS/GNUTLS │
│ (finding │ ←→ │ (NAT │ ←→ │ (encryption) │
│ peers) │ │ traversal) │ │ │
└─────────────┘ └─────────────┘ └────────────────┘As in every self-respecting cryptography story, we must bring Alice and Bob into play.
Highly simplified workflow:
- Alice creates an account → generates a key pair (public/private): the public key is Alice's identity (hash of the key)
- Alice publishes her presence on the DHT
- Bob searches for Alice's ID on the DHT → gets her current IP address/port
- Direct connection (or via TURN if NAT is problematic)
- End-to-end encrypted communication
What is a DHT? (Distributed Hash Table)
First, let's brush up on the definition of an Hash Table, which is a table that associates a hash value with a key:
key "mario" → value "abcd123456"
key "lucia" → value "cde0123212"
key "marco" → value "9091019abf"A traditional hash table resides on a single host -> if the host goes down, the HT can no longer be queried.
The problem
I want a hash table that:
- does not live on a single host (no single point of failure)
- can have hosts added/removed without breaking everything
- can be queried from any host to get the answer
The solution
We take 1000 hosts. We give each a random numerical ID between 0 and 2^160 (a massive number).
We take a key, for instance "mario". We hash it, getting a number in the same range.
Fundamental rule: the key "mario" is stored on the host whose ID is closest to the hash of "mario".
"What does closest mean?" — XOR distance is used, but to keep it simple: imagine the numbers on a circular line. The host that owns the key is the one with the ID numerically closest to the hash of the key.
Example of looking up a value
We want to find the value associated with "mario".
- we calculate hash("mario") = 42 (made up, obviously)
- we contact any host on the DHT (literally any node)
- it replies: "I don't have key 42, but I know who is closer than me to 42: contact host X, they are closer"
- we go to X, and the same routine repeats until we find the one holding the key
Jami (like BitTorrent) uses the Kademlia DHT, which leads to finding the key in a few (logarithmic) steps even with billions of hosts.
Example of publishing a value
This is the dual operation of the previous one:
- Alice wants to publish hash(Alice's_public_key) → Alice's_IP_address.
- she calculates the hash of her public key
- she finds (using the same lookup mechanism) the hosts closest to that hash
- she sends them the (key, value) pair
Those hosts store it for a certain time (TTL).
What happens if a host leaves or lies
The DHT is redundant: a key is not stored on a single host but on the k closest hosts (typically k=8 or k=20).
If a host disappears, the key is still on the others. Periodically, hosts "reorganize" themselves and replicate the data.
An queried host has very little room to lie about the lookup because:
- multiple hosts are queried in parallel to avoid unstable nodes, and the geometric structure of the network makes it possible to verify if someone is routing the request in the wrong direction.
- validation of signed keys reduces the possibility of injection (though in purely theoretical terms it doesn't entirely eliminate the risk of DHT poisoning).
So what is a DHT
A DHT is a system where a set of hosts coordinates to act as a "distributed address book": given a key, they tell you which host has the associated value, without any single host knowing everything.

Keep in mind that the bootstrap server is necessary, but:
- caching exists
- you can run your own opendht node (so there is no dependency on Jami's official one)
[May the mole be with you]
Discussion and support channel. To discuss configurations or other topics, join us on fluxer.dreadful.work.

