惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

V2EX - 技术
V2EX - 技术
P
Privacy International News Feed
Security Latest
Security Latest
H
Hacker News: Front Page
T
Tenable Blog
The Hacker News
The Hacker News
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
S
Security @ Cisco Blogs
Project Zero
Project Zero
O
OpenAI News
AI
AI
Spread Privacy
Spread Privacy
C
CERT Recently Published Vulnerability Notes
The Last Watchdog
The Last Watchdog
G
GRAHAM CLULEY
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Scott Helme
Scott Helme
Application and Cybersecurity Blog
Application and Cybersecurity Blog
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
C
CXSECURITY Database RSS Feed - CXSecurity.com
NISL@THU
NISL@THU
A
Arctic Wolf
T
Threat Research - Cisco Blogs
PCI Perspectives
PCI Perspectives
N
News and Events Feed by Topic
C
Cyber Attacks, Cyber Crime and Cyber Security
C
Cybersecurity and Infrastructure Security Agency CISA
Simon Willison's Weblog
Simon Willison's Weblog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Know Your Adversary
Know Your Adversary
Google Online Security Blog
Google Online Security Blog
罗磊的独立博客
L
LINUX DO - 最新话题
U
Unit 42
S
Security Affairs
有赞技术团队
有赞技术团队
WordPress大学
WordPress大学
博客园 - 【当耐特】
T
The Exploit Database - CXSecurity.com
S
Schneier on Security
月光博客
月光博客
Engineering at Meta
Engineering at Meta
腾讯CDC
F
Full Disclosure
Cyberwarzone
Cyberwarzone
S
SegmentFault 最新的问题
Recorded Future
Recorded Future
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
博客园 - 司徒正美
The Cloudflare Blog

Все публикации подряд на Хабре

Ловим музу за клавиатуру: как айтишнику стать автором Что умеет Midjourney в 2026? Мой немного грустный разбор этого шикарного инструмента Никто не любит писать тесты, но ИИ может исправить это IPv8 выглядит как мечта. Поэтому почти наверняка не взлетит Производители вернули в продажу материнки с DDR3. Что происходит? Управление агентом с телефона через Telegram теперь в KodaCode От координации к лидерству: как меняется роль руководителя разработки Я сделала родителям бизнес вместо пенсии: зарабатываем 70 тысяч, мама не даёт продать В три раза быстрее приемка товара и оптимизация трудозатрат на 73%: как «РСТ-Инвент» помог Gulliver Group ИИ-шечный мир победил? О влиянии искусственного интеллекта на игропром Кремль снижает давление на Телеграмм пока Европа строит интернет по паспорту Как CEO, CTO и CIO за 8 часов собрали ИИ-директора, который умеет держать позицию под давлением Как (не) потерять домен за выходные Вместо 8 разных VPS: как я организовал практику студентам на одном сервере Почему твой Open Source проект не замечают? R&D: искусство управления неопределенностью в разработке AI-дефляция: вакансий для разработчиков больше, а рост зарплат — худший за 15 лет Мы отдали управление роботами OpenClaw. Что из этого вышло Галактический ID: система идентификации для всех форм разумной жизни Шесть основ бизнес-анализа: начинаем с вопроса «Кто в игре?» Код-ревью, в котором дело не в коде Данные переехали. Команда — нет Системной подход к сдаче OSWE в 2025 Почему комната управления реактором покрашена в цвет морской пены 4 YAML-файла вместо PySpark: как аналитикам строить пайплайны без разработчиков LLM-агент для поиска свободных доменов: автоматизируем подбор Когда, зачем и как правильно начинать новую сессию в Claude Code? Как я заставил нейросеть писать макросы для FreeCAD Анатомия ИИ‑агента для подбора персонала. От тысячи резюме к топ‑10 за минуты Опыт разработчика как экономика внимания Автономность как точка невозврата: кто будет субъектом в цифровом будущем Обучение ИИ в «диких» условиях: как рутинные действия превращаются в датасеты Как измерить LLM для задач кибербеза: обзор открытых бенчмарков Где хранить код? Сравнение GitHub, GitLab и Bitbucket Математика объясняет, почему нормальное распределение встречается повсюду Почему ваш FinOps не работает: 12 тезисов от практиков Как подписать проектную документацию УКЭП с использованием бесплатных лицензий Pilot Адаптивное администрирование Sigla Vision Я грузил уран в бочки, а потом 20 лет строил ИТ в атомной отрасли Чем позвонить с Эвереста? История и обзор спутниковой связи. Часть 2 Как языковая модель помогает контролировать качество инструктажей по охране труда в металлургии Как не передать на desktop свой IP в РКН Анатомия SAP Privileges: как устроено управление правами в macOS MoneyDev: Сказка про три главных слова Обновлённый токенизатор видео K-VAE 2.0 от Сбера Как сделать диспетчеризацию дома на 1284 квартиры почти бесплатно Как мы разогнали железную дорогу Мы дали агентам рутину. Теперь надо решить — что делать с освободившимся временем Токсичный контент, промпт-хакинг и защита ИИ — всё о Guardrails для LLM Умный город начинается с точного взгляда: как «Фалькон Тех» меняет пространство к лучшему Навайбкодил приложение для анализа графов Почему Дюну так интересно читать? Упрощаем работу с рутиной или как стать Гендальфом Белым Деконструкция Go: CPU, RAM и что там происходит. Go Assembler база. Часть 1.1 Какие профессии исчезнут из-за ИИ, а какие появятся? И что с этим делать Как мы построили IT-отдел, где хочется расти: архитектурные встречи, прозрачные метрики и книжные подарки Rufler: Делаем из Claude Code автономный рой через один YAML-конфиг Sing-box и белый список приложений Как построить надёжный обмен сообщениями в микросервисах: лучшие практики для enterprise OpenAI строит MLM-пирамиду, а McKinsey и Accenture помогают ей в этом Дом, который не построил Фишер (Часть 2) «Сверхзвуковой математик» против «Вдумчивого логиста»: битва алгоритмов 3D-упаковки Мультимодальные модели – грубый и дорогой инструмент Разговоры ничего не стоят. Код тоже Проверки физических лиц: с кого начнет ФНС Топ-10 бесплатных нейросетей для создания видео в 2026 году Первые слои кода: как наши решения сегодня определяют архитектуру ИИ на десятилетия Разработка нового статического анализатора: PVS-Studio JavaScript Поиск уязвимостей ПО: базовый минимум или роскошный максимум Почему оценка персонала не работает как инструмент управления Как мы разработали ИИ-ассистента и сократили рутину продуктовой команды на 50% Как я ушел из найма, нажарил косточек и продал на маркетплейсах на 168 млн в год Когда 1С:ERP уже внедрена, а нормального производственного плана всё ещё нет Как я сделал Claude мультимодальным, подключив к нему Qwen Omni Как приглашение на вакансию мечты превращается в атаку Infrastructure as Code: философия и лучшие практики IaC Тестируем Yandex Code Assistant на задаче, в которой нужно хранить секреты nxs-universal-chart v3.0: новое поколение универсального Helm-чарта Callback Injection: Техника, которая отправила Microsoft Defender в глухой нокаут «Все идеи на стол»: митап как способ вывести проект из тупика Сегодня я узнал нечто новое о GPU благодаря багу в своей игре Как заставить LLM ̶ ̶г̶а̶л̶л̶ю̶ ̶ эволюционировать Карта событий как фундамент аналитики: практический кейс для E-commerce Что выбрать для AI: x86, ARM или RISC-V? Дайджест железа за март Роль соматических мутаций в развитии аутоиммунных заболеваний: путь к избирательной терапии Mythos от Anthropic — тревожный сигнал для всех, а не только для банков Guardrails для LLM на Java: как приручить промпт‑инъекции и токсичные ответы Green-VLA: как мы собрали VLA-модель для реального антропоморфного робота и не потеряли обобщение Финансовая гонка вооружений: почему умные люди добровольно в ней участвуют Эра ИИ-агентов наступила: выбираем лучшего цифрового сотрудника # Практический опыт внедрения WinCC Redundancy на производственном предприятии Сделал MVP за 3 дня, а потом неделю прикручивал оплату. Оно того стоило? Физика против Маска: почему Starship V3 может оказаться ещё одной катастрофой Нефть Венесуэлы: крупнейшие запасы в мире, но не крупнейшая нефтяная держава JPA 4. Переосмысление Hibernate Почему зеркальная фотокамера Nikon D5 десятилетней давности идеально подошла для миссии «Артемида-2» Проект «Уровень-Спутник» или как мы сделали платформу для гидрологов «Замедлиться, чтобы ускориться»: почему ИИ повышает цену ошибок в требованиях и архитектуре Как с нуля поднять трафик IT-компании на 1657% при бюджете 55 тыс. и выжить Pixel-perfect Downsampling — идеальная отрисовка 50 миллионов точек без потерь
NAT traversal в embedded P2P-мессенджере на Go: почему overlay routing, а не STUN/TURN/ICE
Ian Chechin · 2026-06-03 · via Все публикации подряд на Хабре

Средний

14 мин

8K

Несколько месяцев пилю embedded P2P-мессенджер на Matrix-протоколе как личный pet-проект в свободное от основной работы время. Стек: форк Dendrite (Matrix homeserver на Go), Pinecone overlay routing от matrix.org research, gomobile bind для упаковки в .aar и .xcframework, modernc.org/sqlite вместо CGO-варианта (иначе gomobile капризничает). Не туториал и не “hello world на gomobile”, а серьёзная архитектурная амбиция в свободное время. Делюсь reasoning’ами почему такие архитектурные выборы и где они начинают течь.

Без обещаний неубиваемости. Проект в активной разработке, на этапе интеграции в клиентское приложение поверх Rust SDK matrix.org. Цифры приведу с явной маркировкой “где замерено на моём стенде, где плановая оценка, что ещё не проверено”. Production-NAT-кейсы (CGNAT, реальные мобильные сети) - впереди в следующем рывке. Если что-то принципиально новое всплывёт - напишу продолжение.


Что под капотом моего стенда

Стек, который сейчас собран и работает у меня дома:

  • Matrix-протокол, форк Dendrite (Matrix homeserver на Go).

  • Embedded на мобильнике через gomobile bind: .aar для Android, .xcframework для iOS.

  • modernc.org/sqlite вместо mattn/go-sqlite3 (pure Go, без CGO - иначе gomobile в продакшен-сборке начинает капризничать).

  • Pinecone от matrix.org как overlay-роутинг.

  • Клиентское приложение поверх Rust SDK matrix.org делает E2E (Olm/Megolm), моя Go-библиотека в шифрование не лезет - только маршрутизирует.

  • NATS JetStream встроен в Dendrite как async-брокер событий.

Это библиотека-обёртка, не shipping-мессенджер. Тестовые приложения с двумя кнопками собирал под Android и iOS чтобы проверить connectivity между устройствами. Финальное product-приложение клиентской части - в планах, пока что библиотека собирается, интегрируется в клиент через bridge, и проверяется на demo-сборках.

Что уже работает: P2P-обмен сообщениями между двумя устройствами в одной Wi-Fi сети, cross-platform (Android ↔ iOS прошёл), fallback на Matrix-сервер при недоступности P2P, multi-device sync, SSL pinning к серверу через SHA-256 SPKI. Что сейчас в работе: двусторонний handshake-протокол через два custom Matrix event types для включения P2P-режима, мультидевайс через overlay-форвардинг.


Четыре варианта на столе

Когда сел проектировать NAT traversal, рассматривал четыре класса подходов. Без оценочных суждений в этом разделе, оценки дальше:

  • STUN/TURN/ICE (WebRTC-style). Классика. Клиенты обмениваются ICE candidates через signaling channel, пробивают NAT через STUN, при провале гонят трафик через TURN-сервер. Подробные русскоязычные разборы - WebRTC для всех и каждого (перевод webrtcforthecurious.com) и 5 ошибок при разработке WebRTC звонков от Voximplant.

  • libp2p. Модульный P2P-стек от Protocol Labs, используется в IPFS/Filecoin/Kubo. Kademlia DHT для discovery, AutoNAT v2 для определения reachability, DCUtR (Direct Connection Upgrade Through Relay) для hole punching, Circuit Relay v2 для fallback. Реализации на Go и Rust. Русскоязычные обзоры - P2P на Go: библиотека libp2p от Otus и flagship-перевод 2021 Азбука libp2p от Textile.

  • Overlay routing крипто-адресуемых сетей. Yggdrasil, cjdns, Hyperboria. У каждого узла IPv6-адрес производный от ed25519-ключа, маршрутизация по spanning tree (Yggdrasil) или по локально-вычисляемым координатам в виртуальном пространстве. NAT traversal неявный: узел открывает outbound TCP до bootstrap-пира, и связность достигается тем что 100% участников держат хотя бы одно outbound-соединение. Подробно - Что такое Yggdrasil Network от @Revertis (мейнтейнер Android-клиента) и Yggdrasil как встраиваемая библиотека от @AsciiMoth (форк yggdrasil-go).

  • Overlay routing matrix.org-style (Pinecone). Концептуально близко к Yggdrasil, но протокол спроектирован специально под Matrix federation. Двухслойная маршрутизация: SNEK (Sequentially Networked Edwards Key) для известных адресатов, spanning tree как fallback и для bootstrap’а. Open source - github.com/matrix-org/pinecone. На Habr не разобран, белое пятно.

Mesh-VPN корпоративного класса (Tailscale, ZeroTier, NetBird) тоже посмотрел - не подошли по сценарию (это VPN-доступ к собственным ресурсам, не peer-to-peer messaging). Подход librats + Hyperswarm + DCUtR из P2P в РФ: почему нужна система, а не протокол (kda2210, апрель 2026) - третий путь, DHT с hole-punching. Не делал бенчмарков, но в той статье есть конкретные измерения “1.4 MB RAM при 100 пирах vs 400 MB у JS-libp2p”.


Почему выпилил STUN/TURN/ICE

Не из идеологии. Конкретно под этот use case шесть причин:

  1. Нет естественного signaling channel. STUN/TURN/ICE предполагает что у клиентов уже есть способ обменяться SDP-offer/answer и ICE candidates. В P2P-мессенджере на Matrix-стеке такой канал, технически, есть - Matrix-сервер. Но архитектурная идея конкретно в том чтобы НЕ зависеть от него постоянно: сервер используется как “телефонная книга” pinecone-ключей и резервный канал доставки, не как signaling. Запустить overlay через сервер один раз для регистрации ключа и дальше коммуницировать через overlay - архитектурно чище.

  2. CGNAT в мобильных сетях. МТС, Tele2, Билайн через UE-инициированный CGNAT по сути дают симметричный NAT. Hole punching через STUN там не работает. Все WebRTC-проекты в этом сценарии падают на TURN. Это значит обязательный production-grade TURN-фарм и постоянные расходы на relay-трафик. Автор статьи про Jami в России прямо подтверждает: “встроенные TURN-серверы turn.jami.net недоступны (подтверждено тестами)”. TURN-инфраструктуру всё равно придётся поднимать (relay-узлы в следующем рывке), но как часть overlay-семантики, а не отдельную WebRTC-инфру.

  3. Бесплатные публичные STUN - известная ловушка. Voximplant в 5 ошибках при разработке WebRTC называет это ошибкой №1: “Не надейтесь на бесплатные STUN сервера (например, широко известные stun.l.google.com:19302) и тем более на ‘бесплатные’ TURN сервера”. Если строить production-grade мессенджер - нужен свой STUN/TURN. Это снова инфра.

  4. Размер deps на embedded. Pure-Go WebRTC-стек (pion/webrtc + ICE-агент) тащит несколько мегабайт. Для серверной разработки это ничего, для мобильной библиотеки каждый мегабайт критичен. У меня и так с Dendrite + Pinecone production-сборка .aar выходит в районе 35-40 MB. Добавлять полноценный ICE-stack ради одного use case (peer-to-peer соединение) - перебор.

  5. Мультидевайс плохо ложится на peer-to-peer connection. STUN/TURN/ICE - про установление соединения между двумя точками. У одного юзера может быть несколько устройств (телефон, планшет), и сообщение от собеседника должно дойти на все активные. Это или N×M ICE-соединений (плохо), или relay через signaling, что снова возвращает к Matrix-серверу как единой точке доверия, чего и пытаюсь избежать.

  6. Synchronous offer/answer. WebRTC signaling предполагает синхронность установки сессии. Это нормально для video-call’а, но плохо ложится на async-семантику мессенджера, где сообщение может прийти через несколько часов после отправки, и где соединение между двумя пирами либо есть постоянно, либо его нет.

Это не значит что WebRTC плохой. Для video calling, browser-based applications, screen sharing - идеален. Просто у меня другая задача: persistent multihop routing между подписанными ключами, а не peer-to-peer media stream.


Почему не libp2p

С libp2p отдельная история. Не подошёл по другим причинам:

  1. Heavyweight для embedded. go-libp2p тащит Kademlia DHT, GossipSub, Noise/TLS handshake stack, несколько muxer’ов (Yamux, mplex), несколько транспортов. Для серверного P2P это нормально, для мобильной библиотеки набегает несколько MB только на dependency tree, и большая часть функционала мне не нужна (своя discovery через registry на Matrix-сервере, своя federation-семантика Matrix).

  2. AutoNAT v2 на проде ещё прохладен. В апреле-мае 2026 опубликован cross-implementation audit от ProbeLab с конкретным root-cause: dialerHost в AutoNAT v2 наследует UDP black hole detector в read-only mode, и для свежих узлов (с пустой историей) detector трактует “пробное соединение неуспешно” как “соединение в blacklist”. Клиент остаётся в Unknown reachability state навсегда. Полный отчёт - github.com/probe-lab/autonat-perf-audit. Пробовал отправить fix в libp2p/go-libp2p (PR #3505), мейнтейнер закрыл с обоснованием что v2 не должен повторять архитектурный workaround от v1 - это, говорит, latch existing behavior, а не design pattern. Нормальная design conversation, но иллюстрирует что connectivity-стек ещё не стабилен.

  3. Нет нативного fit с Matrix federation. Matrix S2S API - HTTP-like запросы между серверами. libp2p даёт streams. Заворачивать Matrix в libp2p streams значит писать кастомный wrapper и поддерживать его. Pinecone специально проектировался matrix.org research как HTTP-transport-compatible: federation идёт почти-обычными HTTP-запросами поверх overlay.

  4. DCUtR требует Circuit Relay. Direct Connection Upgrade Through Relay сначала устанавливает соединение через relay, потом пытается upgrade’нуть до direct - это работает, но требует инфраструктуру relay-узлов и retry-цикл с известными граничными условиями. Те же relay-узлы у меня всё равно появятся (в следующем рывке), но в Pinecone-семантике (где relay - это просто узел overlay с лучшим reach), а не как отдельный libp2p-слой.

Снова - не приговор libp2p. IPFS, Filecoin, Kubo живут на нём, ProbeLab в общем заключении говорит что протокол работает. Для моего use case - overhead и mismatch архитектурной семантики.


Что такое overlay routing и Pinecone-специфика

Overlay routing в одной фразе: каждый узел открывает outbound TCP/WebSocket к одному из известных bootstrap-пиров, дальше трафик ходит многохопно через виртуальную сеть поверх обычного TCP. NAT проходится потому что соединение исходящее изнутри NAT’а - правило трансляции остаётся активным пока кому-то нужно отвечать через ту же сессию. Hole punching не нужен.

Это не магия и не “беспроигрышная альтернатива” - просто другой набор trade-off’ов. Overlay-подход переносит сложность из “договориться о direct connection между двумя пирами за NAT’ом” в “обеспечить связность виртуальной сети”. Yggdrasil делает это через крипто-spanning-tree. Pinecone - через комбинацию SNEK + spanning tree.

  • SNEK в одном абзаце. Sequentially Networked Edwards Key. Алгоритм построения виртуальной линии узлов, упорядоченной по ed25519-ключам. Каждый узел периодически отправляет bootstrap-сообщение в направлении возрастания ключа, ближайший по ключу сосед регистрирует себя как “descending” peer. В результате - двунаправленная линейная структура, по которой можно дойти от любого узла до любого через логарифмическое число хопов от размера сети. Реализация лежит в router/state_snek.go, периодический интервал bootstrap’а - 5 секунд.

  • Spanning tree. Параллельный механизм. Один узел сети объявляет себя root’ом (по наибольшему ed25519-ключу - детерминированно), root шлёт периодические TreeAnnouncement фреймы, узлы выстраивают tree относительно root’а. Координаты узла в tree - путь от root’а как вектор portID. Когда SNEK-путь неизвестен (свежий узел, ещё не накопил history), трафик идёт через tree.

  • Пять типов фреймов. Keepalive, TreeAnnouncement, Bootstrap (для SNEK setup), Traffic (пользовательский), WakeupBroadcast. Каждый фрейм имеет 10-байтный header с magic 0x70696e65 (ASCII pine). Описание - в types/frame.go.

  • Транспорты. Pinecone поддерживает Pipe (для тестов), Multicast (UDP в LAN), Bonjour (mDNS на macOS/iOS), Remote (TCP/WebSocket с TLS - основной production-транспорт), Bluetooth (через gomobile binding на мобильниках). Выбор маршрута приоритизирует Multicast < Remote < Bluetooth.

  • Самое главное для NAT-аргумента. В Pinecone нет STUN-клиента. Нет TURN-сервера. Нет ICE candidate negotiation. Цитата из README:

    Pinecone peering connections look like regular TCP or WebSocket connections and will work fine through firewalls or NATs. If you make an outbound connection to a static node, you will still be able to receive incoming Pinecone traffic over that peering.

То есть: мобильный клиент знает адрес одного-двух bootstrap-узлов (свои Relay в дальнейшем плане или публичные узлы matrix.org для текущего PoC), открывает к ним outbound TCP+TLS, NAT создаёт трансляцию. Другие узлы overlay-сети узнают этот клиент через распространение TreeAnnouncement и шлют ему трафик через тот же канал обратным путём. Hole punching не нужен - связность достигается тем что 100% клиентов хотя бы один outbound держат всегда.

Упрощаю, конечно. В реальной сети есть очереди фреймов, watermarks для дедупликации, backpressure-механизмы, выбор маршрута между tree и SNEK при наличии обоих. Деталей - в Pinecone-репозитории, подкаталог docs/. Там же есть симулятор - cmd/pineconesim/, поднимает виртуальную сеть с веб-визуализацией на localhost:65432. Удобно если хочется поиграться с топологией.


Как это собирается в стек

Архитектура с Go-стороны:

Клиент (мобильное приложение на Rust SDK matrix.org)
        │
        ▼ через gomobile bridge
[Go-библиотека: моя обёртка]
   ├── Embedded Dendrite (Matrix homeserver)
   │     └─ Matrix federation API
   │         └─ через Pinecone overlay routing
   │             └─ TCP/WebSocket → bootstrap peer (outbound из NAT)
   │
   ├── REST-клиент к Matrix-серверу (publish/get/deactivate Pinecone-ключа в registry)
   │     └─ через HTTPS с SSL pinning
   │
   └── Колбэки в клиент (handshake events, lifecycle статусы)

Клиент через Rust SDK хранит расшифрованную историю и делает Olm/Megolm. Моя Go-библиотека в шифрование не лезет - маршрутизирует обёрнутые matrix-events через overlay, и всё.

Архитектурные решения, на которые сел после reasoning:

  1. Защита per-chat, а не per-account. Юзер включает “щит” в конкретном чате через UI, не одной кнопкой на весь аккаунт. Pinecone-узел запускается один раз при первом щите в любом чате, переиспользуется для всех защищённых чатов, останавливается когда юзер выключает последний щит. Это даёт юзеру granular контроль и не заставляет постоянно держать P2P-стек для обычной переписки. Запуск идемпотентен: повторные вызовы из разных мест UI не пересоздают ключ и не делают повторных публикаций.

  2. Двусторонний handshake через Matrix-события. Включение защиты идёт через стандартный Matrix Client API: два custom event types - один для запроса включения P2P-режима, второй для ответа. В payload - pinecone-ключ инициирующей стороны. Только после обоюдного согласия активируется overlay-доставка для этой комнаты. Это обходит проблему “первое сообщение через overlay требует уже знать peer’s key” - до handshake’а overlay-узел может быть даже не запущен.

  3. modernc.org/sqlite вместо CGO-SQLite. Критично для gomobile. Стандартный mattn/go-sqlite3 - CGO-биндинг к libsqlite3.c. gomobile bind с CGO собирается, но требует Android NDK toolchain, увеличивает размер артефактов на несколько MB, и периодически ломается при обновлении gomobile (последний раз попал на это пару месяцев назад - дебаг-сценарий с lipo и манипуляциями над .xcframework). modernc.org/sqlite - pure Go, libsqlite3 транспилирован через c2go. Trade-off: примерно 5-10% медленнее на write-heavy workload (синтетика), зато gomobile собирается чисто без NDK-плясок.

  4. Мультидевайс синхронизация через overlay, а не через Matrix-сервер. У одного юзера может быть несколько устройств. Каждое регистрирует свой Pinecone-ключ в registry на сервере. При получении overlay-сообщения от собеседника моя библиотека находит другие свои устройства через registry и форвардит им то же зашифрованное (Rust SDK расшифрует на их стороне) сообщение через Pinecone, с меткой forwarded:true для защиты от петель. Matrix-сервер в синхронизации между своими устройствами не участвует. Дедупликация на принимающей стороне - по Matrix event_id. Это standard multicast-style sync с loop-protection меткой, у Yggdrasil примерно та же идея.

  5. SSL Pinning к Matrix-серверу через SHA-256 SPKI. Все HTTPS-запросы проверяют SHA-256 хеш Subject Public Key Info через кастомный http.Transport.VerifyPeerCertificate. Два хеша вшиты в код: основной + резервный. Это нужно для ротации сертификата без обновления приложения: вводим новый сертификат, в переходный период оба хеша валидны, через несколько недель приложение обновляется и primary заменяется. Известный паттерн, но в pure-Go реализации деталей хватает - возможно, отдельная статья.


Замеры на моём стенде

Disclaimer перед таблицами: цифры - с моего тестового стенда, не из синтетических бенчмарков. Это PoC-сборки, не production-готовый артефакт. Production-NAT-кейсы (CGNAT, реальные мобильные сети) ещё впереди. Кроме того, не все измерения одинакового качества: iOS-цифры замерял на реальном устройстве через Xcode Instruments, для Android реальных замеров на флагмане пока нет, эмулятор недостоверен.

iOS, 30-минутный тест, iPhone 16 Pro Max:

Метрика

Значение

RAM

6 MB

CPU при отправке/получении

3%

CPU средняя нагрузка

1%

Батарея

0.1%/час

Размер артефактов:

Артефакт

Значение

Замечание

.aar production

35-40 MB

Релизная сборка без debug-символов

.aar debug

около 68 MB

С debug-символами, тестовые сборки

.xcframework debug

161 MB

Два слайса (device arm64 + simulator arm64), каждый около 80 MB. Production-замер не делал

  • Android RAM/CPU отдельно. Замеры пока только на эмуляторе - получаю около 120 MB RAM и ~0.5% CPU при средней нагрузке. Эмулятор делит ресурсы с хост-системой и эти числа недостоверны для реального устройства. На реальном Android-флагмане замеры в планах после релиз-сборки. На уровне гипотезы - ожидаю в районе 70-110 MB RAM на Snapdragon 8 Gen 2-3 классе, но это вилка, не измерение.

  • Из чего основной вес .aar. Главный вклад: NATS JetStream (встроенный async-брокер событий внутри Dendrite, не вырезается - критичен для federation flow), gomatrixserverlib (Matrix-логика как таковая), crypto (Olm/Megolm библиотеки), Bleve (поисковый индекс - на 6 MB, в плане вырезать через build tags после стабилизации функционала). Pinecone сам по себе небольшой - около 1-2 MB чистого кода.

  • Cross-platform connectivity подтверждён. Android и iOS на реальных устройствах в одной Wi-Fi обмениваются сообщениями через Pinecone multicast. Cross-network (через bootstrap peer в другой сети) пока тестировал только в локальном setup’е с docker-сетью. Реальная мобильная сеть с CGNAT - впереди.

  • Multi-device fallback flow. Полный fallback реализован: если overlay не доходит (peer offline, нет route’а, timeout) - моя библиотека возвращает типизированную ошибку, и клиент отправляет сообщение стандартным Matrix-каналом через сервер. Шифрование (Olm/Megolm) делает Rust SDK независимо от overlay. То есть worst-case - обычный Matrix-клиент с E2E. Best-case - overlay-доставка вообще мимо сервера.

  • 80-90% direct P2P без relay. Это плановая цифра из архитектурного видения, основана на статистике других overlay-сетей. Не моя измеренная. До production-relay-инфраструктуры и теста на реальном CGNAT подтвердить не могу.


Что overlay-подход не закрывает

Раздел про ограничения - обязательный, без него получится “магическая альтернатива WebRTC”, которой не бывает. Что overlay-подход на текущий момент не закрывает или закрывает с оговорками:

  1. CGNAT в мобильных сетях. Когда оператор гонит всех клиентов через один шлюз, outbound TCP от мобильника к bootstrap-peer’у работает (правило трансляции открывается), но второй мобильник в той же сети не может стать сам bootstrap’ом для других. Нужны public-IP relay’и где-то снаружи. Это явная цена overlay-подхода: relay-узлы не “опционально”, они часть боевой архитектуры. У Tailscale это DERP-серверы, у I2P - Tor-relay’и, у Yggdrasil - публичные peer-lists, у Pinecone это будут мои relay-узлы в следующем рывке. Никто этого не избежал.

  2. Cross-network NAT traversal в реальных условиях ещё не проверен. PoC работает на одной Wi-Fi через Pinecone multicast. Связность через bootstrap-peer в другой сети - тестировал в локальном Docker-setup’е, но реальную мобильную сеть с CGNAT ещё не гонял. Это в плане. Если что-то всплывёт принципиально неожиданное - напишу честно.

  3. Invite routing нестабилен. Известная боль: invite-flow между Matrix-серверами через Pinecone overlay не всегда корректно маршрутизируется через многохоп. В PoC обхожу через создание комнаты без invite + join по room_id (юзер копирует room_id, не получает invite-event). Production-fix потребует или deeper rework Dendrite’s federation handler’а, или явного изменения UX.

  4. iOS background - 30 секунд жизни процесса. Это не overlay-проблема, это Apple. Любое мобильное приложение в фоне убивается через ~30 секунд, все TCP-соединения дропаются. Решается стандартным паттерном: APNs push → Notification Service Extension → wake-up Pinecone-узла в lite-режиме на короткий срок, чтение pending overlay-сообщений, сохранение в Rust-SDK-store для UI. У Signal и Element X точно тот же подход. NSE имеет жёсткий 30-секундный лимит на работу, поэтому lite-режим Pinecone должен подняться за <5 секунд.

  5. Android background - vendor wars. Foreground Service с уведомлением держит overlay-узел постоянно. Но на Xiaomi/Huawei/Samsung-оболочках Foreground Service всё равно убивается battery saver’ом, если юзер не добавил приложение в исключения. Это не моя вина, но юзеры воспринимают как мою. Понадобится отдельный гайд “добавьте в исключения battery saver’а на вашем телефоне”. Все мессенджеры в этом контексте в одной лодке, эталонная ссылка - dontkillmyapp.com.

  6. Размер артефактов. .aar 35-40 MB production, .xcframework 161 MB debug - это существенно больше чем у lightweight WebRTC-стека (несколько MB). Стоит ли overlay-подход этого веса - зависит от того, готов ли продукт не зависеть от своего сигнального сервера. Для большинства мессенджеров - нет, дешевле взять WebRTC и держать TURN-фарм. Для моего use case (privacy-first, anti-blocking, мультидевайс) - да.


Резюме

Overlay routing - не новая магическая альтернатива WebRTC, и не претендует. Это другой набор trade-off’ов:

  • меньше зависимость от STUN/TURN-инфраструктуры → больше зависимость от bootstrap/relay-узлов

  • меньше per-connection negotiation → больше per-network maintenance

  • меньше CGNAT-головной боли в connectivity → больше архитектурного веса в саму библиотеку

Под мой сценарий (privacy-first P2P-мессенджер на Matrix, embedded на мобильнике, мультидевайс) - работает. Под другие сценарии (browser-based, video conferencing, screen sharing) - не подойдёт, лучше WebRTC.

Если хочется поковырять самостоятельно - Pinecone open source, есть симулятор в cmd/pineconesim/ который поднимает виртуальную overlay-сеть на сотни узлов с веб-визуализацией. Удобно потрогать как трафик расходится при разной топологии. Yggdrasil тоже хороший entry-point если интересен этот класс задач, и AsciiMoth недавно написал отличный разбор Yggdrasil как библиотеки - похожая ниша, другая реализация.

После следующего рывка (relay-инфраструктура, CGNAT cross-network тестирование, push-flow для iOS) - посмотрю что вылезет в реальных мобильных сетях. Может, напишу продолжение с замерами уже на CGNAT и доли direct-vs-relay. А пока - спасибо что дочитали)


Ссылки

Pinecone и сопредельное:

По NAT traversal и hole punching:

libp2p:

Соседние работы на Habr: