Технический разбор для разработчиков

От автора

Я не участвую в разработке Jami и не являюсь профессиональным разработчиком этого проекта. Однако я потратил достаточно времени, изучая архитектуру Jami, тестируя его в российских сетях и разбираясь в документации.

В процессе стало очевидно две вещи:

  • У Jami огромный технический потенциал.

  • В реальных сетевых условиях (особенно в мобильных сетях России) он работает значительно хуже, чем мог бы.

Эта статья — попытка разобрать проблему с инженерной точки зрения и предложить возможные направления развития.

Если вы разработчик, знакомый с C++, сетевыми протоколами, ICE или распределёнными системами — возможно, этот разбор будет вам интересен.


Часть 1. Что такое Jami и почему он интересен

Jami (ранее GNU Ring) — это полностью децентрализованная коммуникационная платформа с открытым исходным кодом, развиваемая компанией Savoir-faire Linux при поддержке сообщества свободного программного обеспечения.

В отличие от большинства популярных мессенджеров, Jami изначально проектировался как peer-to-peer система.

Основные принципы архитектуры:

  • Поиск контактов: использует распределённую хеш-таблицу OpenDHT

  • Соединения между пользователями: устанавливаются напрямую через ICE (Interactive Connectivity Establishment)

  • Регистрация: анонимная — не требуется номер телефона или email.

  • Шифрование: все сообщения и звонки защищены end-to-end encryption, ключи хранятся только на устройствах .

Фактически Jami пытается реализовать модель «пользователь ↔ пользователь» без центральных серверов.

Jami и другие мессенджеры

Мессенджер

Архитектура

📱 Номер телефона

🌐 Серверы

🚫 Блокировки

Telegram

Централизован

Опционально

Telegram

Легко по IP

WhatsApp

Централизован

Обязательно

Meta *

Блокируется

Signal

Централизован

Требуется

Собственные

Возможны

Session

Децентрализован

Нет

Lokinet

Сложно

Briar

P2P

Нет

Нет

Почти невозможно

Jami

P2P + DHT

Нет

Bootstrap

Относительно устойчив

*Meta признана экстремистской и запрещена на территории РФ.

При правильной реализации такой подход делает систему очень устойчивой к блокировкам, потому что:

  • нет центрального сервера

  • нет единой точки отказа

  • сеть распределена между пользователями

Однако на практике возникают серьёзные проблемы.

Обзор от Треугольника


Часть 2. Почему Jami работает нестабильно в российских сетях

Я провёл серию тестов в различных сетях (домашний интернет, Wi-Fi, мобильные операторы) и собрал отзывы пользователей. Результаты показали, что основная проблема связана не с самим протоколом, а с особенностями современных сетей.

2.1. CGNAT в мобильных сетях

Большинство мобильных операторов используют Carrier Grade NAT (CGNAT). Это означает:

  • у абонента нет публичного IP-адреса

  • исходящие соединения проходят через общий NAT

  • входящие соединения невозможны

Особенно сложен случай симметричного NAT. В такой ситуации стандартные методы (STUN, UDP hole punching) работают крайне плохо. ICE может пытаться подобрать комбинации кандидатов, но вероятность успешного соединения остаётся низкой.

   Алиса (CGNAT)                      Боб (CGNAT)
   [Устройство] <--- (прямой P2P) ---> [Устройство]
        |                                  |
        |        (не работает)              |
        +----------------------------------+

Один из тестировщиков точно описал ситуацию:

«В мобильных сетях операторы часто используют симметричный NAT (CGNAT). Проброс P2P-соединения через такой NAT — задача нетривиальная. ICE пытается использовать STUN, но часто безуспешно».

2.2. TURN — костыль, который в России сломан

Когда прямое соединение установить не удаётся, ICE использует TURN-сервер. TURN (Traversal Using Relays around NAT) работает как ретранслятор:

   Алиса (CGNAT) → TURN-сервер → Боб (CGNAT)

Это решает проблему NAT, но создаёт новые сложности:

  • Весь трафик проходит через сервер → растёт задержка и нагрузка.

  • Публичные TURN-серверы могут быть перегружены или заблокированы.

  • В России встроенные TURN-серверы turn.jami.net недоступны (подтверждено тестами).

  • Поднять свои TURN-серверы на территории РФ бессмысленно из-за DPI и «белых списков» — соединение оборвётся на 16-м килобайте.

  • Jami не поддерживает TURN over DTLS/TLS — TURN-трафик легко детектируется DPI.

В результате TURN остаётся ненадёжным костылём.

2.3. Bootstrap-узлы DHT

Для входа в распределённую сеть клиент должен сначала найти bootstrap-узел. По умолчанию используется bootstrap.jami.net:4222. Если этот узел недоступен или заблокирован, клиенту становится сложнее подключиться к сети.

Проблема решается наличием альтернативных bootstrap-узлов, но в стандартной конфигурации их немного. Сообщество может поднять свои узлы, но они не включены в официальный список .


Часть 3. Каким должен стать Jami, чтобы работать в России

TURN — это архитектурный костыль, от которого необходимо отказаться полностью. Не как опция, а как обязательное требование для работы в современных российских сетях.

Целевая архитектура (без TURN)

Сценарий 1: Прямое соединение через IPv6 / улучшенный ICE

   Алиса (CGNAT, с IPv6)                                       Боб (CGNAT, с IPv6)
   [Устройство] <------------ прямое P2P (IPv6) ------------> [Устройство]
        |                                                           |
        |      (лёгкий сигналинг через DHT)                         |
        +-----------------------------------------------------------+

Что изменилось: Благодаря приоритизации IPv6 и улучшенному ICE клиенты находят друг друга напрямую через глобальные IPv6-адреса. NAT не требуется, TURN не нужен.

Сценарий 2: Ретрансляция через Relay Peer (если прямое соединение невозможно)

   Алиса (CGNAT)                 Relay Peer (Пётр, белый IP)          Боб (CGNAT)
   [Устройство] <------------> [Устройство Петра] <-------------> [Устройство]
        |                      (ретранслирует трафик)                  |
        |                                                              |
        | (обмен ключами и установка канала через DHT)                 |
        +--------------------------------------------------------------+

Что изменилось: Вместо централизованного TURN-сервера используется узел другого пользователя (Пётр) с белым IP. Трафик ретранслируется через него, но остаётся зашифрованным end-to-end.

Сценарий 3: Mesh-сеть (множественные пути)

        [DHT-сеть]  (распределённая, для поиска и маршрутизации)
         /       \       \
        /         \       \
   [Алиса]----[Пётр]----[Боб]
       \        /        /
        \  [Мария]      /
         \     |       /
          \----[Иван]--/

Что изменилось: Если нет прямого пути и даже одного ретранслятора недостаточно, сеть строит маршрут через несколько узлов. Протокол динамической маршрутизации выбирает оптимальный путь.


Часть 4. Инженерный план: что необходимо внедрить (для разработчиков)

Я не программист, но я изучил код Jami (доступен на git.jami.net) и документацию. Ниже — конкретные технические задачи, которые нужно решить, чтобы Jami стал по-настоящему неубиваемым.

Уровень 1. Улучшение P2P-соединений

1.1. Приоритизация IPv6 до фанатизма

Где копать: src/ice_transport.cpp, формирование ICE-кандидатов.

Что сделать:

· Принудительно поднять приоритет всех IPv6-кандидатов выше максимального приоритета IPv4.
· В ICE-переговорах форсировать выбор IPv6 даже при чуть худшем RTT.

Почему это необходимо: В российских сотовых сетях IPv6 уже активно раздаётся (МТС, Билайн, Мегафон). Если у обоих абонентов есть глобальные IPv6-адреса — NAT не нужен, P2P работает идеально. Оценка: до 70% мобильных пользователей могут получить прямое соединение.

1.2. Агрессивный UPnP с автоматическим ремаппингом

Где копать: src/upnp/upnp_control.cpp.

Что сделать:

  • Увеличить количество попыток UPnP-маппинга.

  • Реализовать механизм «port prediction» для роутеров, которые последовательно открывают порты.

  • Участить keepalive-пакеты для удержания NAT-маппинга (каждые 15 секунд вместо 60).

Почему это необходимо: Без агрессивного UPnP многие роутеры просто не успевают открыть порт до таймаута ICE.

1.3. Улучшенный ICE для симметричного NAT (CGNAT)

Где копать: патчи к PJSIP в contrib/src/pjproject.

Что сделать:

  • Имплементировать «port inference» — анализ поведения NAT и предсказание следующего порта для конкретного собеседника.

  • Добавить множественные параллельные ICE-переговоры с разными комбинациями кандидатов.

Почему это необходимо: Без этого прямое P2P через CGNAT невозможно в принципе.

Уровень 2. Современные транспортные протоколы

2.1. Переход на QUIC

Статус в Jami: В списке пожеланий есть «Add new transports (e.g. QUIC?)» . Это должно стать приоритетом.

Где копать: весь стек PJSIP или его замена.

Что сделать: заменить текущую транспортную связку (TCP для signalling, UDP для media) на единый протокол QUIC.

Почему это необходимо:

· QUIC лучше работает через NAT (встроенные keepalive, миграция соединений).
· Мультиплексирование без head-of-line blocking.
· Встроенное шифрование (TLS 1.3) — меньше зависимостей.
· Лучшая адаптация к мобильным сетям (смена Wi-Fi/4G не рвёт соединение).
· QUIC-трафик сложнее детектировать и блокировать DPI.

Уровень 3. P2P-ретрансляция (relay peers)

Если прямое соединение невозможно даже после всех улучшений, нужен механизм, не требующий централизованных серверов.

Идея: Если Алиса и Боб не могут соединиться напрямую, но у них есть общий друг Пётр с белым IP и хорошим каналом, пусть Пётр выступит ретранслятором.

Техническая реализация:

  • Добавить новый тип ICE-кандидата — peer_relay.

  • Узлы с белыми IP могут публиковать в DHT запись: «я готов ретранслировать трафик для до N пользователей».

  • При невозможности P2P клиент ищет в DHT ретрансляторы, доступные обоим абонентам.

  • Трафик идёт по схеме: Алиса → Релей (Пётр) → Боб, с end-to-end шифрованием.

Где копать: потребует изменений в протоколе сигнализации, DHT-схеме и ICE-стеке.

Преимущество: Сеть самоорганизуется. Чем больше пользователей с белыми IP, тем устойчивее становится сеть. Полная независимость от централизованных TURN-серверов.

Уровень 4. Расширенное использование DHT

DHT уже используется для поиска контактов и сигналинга . Теоретически её можно расширить для передачи небольших сообщений (текст, файлы) как в BitTorrent.

Что сделать: исследовать возможность добавления libtorrent (в feature requests Jami уже есть пункт «Add libtorrent support?») .

Почему это необходимо: Это даёт полную децентрализацию для не-медиа трафика.

Уровень 5. Mesh-маршрутизация

В более далёкой перспективе сеть может развиться в оверлейную mesh-архитектуру, где каждый узел знает несколько соседей и строит маршруты динамически (как BATMAN или OLSR).

Преимущество: сеть самоорганизуется и становится устойчивее с каждым новым участником.

Уровень 6. Полное удаление TURN из кодовой базы

Когда P2P, relay peers и mesh-сеть заработают в 99.9% случаев, TURN должен быть полностью вырезан:

  • Удалить relay-кандидатов из ICE (src/ice_transport.cpp).

  • Убрать настройки TURN из GUI.

  • Собрать PJSIP без TURN-поддержки.


Часть 5. Оптимизация: проброс соединения через реле (Hole Punching с координатором)

Внимательный читатель может задать справедливый вопрос: а обязательно ли гнать весь трафик через Петра? Что, если использовать его только как «трамплин» — для первоначального установления связи, а когда канал пробит, убрать его из цепочки и перейти на чистое P2P?

Технически это возможно, и это называется Hole Punching с координатором (Coordinated Hole Punching).

Схема выглядит так:
1. Алиса и Боб находятся за NAT (возможно, симметричным). Прямой Hole Punching не работает или затруднён.
2. Они подключаются к Петру (публичный IP). Пётр видит их внешние адреса: Алиса:PORT_A и Боб:PORT_B.
3. Пётр сообщает Алисе внешний адрес Боба, а Бобу — внешний адрес Алисы.
4. Алиса и Боб одновременно отправляют UDP-пакеты на внешние адреса друг друга.
5. Если NAT «лояльный» (full-cone или address-restricted), он пропустит ответный пакет, и прямое соединение установится. Пётр больше не нужен.

   Алиса (CGNAT)          Пётр (координатор)          Боб (CGNAT)
        |                         |                         |
        |----(1) Привет, я Алиса-->|                         |
        |                         |<---(2) Адрес Боба--------|
        |                         |--------(2) Адрес Алисы-->|
        |                         |                         |
        |----(3) Стучусь к Бобу--------------------------->|
        |<------------------------(3) Стучусь к Алисе-------|
        |                                                   |
        |=========== (4) Прямой P2P-канал пошёл! ===========|
        |                                                   |
        [Пётр больше не участвует]

Почему я не описал это сразу и почему это не отменяет необходимости в ретрансляции?

Потому что это работает не всегда. В современных мобильных сетях доминирует симметричный NAT (тот самый CGNAT). Его поведение убивает эту схему:

  • Когда Алиса стучится к Петру, NAT создаёт правило: «пакеты от Алисы к Петру» с портом PORT_A1.

  • Когда Алиса стучится к Бобу, NAT создаёт новое правило с другим внешним портом PORT_A2.

  • Боб ждёт пакет от Алисы на старый порт (PORT_A1), который он узнал от Петра, но пакет приходит с нового (PORT_A2). Результат — тишина.

Вывод

Использовать реле только для установки соединения — хорошая оптимизация, которую стоит добавить в арсенал ICE. Для 10–20% пользователей с менее строгими NAT она может сработать и снизить нагрузку на ретрансляторы.

Но для оставшихся 80% (симметричный NAT/CGNAT) без постоянной ретрансляции (Алиса → Пётр → Боб) не обойтись. Поэтому стратегия должна быть гибридной:

1. Пытаемся пробить дыру через реле (как описано выше).
2. Если не получилось — включаем полноценную ретрансляцию через того же Петра.


Часть 6. Что может сделать сообщество прямо сейчас

Даже без изменений в основном коде можно предпринять несколько шагов:

  1. Запуск дополнительных bootstrap-узлов в России. Это улучшит доступ к сети.

  2. Тестирование в различных типах NAT. Особенно полезны тесты в мобильных сетях (CGNAT).

  3. Обсуждение новых транспортов (QUIC, relay peers) на форуме Jami (https://forum.jami.net/).

  4. Создание форка Jami с российскими настройками (российские bootstrap-узлы, приоритет IPv6) и распространение через RuStore.

  5. Эксперименты с peer relay — прототипирование на базе существующего кода.


Заключение

Jami — это не просто мессенджер. Это единственная по-настоящему децентрализованная платформа, которая при правильной реализации может стать абсолютно неуязвимой для блокировок. Никаких серверов, никаких единых точек отказа, никакой зависимости от «доброй воли» провайдеров или регуляторов.

Но сегодня, в российских реалиях 2026 года, Jami не работает. И причина не в злом умысле разработчиков, а в том, что архитектура 10-летней давности не учитывает современных реалий: CGNAT в каждой мобильной сети, тотальный DPI и «белые списки», которые делают TURN-серверы бесполезными.

Что мы теряем прямо сейчас?

Мы теряем возможность иметь инструмент, который:

  • Не требует номера телефона

  • Не хранит метаданные

  • Не зависит от серверов

  • Не может быть заблокирован

Jami мог бы стать этим инструментом. Но пока он остаётся сырой идеей, разбивающейся о суровую реальность сотовых сетей.

Что нужно сделать, чтобы это изменить?

Я не программист. Я не могу написать код. Но я могу чётко сказать, какой код нужно написать:

Что изменить

Где копать

Зачем

IPv6 priority

ice_transport.cpp

70% мобильных пользователей получат прямое P2P

QUIC вместо TCP/UDP

Весь стек PJSIP

Современный транспорт, который не режется DPI

Relay peers

ICE + DHT

Ретрансляция через других пользователей вместо TURN

DHT как транспорт

OpenDHT + libtorrent

Полная децентрализация для текста и файлов

Mesh-сеть

Оверлейная маршрутизация

Самоорганизующаяся сеть без единых точек отказа

Кто нужен, чтобы это сделать?

  • C++ разработчики — для изменений в ICE-стеке и транспортном уровне

  • Специалисты по сетям — для работы с QUIC, NAT traversal, DPI

  • Эксперты по DHT — для расширения OpenDHT и интеграции libtorrent

  • Тестировщики — с разными типами NAT (особенно CGNAT)

  • Документаторы — чтобы объяснить изменения сообществу

Что можно сделать прямо сейчас, даже не меняя код?

  1. Поднять российские bootstrap-узлы OpenDHT — это может любой, у кого есть VPS в РФ. Инструкция есть в статье.

  2. Начать обсуждение на форуме Jamihttps://forum.jami.net/. Создать тему с ссылкой на эту статью.

  3. Сделать форк с российскими настройками — российские bootstrap-узлы, приоритет IPv6, отключение TURN. Выложить в RuStore.

  4. Прототипировать relay peers — взять код и попробовать добавить новый тип ICE-кандидата.

Почему это важно именно сейчас?

Потому что завтра может быть поздно. «Белые списки» уже работают. DPI становится умнее. TURN-серверы отмирают один за другим. Если мы не сделаем Jami по-настоящему P2P сейчас, через год он станет просто ещё одной заблокированной игрушкой.

Jami может стать тем, чем Telegram обещал быть, но не стал — действительно свободным, децентрализованным и неубиваемым мессенджером.

Но для этого нужны не просто обсуждения. Нужен код. Нужны люди, которые готовы взять и сделать.

Кто со мной?

Если вы разработчик — пишите в комментарии, в личку, на форум Jami. Если вы не разработчик, но хотите помочь — распространяйте статью, ищите тех, кто может, тестируйте форки.

Давайте сделаем так, чтобы в России был по-настоящему свободный и работающий P2P-мессенджер. Технически это реально. Осталось только взять и сделать.


P.S. Если у вас есть идеи, как улучшить предложенный план, или вы уже начали что-то делать — делитесь. Нам нужна любая помощь. Этот проект слишком важен, чтобы оставлять его на волю случая.

(обновление от 17.03.2026):

В комментариях мне совершенно справедливо указали на существование экосистемы libp2p. Для тех, кто, как и я, не в теме: libp2p — это не программа, а модульный набор инструментов (набор протоколов и библиотек) для создания P2P-сетей, который используется, например, в IPFS и Ethereum.

Самое интересное, что многие из описанных в статье «идеальных» решений в libp2p уже реализованы «из коробки»:

  • Ретрансляция через пиров (Peer Relay) — там это называется Circuit Relay и AutoRelay. Узлы с публичными IP автоматически становятся ретрансляторами для тех, кто за NAT.

  • QUIC — поддерживается как один из основных транспортов наряду с TCP.

  • Продвинутый NAT Traversal — используются механизмы AutoNAT и Hole Punching, которые работают даже с симметричным NAT.

  • DHT — встроенная Kademlia DHT для поиска узлов.

По сути, libp2p — это готовая реализация 70-80% той архитектуры, которую я предлагаю для Jami. Возможно, разработчикам Jami стоило бы обратить внимание не на "изобретение велосипеда" с патчами для PJSIP, а на интеграцию с libp2p. Это могло бы дать современный, отказоустойчивый сетевой уровень с минимальными затратами на разработку. Спасибо комментатору за наводку!

(обновление от 20.03.2026):

Тема на форуме Jami создана + issue