Self-hosted минималистичный мессенджер с голосовыми/видео звонками и демонстрацией экрана (или отдельного окна/приложения).
- Регистрация/логин по имени пользователя и паролю
- Единый список всех пользователей — без друзей, звонить и писать можно кому угодно
- Direct-сообщения с историей, markdown, редактированием/удалением и системными сообщениями о звонках
- Групповые чаты (до 10 участников) с названием, аватаркой, списком участников, сообщениями/файлами/голосовыми
- Групповые звонки (голос + опционально видео) — mesh WebRTC, до 10 участников, сетка плиток
- Файлы и медиа: фото, видео, любые документы (до 50 МБ), голосовые сообщения
- WebRTC звонки 1:1: микрофон, камера (опционально), шаринг экрана/окна/приложения
- Окно реконнекта 5 мин: если связь 1:1 оборвалась, кнопка «Подключиться» позволяет вернуться без нового вызова
- Регистрация по приглашению: серверный
REGISTRATION_CODE, одноразовые/multi-use коды через UI, или полное закрытие регистрации - Push-уведомления: системные через Web Push — звонки и сообщения долетают и при закрытой вкладке
- Профиль: ник, аватарка, политика удаления собственных сообщений, удаление аккаунта (история сохраняется как «Удалённый пользователь»)
- Авто-чистка истории: сообщения и вложения старше
RETENTION_DAYS(по умолчанию 90) автоматически удаляются из БД и с диска - Мьюты: серверные, синхронизируются между устройствами
- Гранулярные звуковые уведомления + общий тумблер
- Кроссплатформенно: работает в любом современном браузере (ПК и телефон), ставится как PWA
- Backend: Node.js + Express + Socket.IO + SQLite (
better-sqlite3) + JWT/bcrypt - Frontend: React + Vite + TailwindCSS + Lucide icons
- P2P медиа: WebRTC (STUN — Google public). Для жёстких NAT можно подключить TURN (см.
.env).
# из корня проекта
npm run install:all
# dev-режим (сервер :3001, клиент :5173, клиент проксирует API на сервер)
npm run devОткрой http://localhost:5173, зарегистрируй двух пользователей в двух вкладках/устройствах и звони.
Самый быстрый путь — на одном сервере:
npm run build # собирает клиент в client/dist
npm start # сервер раздаёт dist + API на одном порту (по умолчанию 3001)Для нормального деплоя с доменом, HTTPS и автозапуском есть отдельный
пошаговый гайд: deploy/DEPLOY.md.
В папке deploy/ лежат готовые шаблоны:
install.sh— устанавливает Node, ставит зависимости, собирает фронт, заводит системного пользователяowncord, кладёт код в/opt/owncord, включаетsystemd-сервис.nginx.conf.example— reverse-proxy с поддержкой WebSocket и большими загрузками; стыкуется с Let's Encrypt через certbot.owncord.service— unit-файлsystemdдля автозапуска и логов черезjournalctl.
Для маленькой группы пользователей хватает: VPS → bash install.sh →
nginx + certbot → выставить REGISTRATION_CODE в .env → раздать код
доверенным людям.
Полный пример с комментариями: server/.env.example.
| Переменная | Назначение |
|---|---|
PORT |
HTTP-порт сервера (по умолчанию 3001). |
NODE_ENV |
production включает строгий CSP/CORS и форсит проверку JWT_SECRET. |
JWT_SECRET |
Секрет для JWT. Обязательно замени на длинную случайную строку (≥16 символов). В production сервер не стартует без неё. |
JWT_TTL |
Срок жизни токена. По умолчанию 14d. |
APP_ORIGIN |
CSV доменов фронтенда для CORS. В production без неё все кросс-доменные запросы блокируются (same-origin продолжает работать). |
REGISTRATION_CODE |
Если задан — без этого кода регистрация невозможна. |
REGISTRATION_DISABLED |
1/true — полностью закрыть регистрацию (логин остаётся). |
ADMIN_USERNAMES |
CSV username'ов админов. По умолчанию админ — пользователь id=1. |
MAX_UPLOAD_MB |
Максимальный размер одного файла, по умолчанию 500. |
RETENTION_DAYS |
Сколько дней хранить сообщения/вложения. По умолчанию 90 (раз в час чистится). |
VAPID_PUBLIC_KEY / VAPID_PRIVATE_KEY / VAPID_SUBJECT |
Web Push (опц.; иначе автогенерация). |
TURN_URL / TURN_USERNAME / TURN_PASSWORD |
Свой TURN (для строгого NAT). |
- Браузер сам предлагает выбрать: весь экран / окно / вкладка. Это эквивалент "конкретного приложения" в Discord.
- На iOS Safari шаринг экрана через
getDisplayMediaне поддерживается — на iPhone можно только принимать демонстрацию и участвовать в звонке. На Android Chrome и всех десктопах работает полноценно.
Опциональная нативная обёртка над веб-клиентом — нужна, если хочется глобальные хоткеи (мьют микро/наушников, работают даже когда окно не в фокусе) и автостарт. Все данные (сообщения, звонки, контакты) по-прежнему живут на сервере — десктоп ходит к нему по тем же URL'ам, что и браузер, и не дублирует ничего локально.
# Запуск из исходников (поднимет Electron, грузящий http://localhost:5173)
npm run dev # стартует сервер + dev-сервер клиента
npm run desktop:dev # в отдельном терминале — Electron-окно
# Сборка релиза под текущую ОС
npm run desktop:build
# Артефакты: desktop/dist/<platform>/...При первом запуске десктоп спросит URL сервера. Конфиг сохраняется в
%APPDATA%/OwnCord/owncord.config.json (на Linux/macOS — аналогичная
папка профиля). Подробности и пер-OS сборка — в desktop/README.md.
Биндинги: Настройки → вкладка «Биндинги» (видна только в десктопе).
Сейчас доступны два действия: «Мьют микрофона» и «Глушить динамики».
Хоткей назначается записью комбинации (Ctrl/Shift/Alt + клавиша),
формат — accelerator Electron'а (https://electronjs.org/docs/latest/api/accelerator).
Backend (vitest + supertest) и frontend (vitest + RTL) покрывают auth, мьюты, сообщения,
конечный автомат звонков (callRegistry), markdown-рендерер и ключевые компоненты.
# из корня проекта
npm test --workspace server # node-тесты, изолированная sqlite
npm test --workspace client # jsdom-тесты компонентов- В сайдбаре есть отдельная секция Группы с кнопкой + для создания.
- Создатель автоматически становится owner'ом; owner может переименовать группу, загрузить/удалить аватар, добавлять и удалять участников, а также удалить группу для всех. Обычный участник может только выйти.
- Правой кнопкой на группе в сайдбаре — меню: «Редактировать», «Замутить», «Выйти»/«Удалить».
- В групповом чате поддерживаются текст, редактирование/удаление, голосовые, файлы и изображения (всё как в DM).
- Групповой звонок: кнопки 📞/🎥 в заголовке чата запускают сессию.
Все остальные участники группы получают системное сообщение «X начал(а)
звонок» с кнопкой «Подключиться»; над чатом параллельно показывается
баннер «В этой группе сейчас идёт звонок · Подключиться», поэтому
присоединиться можно в любой момент, пока сессия живая. В самом окне
звонка доступны микрофон, камера и демонстрация экрана. Mesh-модель:
каждый участник держит RTCPeerConnection с каждым, поэтому практический
потолок — 8–10 человек; для больших встреч потребуется SFU (например
mediasoup). - Все события групп (создание/правка/удаление/входящие сообщения/звонок) приходят через сокет в реальном времени — UI обновляется без перезагрузки.
Основа специально минимальная и расширяемая:
- SFU для групповых встреч 10+ человек (например
mediasoup) - Роли/модерация внутри группы
- E2E-шифрование сообщений
- Мобильные клиенты-обёртки (Capacitor/Tauri Mobile)
MIT © 2026 Restez
Исходный код и все производные работы распространяются свободно. Если
разворачиваешь у себя — достаточно сохранить файл LICENSE с
авторскими. Багрепорты и PR — в GitHub репозиторий.