Перейти к содержимому
UGunixgram

Protocol

UnixProto

UnixProto — транспорт, на котором работает Unixgram. Он решает одну задачу: доставить сообщение туда и обратно по плохому каналу, не выдавая при этом, что именно передаётся. Ниже — как это устроено.

Путь сообщения

Путь сообщенияWebbrowserAndroidnativeCaddy:443 TLSGatewayGo · :5222UnixProtoWS :4001Unixgramapp :3000PostgreSQLRedisgw:push · unxp:pushwssreverse_proxytcpobfuscated2fan-outКаждый push публикуется в Redis, чтобы его увидели клиенты всех реплик — WIRE.md §1.7
Веб-клиент подключается по WebSocket через TLS-терминатор. Нативное приложение может открыть прямое TCP-соединение с гейтвеем, минуя HTTP целиком. Оба пути сходятся на приложении.

Стек транспорта

Стек транспортаПакетauth_key_id · nonce · ciphertextОбфускацияAES-256-CTR · необязательноТранспортlegacy · abridged · intermediate · padded · fullTCPпоток байтовТранспорт выбирается один раз по первым байтам соединения — WIRE.md §1.2
Снизу вверх: байты TCP, кадрирование выбранного транспорта, необязательный слой обфускации и, наконец, пакет — то единственное, что видит прикладной уровень.

Рукопожатие и сессия

Рукопожатие и сессияКлиентСерверclient_hello · X25519 pubserver_hello · X25519 pub · ECDSA подписьпроверка подписи по закреплённому ключуHKDF-SHA256 → auth_key · server_saltrpc · salt + msg_id (строго возрастает)resume · последний msg_id → досылка пропущенногоНезашифрованные вызовы отклоняются: GATEWAY_REQUIRE_ENCRYPTION = true — WIRE.md §1.5
Обмен ключами, проверка подписи сервера по закреплённому ключу и восстановление сессии после обрыва: клиент присылает номер последнего полученного сообщения, сервер досылает пропущенное.

Два независимых слоя

Транспорт отвечает за то, как payload нарезан на потоке TCP. Пакет отвечает за то, что внутри одного payload. Слои не знают друг о друге: клиент может выбрать любой транспорт, и формат пакета от этого не изменится.

Пять транспортов

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

Обфускация

Поток целиком, вместе с тегом транспорта, заворачивается в AES-256-CTR. Это не шифрование — ключ выводится из самого рукопожатия. Задача другая: убрать узнаваемые заголовки, по которым трафик классифицируется как «мессенджер».

Шифрованный канал

Обмен ключами — X25519, затем HKDF-SHA256 даёт 256-битный auth_key. Внутри пакета лежит соль, идентификатор сообщения, метод и параметры. Соль сравнивается за постоянное время, а идентификатор обязан строго возрастать — это защита от повтора. Незашифрованные вызовы сервер отклоняет.

Realtime поверх WebSocket

Второй контур — кадры MessagePack на отдельном порту. Версия протокола согласуется одним числом при рукопожатии и может быть только понижена. Клиент, который просит слишком старую версию, получает терминальный отказ и переходит на резервный канал.

Доставка между репликами

Реестр соединений живёт в процессе, поэтому push, пришедший на одну реплику, сам по себе не увидят клиенты остальных. Каждая отправка публикуется в общий канал Redis, и каждая реплика доставляет своим клиентам сама. Отправитель ждёт свою же копию обратно — иначе локальные клиенты получили бы событие дважды.

Транспорты и их накладные расходы на пакет

ТранспортТегКадрированиеНакладные
legacyuint32 BE длина + тело4 Б
abridged0xefдлина/4 одним байтом; 0x7f + 3 Б LE для длинных1–4 Б
intermediate0xeeeeeeeeuint32 LE длина + тело4 Б
padded intermediate0xdddddddduint32 LE длина + тело + 0–15 случайных байт4–19 Б
fullдлина + seqno + тело + CRC3212 Б