Основатель «Школы траблшутеров» Олег Брагинский и ученица Марина Строева с помощью ИИ-агента спроектировали архитектуру мессенджера: выбрали модель, отказались от ошибок, внедрили HTTP federation и довели систему до консистентного состояния без компромиссов.
Суть задачи заключалась в создании масштабируемой, федеративной коммуникационной платформы, которая объединяет в себе возможности современного мессенджера и системы видеоконференций, но работает по принципу распределённой сети автономных узлов.
В отличие от классических централизованных сервисов (WhatsApp, Telegram, Slack), где все данные проходят через единое облако владельца, здесь было необходимо разработать сеть равноправных инстансов для региональной или корпоративной локализации.
Каждый сервер должен быть полностью автономен, иметь свою базу данных и пользователей. Переписка и сигнализация между разными системами (из Москвы в Белград) должна происходить по защищённому протоколу с использованием криптографических подписей.
Вместо того чтобы пересылать тяжёлый видеотрафик между серверами, клиентское приложение подключается напрямую к медиа-серверу собеседника. Это минимизирует задержку и позволяет проводить звонки между разными странами так же качественно, как внутри одной локальной сети.
Система доставки мгновенных уведомлений должна быть вынесена на отдельные лёгкие узлы, что позволяет основному ядру заниматься только бизнес-логикой, в то время как тысячи соединений пользователей обслуживаются распределённым слоем, который легко масштабируется.
Проект включал продакшн-скрипты развёртывания. Новую суверенную федерацию или дополнительный медиа-сервер можно развернуть и подключить к сети одной командой, что подходит для сценариев, где важна защита данных, масштабируемость и низкая задержка:
Идеально для создания систем связи масштаба страны. Каждый регион или город может иметь свой инстанс, сохраняя данные жителей локально в соответствии с законодательством, но при этом все связаны в единую глобальную сеть.
Для компаний с жёсткими требованиями к безопасности. Головной офис и дочерние предприятия имеют свои серверы. Даже если связь с интернетом прервётся, внутри каждого филиала мессенджер и видеосвязь продолжат работать. Информация не покидает контур организации.
Мы как разработчики планируем использовать этот проект как фундамент для новых продуктов. На базе архитектуры можно быстро собрать «Slack на стероидах» для продажи другим компаниям, гарантируя, что данные будут физически находиться на их серверах.
Хорошо составленный план – залог успеха будущего проекта. Когда создана комплексная архитектурная структура, создаётся ощущение, что всё уже решено. Есть фазы, есть чёткие контракты, есть протоколы и даже deploy-скрипты. Остаётся только реализовать по пунктам.
Но на практике именно в этот момент начинается самая сложная часть – превращение схемы в живую систему, которая не просто запускается, а стабильно работает под нагрузкой, в разных регионах, с реальными пользователями.
В нашем случае план выглядел почти безупречно: федерация через HTTP с Ed25519-подписями, локальные pollnode для realtime, мульти-SFU модель для медиа и строгая дисциплина идентификаторов через UUIDv7. Но проблема любого большого плана в том, что он описывает, что должно быть, а не с какими проблемами можно столкнуться во время разработки.
На первом этапе запустили ИИ-помощника. Агент начал с того, что проверил главный инвариант: каждый инстанс должен быть полностью автономен. Это означает не только отдельные базы и Redis, но и отсутствие скрытых зависимостей – никаких shared-кэшей, никаких временных обходов.
Именно здесь всплыла первая потенциальная проблема: если хотя бы один компонент (pollnode) не полностью отвязан от core, вся система теряет устойчивость при partition. Решение оказалось не в переписывании логики, а в ужесточении контрактов.
Pollnode перестал знать что-либо о бизнес-логике. Стал простым транспортным слоем: принимает long-poll, читает Redis-стрим, отдаёт события. Всё! Любая попытка добавить туда немного логики сразу рассматривалась как архитектурный риск.
Следующий слой – realtime. На бумаге всё выглядело просто: есть два канала (rpc и events), есть Redis-stream, есть курсоры. Но при детальном разборе выяснилось, что без строгой привязки к target_client_id невозможно гарантировать корректное поведение звонков.
Особенно в multi-device сценариях. В результате envelope событий был пересобран так, чтобы каждое событие могло быть адресовано конкретному клиенту, а не просто пользователю. Теперь система оперировала не пользователями, а клиентскими сессиями.
Дальше – федерация. Самое опасное место любой распределённой системы – граница между инстансами. Именно там чаще всего возникают полурабочие состояния. Сообщение может быть создано на отправителе, но не доставлено получателю. Или звонок начался, но нет сигнала accept.
Чтобы избежать этого, был выбран строгий Z3-подход: синхронный federation-RPC. Пока remote-инстанс не подтвердил приём, сообщение считается pending. Если не ответил – queued, и включается outbox-ретрай. Это убирает иллюзию доставки и заменяет на чёткую модель состояний.
Именно здесь проявилась одна из самых недооценённых проблем – идемпотентность. Без dedup-таблицы на стороне inbox повторная доставка событий могла создавать дубликаты сообщений или ломать состояние звонков. Добавление federation_inbox_dedup стало обязательным.
Отдельная история – медиа. Выбор мульти-SFU модели выглядел очевидным на уровне архитектуры, но оказался крайне сложным на клиенте. Проблема в том, что классический mediasoup-клиент предполагает один transport и один device. Это работает, пока у вас один сервер. Но как только появляется второй SFU – всё ломается.
ИИ-агент чётко сформулировал: единственный путь – отдельная сессия на каждый SFU. С отдельным Device, transport’ами и lifecycle. Решение увеличило сложность кода, но убрало целый класс скрытых багов, связанных с несовместимостью capabilities и рассинхронизацией transport’ов.
Следующий момент – deploy. На ранних этапах система могла работать только в ручном режиме: кто-то поднимает сервер, кто-то настраивает ключи, кто-то прописывает peers.yaml. Это нормально для прототипа, но абсолютно неприемлемо для федерации.
Поэтому deploy был вынесен в отдельную фазу и доведён до состояния, где новый инстанс поднимается одной командой. Причём не просто поднимается, а сразу становится частью сети: генерируются ключи, настраиваются сервисы, регистрируются узлы, подключаются peer’ы.
Но даже после всех этих шагов оставалась одна фундаментальная проблема: соответствие плана и реализации. План может быть идеальным, но, если хотя бы один контракт реализован не полностью – система начинает вести себя непредсказуемо.
Именно поэтому был проведён полный аудит. Проверялись не только функции, но и переходы состояний, порядок событий, корректность teardown-процессов, обработка граничных сценариев:
- что происходит, если клиент исчезает без disconnect?
- что если SFU перезапускается в момент звонка?
- как ведёт себя система при задержке federation-RPC?
В итоге система приобрела следующие свойства:
- единый протокол для локальных и кросс-инстанс сценариев
- автоматизированный деплой без ручных шагов
- предсказуемость поведения при сбоях
- полная автономность инстансов.
И, пожалуй, самый важный результат – исчезновение «магии». Каждое событие, сообщение или звонок теперь проходит понятный путь, который можно отследить, воспроизвести и отладить. Это и есть главный признак зрелой системы. Не просто работает, а объяснима!