К содержанию
Попробовать

~/goydacord инженерная сторона

Как это
устроено

Голосовой мессенджер с серверами, звонками, демонстрацией экрана и клиентом для Windows. Ниже — архитектура, ключевые решения и цифры, которые можно проверить.

Смотреть схему
  • 351автотест бэкендаPHPUnit, CI на каждый пуш
  • 580+тестов фронтендаJest + Testing Library
  • 1080p·60показ экранаH.264 на видеокарте
  • 98Lighthouse, десктопдоступность и SEO — 100

01 · архитектура

Кто с кем разговаривает

Страницы и API идут через nginx, события — по WebSocket, а голос, видео и экран — мимо бэкенда, прямо в медиасервер. Бэкенд только выдаёт токены комнат и следит за правами.

Архитектура GoydaCordБраузер, приложение для Windows и телефон ходят через nginx по HTTPS к Next.js, Laravel API и Reverb. Laravel хранит данные в MySQL и Redis и отправляет Web Push. Голос, видео и экран идут напрямую в LiveKit SFU, для строгих сетей — через TURN.HTTPSсобытиятокены комнат, модерацияWebRTC: голос, видео, экранTURNБраузерNext.js 16 · React 19WindowsElectron 37Телефонбраузер + Web PushnginxHTTP/2 · TLSNext.jsSSR-страницы и приложениеLaravel ReverbWebSocket, приватные каналыLaravel 12 APISanctum · политики · сервисыMySQLпереписка в AES-256-GCMRedisкэш, лимиты, превьюWeb PushVAPID, звонки и ЛСLiveKit SFUUDP 7882 · TCP 7881coturnTURN для строгих сетейзапросы, WebSocketмедиа WebRTC

02 · решения

Что сделано и зачем

Каждое решение — ответ на конкретную проблему: что болело, как чинили и что получилось.

  1. 01Звук и видео

    SFU вместо сетки P2P

    В звонке на пятерых по схеме P2P каждый отправляет свой поток четырём людям — и упирается в исходящий канал. Голосовые каналы, личные и групповые звонки идут через свой LiveKit SFU: каждый отправляет поток один раз, сервер раздаёт.

    Медиа ходит через один UDP-порт, для строгих сетей есть TCP и свой TURN (coturn).

    Модерация — заглушить, отключить звук, перетащить в другой канал — проверяется правами на сервере и применяется прямо в комнате.

  2. 02Демонстрация экрана

    1080p · 60 кадров без нагрузки на процессор

    LiveKit по умолчанию кодирует видео в VP8 программно: на 1080p60 это около двух ядер процессора — у стримящего проседает игра.

    Экран публикуется в H.264, и Chromium отдаёт кодирование видеокарте (NVENC, AMF, Quick Sync). Когда сеть не вывозит, держим частоту кадров, а не резкость: в игре плавность важнее.

    Кодирует видеокарта, если умеет, — процессор остаётся игре.

  3. 03Шумодав

    Нейросеть прямо в браузере

    DeepFilterNet скомпилирован в WebAssembly и работает в AudioWorklet у самого пользователя: голос не уходит на сервер ради обработки. Вариант полегче — RNNoise, переключается в настройках.

    Модель весит 17 МБ, поэтому nginx отдаёт её заранее сжатой (gzip_static), без сжатия на лету.

    Клавиатура, кулер и улица вырезаются до отправки звука.

  4. 04Файлы

    Гигабайты с докачкой

    Файл уходит кусками по 16 МБ. Оборвалась связь — загрузка продолжается с того же места, а не с нуля.

    Небольшие вложения шифруются на диске целиком, большие отдаются потоком с перемоткой (HTTP Range). У каждого аккаунта квота 2 ГБ; если на диске кончается место, первыми уходят давно не открытые файлы.

    Видео на гигабайт отправляется так же, как картинка.

  5. 05Хранение

    Переписка зашифрована в базе

    Сообщения лежат в базе зашифрованными AES-256-GCM, у каждого записан номер ключа — ключи можно менять, не перешифровывая старую историю.

    Честно о границах: это шифрование на сервере, а не сквозное между устройствами.

    Утечка дампа базы не раскрывает переписку.

  6. 06Реалтайм

    WebSocket и пуши

    Сообщения, реакции, звонки, упоминания и ответы поддержки приходят по WebSocket через Laravel Reverb в приватные каналы пользователя.

    Если вкладка закрыта — Web Push (VAPID): входящий звонок и личное сообщение приходят системным уведомлением.

    Ответ поддержки виден сразу, без перезагрузки страницы.

  7. 07Надёжность

    Звонок переживает сбои

    Звонок восстанавливается после перезагрузки вкладки и перезапуска приложения. Присутствие держится на heartbeat, зависшие сессии убирает серверный крон.

    Таймеры переподключения сами планируют следующий шаг и не зависят от перерисовок React. При деплое старые JS-чанки хранятся ещё 7 дней, чтобы открытая вкладка с идущим звонком не упала на ChunkLoadError.

    Выкатка новой версии не обрывает текущие звонки.

  8. 08Windows

    Десктоп-клиент на Electron

    Автообновление скачивает только изменившиеся блоки установщика. Клиент видит запущенную игру и показывает её друзьям в статусе, отсеивая лаунчеры и античиты.

    Пока идёт звонок, клиент поднимает приоритет своих процессов и отключает для них энергосбережение Windows (EcoQoS): иначе в полноэкранной игре система душит фон и голос начинает заикаться.

    Голос не прерывается, даже когда игра на весь экран.

  9. 09Сеть в России

    Разбор блокировки у провайдера

    У части абонентов одного провайдера сайт открывался «через раз». Разобрали по шагам: TCP до сервера проходит, TLS с SNI обрывается, без SNI — работает, а соседние сайты из той же подсети /24 недоступны точно так же.

    Вывод — фильтрация на оборудовании провайдера по подсети, а не ошибка на нашей стороне. Сайт и десктоп-клиент перевели на HTTP/2: одно TLS-соединение вместо шести.

    Проверка из 40 точек по России (Globalping): сайт открылся в 39.

  10. 10Эта страница

    Демо-вход в один клик

    Кнопка «Открыть демо» создаёт временный аккаунт с готовым сервером, ролями, друзьями и перепиской — она хранится зашифрованной, как настоящая.

    Гостю закрыто всё, через что можно написать реальным людям, у него своя квота на файлы, в статистику он не попадает, а через сутки его удаляет крон вместе с сервером и чатами.

    Можно пощупать продукт без регистрации — и никому не помешать.

03 · качество

Как держим порядок

04 · стек

На чём написано

Фронтенд
Next.js 16React 19TypeScriptRedux Toolkit + RTK QueryTailwind CSS 4livekit-client
Бэкенд
PHP 8.4Laravel 12SanctumReverbMySQLRedis
Медиа
WebRTCLiveKit SFUcoturnDeepFilterNet · WASMRNNoise
Десктоп
Electron 37electron-builderelectron-updater
Инфраструктура
nginx · HTTP/2systemdпланировщик LaravelGitHub Actions

Лучше один раз потрогать

Демо открывается за пару секунд: свой сервер, друзья и переписка уже на месте. Через сутки всё удалится само.

Завести аккаунт