Волна — мессенджер: веб + десктоп
  • JavaScript 86.6%
  • CSS 8%
  • HTML 5.3%
  • NSIS 0.1%
Find a file
2026-06-11 10:25:10 +03:00
build [CL-VOLNA] Волна 3.10.7: мессенджер (сервер+web+desktop), health endpoint, TRUST_PROXY, NSIS ACL hook 2026-06-11 09:56:48 +03:00
electron [CL-VOLNA] Волна 3.10.7: мессенджер (сервер+web+desktop), health endpoint, TRUST_PROXY, NSIS ACL hook 2026-06-11 09:56:48 +03:00
public [CL-VOLNA] Волна 3.10.7: мессенджер (сервер+web+desktop), health endpoint, TRUST_PROXY, NSIS ACL hook 2026-06-11 09:56:48 +03:00
site [CL-VOLNA] /health endpoint, TRUST_PROXY за reverse-proxy, лендинг volna.adami.kr 2026-06-11 10:21:17 +03:00
tests [CL-VOLNA] prod-smoke тест (wss, 2 юзера, доставка, прочтение, health) 2026-06-11 10:22:44 +03:00
.gitignore [CL-VOLNA] gitignore: служебный .claude-agent-files 2026-06-11 10:25:10 +03:00
nethost.js [CL-VOLNA] Волна 3.10.7: мессенджер (сервер+web+desktop), health endpoint, TRUST_PROXY, NSIS ACL hook 2026-06-11 09:56:48 +03:00
package-lock.json [CL-VOLNA] Волна 3.10.7: мессенджер (сервер+web+desktop), health endpoint, TRUST_PROXY, NSIS ACL hook 2026-06-11 09:56:48 +03:00
package.json [CL-VOLNA] Волна 3.10.7: мессенджер (сервер+web+desktop), health endpoint, TRUST_PROXY, NSIS ACL hook 2026-06-11 09:56:48 +03:00
README.md [CL-VOLNA] Волна 3.10.7: мессенджер (сервер+web+desktop), health endpoint, TRUST_PROXY, NSIS ACL hook 2026-06-11 09:56:48 +03:00
ROADMAP.md [CL-VOLNA] Волна 3.10.7: мессенджер (сервер+web+desktop), health endpoint, TRUST_PROXY, NSIS ACL hook 2026-06-11 09:56:48 +03:00
SECURITY.md [CL-VOLNA] Волна 3.10.7: мессенджер (сервер+web+desktop), health endpoint, TRUST_PROXY, NSIS ACL hook 2026-06-11 09:56:48 +03:00
server.js [CL-VOLNA] Волна 3.10.7: мессенджер (сервер+web+desktop), health endpoint, TRUST_PROXY, NSIS ACL hook 2026-06-11 09:56:48 +03:00

«Волна» 3.10 — мессенджер (веб + десктоп)

Самостоятельный мессенджер с привычным интерфейсом в духе Telegram. Сервер — Node.js (WebSocket), клиент — чистый HTML/CSS/JS, десктоп — Electron. Интерфейс вдохновлён Telegram; проект не аффилирован с Telegram FZ-LLC. Перед публичным запуском прочтите SECURITY.md и проверьте свободность имени бренда.

Функции

  • Регистрация и вход, сессии, работа в нескольких окнах
  • Личные чаты, группы, каналы, «Избранное»
  • Секретные чаты — сквозное шифрование (ECDH P-256 + AES-GCM 256, Web Crypto); сервер хранит только шифртекст и публичные ключи
  • Аудио- и видеозвонки 1:1 (WebRTC, сигналинг через сервер, STUN)
  • Голосовые сообщения (запись с микрофона, плеер с прогрессом)
  • Стикеры — собственный SVG-пак (17 шт.)
  • Истории — фото + подпись, живут 24 часа, счётчик просмотров
  • Системные уведомления о новых сообщениях и звонках
  • Фото-аватары пользователей, групп и каналов
  • Форматирование текста: жирный, курсив, код, блок, зачёркнутый, ||спойлер||
  • Видео в чате, мьют, архив, закрепление чатов, черновики
  • Управление группой: админы, удаление участников, очистка истории, удаление чата
  • Опросы (анонимные, с несколькими ответами), @упоминания с автодополнением
  • Глобальный поиск по сообщениям, множественный выбор (переслать/удалить)
  • Оформление: 5 акцентных цветов, фоны чата; скорость голосовых 1×/1.5×/2×
  • Папки чатов (Все/Личные/Группы/Каналы/Непрочитанные), контакты
  • Видеосообщения-кружки, геопозиция, «записывает голосовое…»
  • Отложенная отправка (ПКМ по кнопке отправки), удалить у себя/у всех
  • Десктоп: сворачивание в трей
  • Веб-превью ссылок (OpenGraph), большие эмодзи, просмотры постов 👁 в каналах
  • Свои стикеры (загрузка в панель), настройка TURN для звонков
  • Групповые голосовые чаты (mesh WebRTC, до 8 участников)
  • Истории с видео, список зрителей своей истории
  • Анимированные стикеры, блокировка пользователей, разделитель «Непрочитанные»
  • Реакция любым эмодзи («+» в меню), автоудаление сообщений (24ч/7д)
  • Экспорт чата в HTML, статистика канала, градиентные стили пузырей
  • Bot API: создание ботов (меню → Боты), HTTP-методы getMe/getUpdates/sendMessage
  • Ссылки-приглашения в группы (код вводится в поиске), медиа-галерея чата
  • Недавние эмодзи, «N в сети» в группах
  • Несколько закреплённых сообщений с навигацией, устройства/сессии с завершением
  • 2FA (TOTP) — профиль → «Включить 2FA», совместимо с любым аутентификатором
  • Дисковые квоты (QUOTA_MB, по умолчанию 512 МБ/пользователь), журнал безопасности
  • Смена пароля, звук уведомлений, drag&drop-оверлей, Ctrl+K — поиск
  • @-бейдж упоминаний в списке, «отметить прочитанным/непрочитанным»
  • Память прокрутки чатов, системная тема (авто), Esc закрывает чат, список закрепов
  • Команды ботов с подсказками по «/», превью фото с подписью, прогресс загрузки
  • ПКМ по реакции — кто поставил
  • Сообщения в реальном времени, «в сети / был(а)», «печатает…»
  • Галочки прочтения ✓/✓✓, счётчики непрочитанных
  • Ответы, редактирование, удаление, пересылка, закрепление, реакции
  • Файлы и фото (до 25 МБ), просмотр изображений
  • Поиск по чатам, пользователям, каналам и сообщениям
  • Пересылка геопозиции сохраняет координаты (фикс 3.3.1)
  • Отложенная отправка уважает блокировку и права канала на момент доставки (фикс 3.3.2)
  • Пересылка уважает блокировку получателя; «удалить у всех» вычищает гео/превью из хранилища (фикс 3.9.1)
  • Сборщик файлов: «удалить у всех», TTL, очистка истории, истёкшие истории и смена аватара стирают файлы и с диска (URL перестаёт открываться), а место возвращается в дисковую квоту (фикс 3.9.2)
  • Блокировка действует везде: секретный чат не создать, в группу не добавить, истории скрыты в обе стороны, «печатает…» не утекает (фикс 3.9.3)
  • Bot API без утечек: replyTo принимается только из того же чата, личка с ботом уважает блокировку (фикс 3.9.4)
  • Исключение/выход из группы завершает и голосовой чат: участник пропадает из звонка у всех (mesh-связь рвётся), слот из 8 освобождается, клиент кладёт трубку при удалении чата (фикс 3.9.5)
  • Очистка истории очищает чат полностью: вместе с сообщениями снимаются @-бейдж упоминания и пометка «непрочитанным» — на пустом чате они больше не «висят» (фикс 3.9.6)
  • Блокировка скрывает присутствие: заблокированный не видит «в сети / был(а)» — в чате, поиске, профиле, userInfo и presence-пушах (фикс 3.9.7)
  • Блокировку не обойти через старые сообщения: редактирование, реакции, голос в опросе и закрепление в личных/секретных чатах отклоняются, пока действует блокировка (фикс 3.9.8)
  • Загруженные файлы не исполняют код: SVG/HTML из /files/ отдаются с жёстким Content-Security-Policy: sandbox — открытый напрямую файл не выполнит скрипт в origin (защита от кражи токена и ключей из localStorage); показ картинок/видео в чате не затронут (фикс 3.9.9)
  • Удаление сообщения снимает @-бейдж: «удалить у всех», «удалить у себя» и автоудаление по таймеру сбрасывают «фантомное» упоминание, которое больше нечем прочитать (фикс 3.9.10)
  • Удаление бота прибирает за собой: личные чаты с ботом удаляются целиком — раньше «осколок» чата отображался как второе «Избранное»; сообщения чатов без участников вычищаются (файлы вернёт сборщик), группы сразу получают обновлённый состав (фикс 3.9.11)
  • Выход/исключение из группы сбрасывает личные настройки чата: мьют, архив, закрепление вверху и пометка «непрочитанным» больше не «воскресают» при повторном добавлении — чат возвращается в обычном виде (фикс 3.9.12)
  • Точные @упоминания: бейдж и пробитие мьюта — только при точном совпадении логина: «@anna» больше не дёргает пользователя @ann, email вида ivan@ann.ru не считается упоминанием (фикс 3.9.13)
  • Заблокированный не создаёт личный чат: пустой диалог больше не «всплывает» в списке чатов у заблокировавшего; существующая переписка открывается как прежде (фикс 3.9.14)
  • Правка сообщения обновляет @-бейдж: упоминание, убранное при редактировании, снимает бейдж (не «висит фантомом»), добавленное правкой — вешает; уже прочитанные упоминания правкой не «воскресают» (фикс 3.9.15)
  • Просмотры каналов не накручиваются: «прочитать» дальше последнего сообщения чата нельзя — дутый msgId в read навсегда завышал 👁 у всех будущих постов и рисовал ✓✓ недоставленным сообщениям (фикс 3.9.16)
  • Просмотры историй не накручиваются: просмотр засчитывается только при общем чате — незнакомец перебором id не накрутит счётчик 👁 и не появится в списке зрителей истории (фикс 3.9.17)
  • Выход из аккаунта останавливает уведомления: logout и завершение сессии (в т.ч. удалённое — «Устройства») отвязывают Web Push этой сессии: разлогиненное устройство больше не получает тексты сообщений (фикс 3.9.18)
  • Смена пароля завершает остальные сессии: украденный или оставленный на чужом устройстве вход перестаёт действовать сразу после смены пароля (как «Завершить все другие сессии»); текущая сессия остаётся (фикс 3.9.19)
  • Код 2FA — одноразовый: повторный вход тем же кодом в 90-секундном окне отклоняется (RFC 6238) — подсмотренный или перехваченный фишингом код нельзя использовать второй раз; код следующего окна работает (фикс 3.9.20)
  • Исключённый не возвращается сам: kick закрывает вход по ссылке-приглашению и повторную самоподписку на канал; добавление админом возвращает (снимает бан), добровольный выход по коду вернуться не мешает (фикс 3.9.21)
  • Бан не снимается «сообщником»: вернуть исключённого (= снять бан) может только админ — addMembers рядового участника молча пропускает забаненного; участников в канал добавляют только админы; приглашение не-забаненных участниками группы работает как прежде (фикс 3.9.22)
  • Аудитория канала не собирается по API: список подписчиков канала в chatInfo отдаётся только владельцу/админам — подписчику остаётся счётчик, как в Telegram; в группах список виден всем (фикс 3.9.23)
  • Пароль и 2FA не подбираются из украденной сессии: смена пароля и отключение 2FA — не больше 5 попыток в минуту (login был защищён лимитом и блокировкой, внутренние вызовы — нет), неудачи видны в журнале (фикс 3.9.24)
  • «Печатает…» не для троллинга: в каналах индикатор транслируют только те, кто может публиковать (у подписчика и композера-то нет), и не чаще 20 раз за 10 с на пользователя — раньше фан-аут на всю аудиторию шёл вообще без лимита (фикс 3.9.25)
  • Реакции в канале не раскрывают аудиторию: рядовому подписчику приходят только счётчики реакций и метка собственной — id реагировавших больше не утекают всем, аудиторию канала не перебрать в обход 3.9.23; владелец и админы видят, кто поставил (фикс 3.9.26)
  • Общий канал не делает «знакомыми»: подписка на канал больше не открывает незнакомцам истории друг друга (и зачёт просмотров 👁), звонки и presence-пуши — родство дают только личка, группа и секретный чат, аудитория канала остаётся скрытой (фикс 3.9.27)
  • Прочтения канала не раскрывают подписчиков: пуш «read» с id читателя больше не рассылается всей аудитории при прочтении поста (галочек ✓✓ в каналах нет, 👁 считается на сервере) — пассивный сбор аудитории закрыт, фан-аут ×N на каждое прочтение убран (фикс 3.9.28)
  • Выход владельца не дарит чат кому попало: владельцем становится админ, а не «первый в списке»; бот — никогда (группа с ботом-владельцем зависала навсегда: его не кикнуть, админов не назначить, чат не удалить); группа из одних ботов удаляется целиком (фикс 3.9.29)
  • Отложенным сообщениям — лимиты: очередь — до 100 на пользователя (раньше росла без предела), создание — не чаще 20/10 с; отложка больше не обходит флуд-лимит отправки доставкой пачки разом (фикс 3.9.30)
  • Реакциям и голосам — лимит частоты: toggle-петля реакции или голоса (доступна и подписчику канала) множила пуши на всю аудиторию и дисковые записи вообще без ограничений — теперь не чаще 30 за 10 с (фикс 3.9.31)
  • Пересылке — предел пачки и частоты: api.forward создавал сообщения с фан-аутом вообще без лимитов, дубли id в пачке не схлопывались (один вызов множил сообщение ×N в обход send-лимита) — теперь не больше 100 уникальных за вызов и не чаще 100 пересланных за 10 с (фикс 3.9.32)
  • ✓✓ не выдают активность при блокировке: пуш о прочтении и readsMax в личке/секретном чате показывали заблокированному, что блокировавший читает сообщения прямо сейчас («в сети» скрыто ещё в 3.9.7) — указатель прочтений скрыт на время блокировки, после снятия ✓✓ догоняют (фикс 3.9.33)
  • Посты канала не подписаны автором: подписчику история, поиск, список чатов, «Переслано от …» и web push показывают канал, а не имя/аватар владельца или админа; «печатает…» в каналах не транслируется; владелец и админы видят авторов как прежде (фикс 3.9.34)
  • Правке сообщений — лимит частоты: edit-петля одного сообщения слала msgEdit-фан-аут на всю аудиторию, save() и повторную загрузку веб-превью (исходящий HTTP) на каждую правку вообще без ограничений — теперь не чаще 30 за 10 с, как реакции и голоса (фикс 3.9.35)
  • Блокировку не обойти управлением чатом: заблокированному в личке недоступны автоудаление (TTL стирал историю блокировавшего по таймеру, сервисное сообщение пушилось в обход блокировки), очистка истории и удаление чата; сам блокировавший управляет чатом как прежде (фикс 3.9.36)
  • Созданию чатов — лимит частоты: петля создания групп/каналов засоряла хранилище навсегда (чаты не истекают), force-добавление «подсаживало» жертв в спам-группы, новые личные/секретные чаты пушились собеседнику без ограничений — теперь не больше 15 новых чатов за 10 с; открытие существующего чата лимит не тратит (фикс 3.9.37)
  • Push при закрытой вкладке — Web Push (Service Worker + VAPID, RFC 8291) без сторонних сервисов; тумблер в «Оформлении», только офлайн-получателям, с учётом мьюта и @упоминаний (3.4.0)
  • Групповые видеозвонки — камера в голосовом чате группы: кнопка 📷 в баре, плитки видео над перепиской, индикатор «📷у участников (3.5.0)
  • Перенос секретных чатов на другое устройство: экспорт/импорт ключей файлом, зашифрованным парольной фразой (PBKDF2 + AES-GCM) — в профиле (3.6.0)
  • Стикер-паки: объединяйте свои стикеры в наборы (📦 в панели стикеров) и делитесь 10-значным кодом — друг вводит его в поиске и устанавливает (3.7.0)
  • Фильтры глобального поиска — от кого (@логин), диапазон дат, «только медиа»; панель под строкой поиска, работает и без текста запроса (3.8.0)
  • Свой порядок папок чатов — перетащите вкладку (Все/Личные/…) мышью, порядок хранится на устройстве (3.9.0)
  • Эмодзи-проверка звонков — при соединении оба собеседника видят 4 эмодзи, выведенных из DTLS-отпечатков сторон: совпадают — посредника на сигналинге нет, сверьте вслух (3.10.0)
  • Профилю, аватарам и историям — лимит частоты: правка профиля и смена аватара (фан-аут на всех знакомых), публикация историй — не чаще 10/10 с, активных историй — не больше 30: db.stories рос без предела, петля публикаций множила пуши (фикс 3.10.1)
  • «Удалить у всех» не обходит блокировку: заблокированный больше не стирает сообщения из лички/секретного чата блокировавшего (там удалять «у всех» можно и чужие) и не шлёт ему msgDel-пуши; блокировавший удаляет как прежде (фикс 3.10.2)
  • Реакциям — потолок на сообщении: не больше 3 реакций одного пользователя на сообщении, новая вытесняет старейшую (как в Telegram) — петля «новый эмодзи на каждый вызов» в лимит 3.9.31 вешала на сообщение тысячи реакций навсегда, раздувая хранилище и пуши (фикс 3.10.3)
  • 2FA-статус не светится наружу: включён ли у аккаунта второй фактор, видит только сам владелец — userInfo, поиск и списки участников больше не отдают готовый перечень аккаунтов без 2FA для подбора пароля (фикс 3.10.4)
  • SSRF-фильтр покрывает IPv6: предпросмотр ссылок и Web Push не сходят на внутренние адреса — фильтр приватных хостов не снимал скобки IPv6 hostname, поэтому ВЕСЬ IPv6 проходил: [::1], IPv4-mapped [::ffff:169.254.169.254] (метаданные облака), ULA fc00::/7 и link-local fe80::/10 дотягивались до внутренних сервисов; теперь блокируются, публичный IPv6 разрешён (фикс 3.10.5)
  • Членству в чатах — лимит частоты и предел пачки: вступление/выход/ добавление участников — не чаще 20 изменений за 10 с (петля join/leave фан-аутила пуши на всю аудиторию и навсегда растила хранилище сервисными сообщениями), addMembers — не больше 50 уникальных id за вызов; повторный join участника и no-op-добавления бюджет не тратят (фикс 3.10.6)
  • Закреплению — лимит частоты: петля pin/unpin (в личке доступна и не-админу) множила notifyChat-фан-аут — chatView с pinViews (до 20 msgView) под каждого участника — и save() на каждый toggle — теперь не чаще 30 за 10 с, как реакции, голоса и правки (фикс 3.10.7)
  • Профиль (имя, о себе, цвет аватара), светлая/тёмная темы, мобильная вёрстка

Запуск (веб-режим)

Нужен Node.js 18+ (https://nodejs.org).

cd telegram-clone
npm install
npm start

Откройте http://localhost:8080. Второй пользователь — окно в инкогнито или другой браузер.

Десктоп-приложение

Запуск без установки:

npm install
npm run app

Сборка установщика .exe (Windows)

npm run dist

Готовый установщик появится в dist/Volna-Setup-<версия>.exe (установка с выбором папки, ярлыки на рабочем столе и в меню «Пуск»). Первая сборка скачивает Electron (~100 МБ) — нужен интернет и несколько минут.

Десктоп-приложение запускает встроенный сервер автоматически; данные хранятся в %APPDATA%/tg-clone. Чтобы переписываться с другими компьютерами, запустите сервер (npm start) на одной машине и откройте её адрес в браузере с других (http://IP-адрес:8080).

Тест

При запущенном сервере: npm test — интеграционный сценарий (регистрация, доставка, прочтение, группы, каналы, реакции, пересылка, секретные чаты, сигналинг звонков, истории, стикеры, голосовые, криптография E2E).

Структура

server.js          — сервер: HTTP + WebSocket, вся логика
electron/main.js   — десктоп-обёртка (встроенный сервер + окно)
public/
  index.html       — разметка клиента
  style.css        — стили (светлая/тёмная темы)
  app.js           — логика клиента (чаты, звонки, E2E, истории)
  stickers/        — SVG-стикеры
build/icon.ico     — иконка приложения
tests/smoke.js     — интеграционный тест
volna.db, files/   — данные (SQLite WAL; создаются автоматически)

Развёртывание (продакшен)

Минимальный безопасный контур: сервер за обратным прокси с TLS.

  1. Node 18+ на сервере, npm install --omit=dev, запуск через systemd:
[Unit]
Description=Volna messenger
After=network.target

[Service]
WorkingDirectory=/opt/volna
ExecStart=/usr/bin/node server.js
Environment=PORT=8080
Environment=DATA_DIR=/var/lib/volna
Restart=always
User=volna

[Install]
WantedBy=multi-user.target
  1. TLS — проще всего через Caddy (автоматический Let's Encrypt):
chat.example.com {
    reverse_proxy 127.0.0.1:8080
}

Либо без прокси: задайте TLS_CERT=/путь/fullchain.pem и TLS_KEY=/путь/privkey.pem — сервер сам поднимет HTTPS/WSS.

  1. Звонки через интернет: установите coturn (apt install coturn), включите lt-cred-mech, создайте пользователя и укажите turn:ваш-домен:3478 + логин/пароль в приложении: Оформление → TURN-сервер.

  2. Бэкапы: каталог DATA_DIR (volna.db, volna.db.bak, files/) — это все данные. Сервер сам делает почасовой бэкап volna.db.bak; настройте внешнее копирование каталога.

Хранилище

Основное хранилище — SQLite (volna.db, WAL): на диск пишутся только изменившиеся записи, при завершении процесса буфер сбрасывается. Данные из старого data.json мигрируются автоматически при первом запуске (оригинал остаётся как data.json.migrated). better-sqlite3 стоит в optionalDependencies: если нативный модуль не собрался, сервер прозрачно работает на прежнем data.json (видно в логе запуска). Десктоп: electron-builder пересобирает нативный модуль автоматически; без тулчейна приложение использует JSON-фолбэк. Проверка персистентности: node tests/persist.js 1 → перезапуск сервера → node tests/persist.js 2.

  1. Перед запуском прочтите SECURITY.md и пройдите чеклист.

Переменная RELAX_RATELIMIT=1 отключает лимиты (только для отладки!).

Bot API

Создайте бота в меню «Боты» и добавьте его в группу как участника. Методы:

curl http://localhost:8080/bot/<ТОКЕН>/getMe
curl http://localhost:8080/bot/<ТОКЕН>/getUpdates
curl -X POST http://localhost:8080/bot/<ТОКЕН>/sendMessage \
  -H "Content-Type: application/json" \
  -d '{"chatId": 123, "text": "Привет из бота!"}'

getUpdates возвращает накопленные входящие сообщения (очередь до 100) и очищает её. Команды бота (подсказки при вводе «/» в чате):

curl -X POST http://localhost:8080/bot/<ТОКЕН>/setMyCommands \
  -H "Content-Type: application/json" \
  -d '{"commands": [{"command": "start", "description": "Запустить бота"}]}'

Как устроены секретные чаты

При открытии секретного чата каждое устройство генерирует пару ключей ECDH P-256; публичные части обмениваются через сервер, из них выводится общий ключ AES-GCM 256. Шифрование и расшифровка — только на клиенте. Следствия, как и в настоящих секретных чатах: история привязана к устройству, доступен только текст. Перенести историю на новое устройство можно экспортом ключей с парольной фразой: профиль → «Экспорт ключей», на новом устройстве — «Импорт». Примечание: это учебная реализация без верификации ключей (отпечатков) и защиты от MITM со стороны сервера.

Ограничения и идеи для развития

Групповые звонки — mesh (каждый с каждым), до 8 человек; в сетях со строгим NAT может понадобиться TURN-сервер (сейчас только STUN). Web Push работает по HTTPS (или на localhost) в браузерах с Push API; в Electron-обёртке уведомления показывает сам клиент. Секретные чаты — без верификации ключей (отпечатков) и защиты от MITM со стороны сервера. Сервер и хранилище — один узел (SQLite/JSON); кластеризация и внешняя БД (Postgres) сознательно не входят в малые итерации. Идеи дальше: треды-комментарии в каналах, импорт/экспорт всего аккаунта, федерация между серверами.