Несколько недель назад я описал на Хабре идею «честного российского мессенджера» с открытым кодом и прозрачным compliance. Та статья была про «зачем». Эта — про «как получилось». Web-клиент и Android-приложение полностью переписаны и работают в production, серверная часть развёрнута и обслуживает реальные подключения. iOS пока не трогали. Ниже — разбор архитектуры, решения, которые мы приняли, грабли, на которые наступили, и открытый набор людей в проект.

Что готово
Backend (Tinode) — Личные чаты, группы до 200К, каналы, typing indicators, доставка/прочтение
Web UI — Чаты, группы, изображения, темная тема, базовые настройки
Мобильное приложение Android — Чаты, группы, изображения, темная тема, базовые настройки
Почему форк Tinode, а не свой сервер
Tinode (github.com/tinode/chat) — это ~13 тысяч звёзд на GitHub, разработка с 2015 года, и из коробки он даёт практически всё, что нужно мессенджеру: личный и групповой чаты, каналы, typing indicators, delivery confirmations, Drafty (формат rich-текста), кластеризацию, WebRTC-сигнализацию, push-уведомления.Написать такое с нуля — это полгода работы команды из 3-4 бэкендеров. С форком — берёшь рабочий сервер и кастомизируешь под себя.
Цена этого решения: GPL v3. Весь производный код сервера — тоже GPL. Это значит, что если вы хотите сделать проприетарный форк — не получится. Для нас это не проблема: проект открытый. Но это важно учитывать тем, кто рассматривает аналогичный подход для коммерческого продукта.
Почему свой Web UI, а не форк tinode/webapp
Оригинальный tinode/webapp написан на Angular-подобном фреймворке с собственной системой шаблонов. Кастомизировать его под другой дизайн-язык — боль. Мы переписали на React 18 + TypeScript + Tailwind CSS + Zustand.Результат: glassmorphism-дизайн, тёмная тема, отзывчивый интерфейс, полный контроль над UX. Стоимость — примерно 2-3 недели работы одного фронтендера.
Почему свой Android, а не форк tindroid
Оригинальный tindroid — на Java. Мы сделали Kotlin + Jetpack Compose + Hilt + Room. Но самое интересное — мы не используем официальный Tinode SDK на Android. Вместо этого — собственный WebSocket-клиент на OkHttp с ручной реализацией протокола.Зачем? Полный контроль над жизненным циклом соединения, корреляцией запросов и обработкой ошибок. Официальный SDK — отличная библиотека, но для production-приложения с offline-first и push-уведомлениями нам нужна была предсказуемость на каждом уровне.
Протокол: WebSocket + JSON, и это неплохо
Tinode работает поверх постоянного WebSocket-соединения. Каждое сообщение — JSON-объект. Вот как выглядит базовый обмен:
// Клиент → Сервер: подключение { "hi": { "id": "abc123", "ver": "0.25", "ua": "Lastochka/1.0" } } // Сервер → Клиент: подтверждение { "ctrl": { "code": 200, "text": "connected", "id": "abc123" } } // Клиент → Сервер: аутентификация { "login": { "scheme": "token", "secret": "eyJhbGc..." } } // Сервер → Клиент: успех { "ctrl": { "code": 200, "text": "ok", "id": "login_req_1" } }
Типы топиков (чатов):
Префикс | Тип | Назначение |
|---|---|---|
| P2P | Личная переписка |
| Group | Группы до 200 000 участников |
| Meta | Список чатов пользователя + уведомления |
| Find | Поиск пользователей по тегам |
Каждый топик — это не просто «чат», а абстракция с собственной системой прав. Tinode использует bitmask-модель:
J — Join R — Read W — Write P — Presence A — Approve S — Sharing D — Delete O — Owner
Это элегантно решает массу задач. Например, «замутить» чат — значит установить режим N (no access для уведомлений). «Закрепить» — записать timestamp пина в приватное поле (pvt) подписки. Никаких дополнительных таблиц.
Грабли, на которые мы наступили
1. Base64 для изображений — удобно, но дорого
Web-клиент сжимает изображения через Canvas API (max 1920px, JPEG 85%), конвертирует в base64 и отправляет прямо через WebSocket внутри Drafty-сообщения. Это работает, но base64 увеличивает размер данных на ~33%. Изображение 2 МБ превращается в 2,7 МБ по сети.Для MVP — приемлемо. Для production — переходим на presigned URL через MinIO. Планируется в следующем спринте.
2. Session expiry без явного уведомления
Токен аутентификации имеет TTL 14 дней. Когда он истекает, сервер возвращает 401. Но Android-клиент обнаруживает это не по коду ошибки, а косвенно: если после логина по токену список подписок топика me пуст — значит, токен принят, но сессия мертва. Это triggering forced logout.Костыль? Да. В идеале сервер должен присылать явное уведомление об истечении сессии. В Tinode это работает неочевидно. Мы написали обёртку, которая обрабатывает это на клиенте, но это — технический долг.
3. Compliance под GPL
Compliance-сервис — отдельный Go-бинарник. Он общается с Tinode только через HTTP на внутренней сети. Это позволяет ему не попадать под GPL v3, поскольку это не производный код, а отдельная программа, взаимодействующая по сети.Но грань тонка. Если compliance начнёт импортировать пакеты Tinode — он станет производной работой. Мы тщательно следим за границей модулей. Это архитектурное ограничение, которое нужно держать в голове.
Android: offline-first и Room
Архитектура Android-приложения заслуживает отдельной статьи, но коротко:StateFlow + MVVM + Repository + Room — стандартный стек, но есть нюансы.
Корреляция запросов через WebSocket
Поскольку WebSocket — асинхронный двунаправленный канал, ответ может прийти в любой момент. Мы сопоставляем запросы и ответы через уникальный ID:
private val pendingRequests = mutableMapOf<String, (ServerMessage) -> Unit>() suspend fun sendAndWait(request: Request): ServerMessage = suspendCancellableCoroutine { continuation -> val id = generateUniqueId() pendingRequests[id] = { response -> continuation.resume(response) } // таймаут 30 секунд }
Это работает как синхронный вызов REST API, но поверх WebSocket.
Room V1 → V2 без потери данных
Когда мы добавили reply-функциональность, потребовались два новых столбца: replyToSeq и replyToContent. Миграция написана вручную:
val MIGRATION_1_2 = object : Migration(1, 2) { override fun migrate(database: SupportSQLiteDatabase) { database.execSQL("ALTER TABLE messages ADD COLUMN reply_to_seq INTEGER") database.execSQL("ALTER TABLE messages ADD COLUMN reply_to_content TEXT") } }
fallbackToDestructiveMigration удалён. Данные пользователей сохраняются при обновлении.
Инфраструктура: Nginx на хосте, не в Docker
Production-стек развёрнут через Docker Compose: PostgreSQL, Redis, MinIO, Tinode, Compliance. Но Nginx — системный, на хосте.Причина: проксирование WebSocket требует тонкой настройки таймаутов (proxy_read_timeout 86400s — 24 часа). В Docker это работает, но при рестарте контейнера Nginx теряет upstream. Системный Nginx с конфигурацией, указывающей на хост-машину, надёжнее.
Два домена:
lastochka-m.ru— лендингapp.lastochka-m.ru— Web SPA + WebSocket proxy
TLS — Let’s Encrypt, Certbot с автоматическим обновлением.
Открытый набор
Проект «Ласточка» — open source. Код скоро будет доступен в Github, лицензия GPL v3 для серверной части. Кто хочет поучаствовать в проекте пишите.
Что вы получите:
Реальный production-проект с живыми пользователями
Полный доступ к кодовой базе и принятию решений
Упоминание в списке контрибьюторов
Возможность вписать строчку в резюме: «участвовал в запуске мессенджера»
Спасибо что дочитали до этого места, дальше уже не техническая, но важная информация
Правовое поле: ОРИ, 3 года хранения, идентификация
Compliance — это не «фича», которую мы прикрутили для галочки. Это юридическая необходимость, которая напрямую влияет на архитектуру. Вот ключевые требования к мессенджерам в России в 2026 году.
Организатор распространения информации (ОРИ)
Если сервис позволяет пользователям обмениваться сообщениями, он подпадает под определение ОРИ. После включения в реестр Роскомнадзора:
Хранение данных: 3 года. С 1 января 2026 года необходимо хранить сведения о действиях пользователей на территории РФ в течение 3 лет с момента их окончания (ранее — 1 год). Сами сообщения хранятся до 6 месяцев.
Предоставление данных. По запросу ФСБ — информация, необходимая для декодирования передаваемых сообщений, а также сведения о пользователях.
Штрафы. За неисполнение обязанностей, включая отказ от добровольного включения в реестр ОРИ — до 300 тыс. руб. для юрлиц, при повторном нарушении — до 1 млн руб. (ч. 1 ст. 13.31 КоАП РФ).
Идентификация и противодействие мошенничеству
С 1 июня 2025 года действуют новые нормы:
Обязательная идентификация по номеру телефона. Мессенджеры обязаны идентифицировать абонентов на основании договора с оператором связи. Передача сообщений от анонимных пользователей запрещена.
Блокировка противоправного контента. В течение суток после получения требования Роскомнадзора мессенджер обязан ограничить для указанного пользователя возможность отправки сообщений с противоправной информацией.
Российское ПО и платформы
Государство активно стимулирует использование отечественных решений:
Предустановка MAX. С 1 сентября 2025 года на все продаваемые в России смартфоны, планшеты и ПК должен предустанавливаться российский мессенджер MAX (ранее — VK Мессенджер).
Запрет на иностранные мессенджеры. Банкам, госорганам, операторам связи, маркетплейсам и другим организациям запрещено использовать иностранные мессенджеры (Telegram, WhatsApp, Viber) для общения с клиентами и передачи персональных данных.
MAX для бизнеса. С 1 сентября 2026 года торговые сети обязаны принимать через MAX данные о возрасте, льготном статусе и дисконтных картах. Отказ — нарушение.
Двухфакторная аутентификация. С 1 сентября 2026 года для подтверждения юридически и финансово значимых действий планируется использовать 2FA через СМС и MAX.
Потенциальные изменения
В феврале 2026 года в Госдуму внесён законопроект, запрещающий блокировать мессенджеры и соцсети целиком. Предполагается блокировка конкретного противоправного контента, а не всего сервиса.
Как это влияет на архитектуру «Ласточки»
Именно эти требования определяют наше ключевое архитектурное решение — compliance-сервис как отдельный бинарник с append-only аудит-логом.
Почему это не «просто middleware в Tinode»:
Хранение 3 года. Tinode хранит метаданные в PostgreSQL, но не заточен под 3-летнюю неизменяемую историю. Нам нужна отдельная таблица с триггерами на UPDATE/DELETE — любая попытка модификации вызывает
RAISE EXCEPTION. Это уровень базы, а не приложения.Аудит-лог — юридический документ. Каждый запрос ФСБ, каждый доступ к данным пользователя должен быть зафиксирован: кто, когда, на каком основании. И эта запись должна быть нестираемой — не только политикой, но и технически.
TOTP 2FA для регуляторов. Government API требует двухфакторной аутентификации. Это не «обычный» пользовательский логин — это отдельный поток с OTP-валидацией, rate limiting (максимум 5 попыток TOTP в минуту) и constant-time comparison токенов.
Идентификация по номеру телефона. Tinode поддерживает basic-auth и token-auth, но не интеграцию с операторами связи. Это слой поверх — отдельный endpoint, который проверяет номер через SMS-OTP перед созданием аккаунта.
Блокировка контента за сутки. Это означает, что compliance-сервис должен уметь мгновенно отозвать права на отправку сообщений у конкретного пользователя. Tinode-система прав (JRWAPSDO) позволяет это, но нужен быстрый pipeline: запрос от РКН → compliance → revoke W (Write) у пользователя.
Всё это — не функциональность мессенджера, а юридическая обвязка. И именно поэтому она вынесена в отдельный сервис:
Tinode (мессенджер, GPL v3) │ │ HTTP POST /internal/audit │ (только события: auth, gov-запросы) ▼ Compliance (наш код, не GPL) │ ├── PostgreSQL (audit_log — append-only) ├── TOTP 2FA middleware ├── Rate limiter (5 attempts/min) └── Government API (2FA-protected endpoints)
Эта граница — не параноидальная архитектурная чистота. Это лицензионная необходимость: GPL v3 Tinode не должен «заражать» compliance-код, который в будущем может потребовать отдельной лицензии. И это безопасность: даже если злоумышленник получит доступ к Tinode, он не сможет подделать аудит-лог, потому что тот лежит в отдельной базе с триггерами.
Заключение
«Ласточка» — это не попытка убить какой-либо (вы сами понимаете какой) мессенджер, это просто свободная альтернатива российского мессенджера, открытая и понятная.
Технически проект работает. Web и Android общаются через WebSocket. Сервер в production. Инфраструктура развёрнута и документирована. Если вам интересна идея открытого мессенджера с прозрачной архитектурой и честным compliance — welcome. Код, задачи и обсуждения — в репозитории (скоро обновлю ссылку)
Да, багов полно, но приложение умеет отправлять сообщения и изображения.
Как со мной связаться в телеграм - @Anton_Budylin
