README_RU.md
August 11, 2026 · View on GitHub
🌐 Другие языки: 中文 · English · 日本語 · 한국어 · Français · Deutsch · Español
Мессенджер со сквозным шифрованием в стиле WeChat. Бессостоянный ECDH + XSalsa20-Poly1305 шифрование для каждого сообщения, видеозвонки в реальном времени, хранение файлов Cloudflare R2, поддержка нескольких языков и развёртывание iOS PWA.
📸 Скриншоты (нажмите для просмотра)
Функции
| Функция | Описание |
|---|---|
| 🔐 Сквозное шифрование | Бессостоянный ECDH + XSalsa20-Poly1305 — эфемерные ключи для каждого сообщения, прямая секретность, верификация номера безопасности в стиле Signal |
| 🗝️ Сервер с нулевым знанием | Сервер хранит только зашифрованный текст; приватные ключи никогда не покидают устройство |
| 📹 Видео- и аудиозвонки | LiveKit SFU для звонков 1:1 и конференций (до 100 участников), отключение микрофонов и режим лекции |
| 🎙️ Изменение голоса | Эффекты голоса в реальном времени для голосовых сообщений, звонков 1:1 и групповых звонков — 3 режима (0.8x низкий / 1.0x обычный / 1.2x высокий), на основе Web Audio API |
| 📱 Сохранение сеанса | Access Token на 30 минут и автоматически продлеваемая 90-дневная refresh-сессия устройства; восстановление после смены сети/IP/VPN/прокси без ввода пароля |
| 📨 Надёжная синхронизация сообщений | Двусторонний heartbeat, обнаружение полуоткрытых соединений, постоянная очередь исходящих, идемпотентные ID и догрузка по курсору серверной последовательности |
| 📴 Офлайн-доступ | Изолированный по аккаунтам кэш контактов, групп, до 2 000 сообщений на диалог, Моментов, Ленты и медиа; офлайн-отправления повторяются автоматически |
| 🔎 Unicode-поиск друзей | Защита композиции IME, нормализация NFC и кодирование UTF-8 обеспечивают надёжный поиск китайских имён пользователей и никнеймов |
| 👥 Групповой чат | До 2000 участников, переключаемые режимы «Зашифрованный» / «Незашифрованный» (только владелец, при переключении история чата очищается). Зашифрованный режим использует протокол Sender Key в стиле Signal (симметричное шифрование XSalsa20-Poly1305 + распределение ключей ECDH) — только участники группы могут расшифровать сообщения; боты недоступны в зашифрованном режиме. Режим «Не беспокоить», управление участниками |
| 👫 Система друзей | Заявки в друзья требуют одобрения с сообщением до 512 символов; пользовательские никнеймы; группировка по нескольким тегам |
| ⏱️ Автоудаление сообщений | 5 уровней (никогда / 1 день / 3 дня / 1 неделя / 1 месяц), настраивается любой стороной в личных сообщениях, только владельцем в группах |
| 🔔 Push-уведомления | Web Push (VAPID) + FCM + OneSignal + ntfy + APNS пятиканальная доставка — доступность пользователей даже в офлайне (поддержка нативного iOS + китайских Android без Google Services) |
| 🌐 Мультиязычность | Китайский, английский, японский, корейский, французский, немецкий, русский, испанский — автоопределение + ручное переключение |
| 📱 iOS — без корпоративного сертификата | PWA через Safari «На экран Домой», работает бессрочно без подписи Apple |
| 📱 Нативное Android-приложение | Доступно в Google Play, с поддержкой FCM push-уведомлений |
| 📱 Нативное iOS-приложение | Доступно в App Store, с поддержкой APNS push-уведомлений |
| 🖥️ Десктопный клиент Windows | Нативное настольное приложение для Windows, скачать здесь |
| 🍎 Десктопный клиент Mac | Нативное настольное приложение для Mac, скачать здесь |
| 💬 Расширенные сообщения | Текст, изображения, видео, документы, голосовые сообщения, 200+ эмодзи, стикер-паки Telegram, отчёты о доставке, индикаторы набора текста |
| 📤 Загрузка файлов | До 500 МБ на файл, Cloudflare R2 или локальное хранилище, с анимацией прогресса |
| 🌐 Моменты | Социальная лента в стиле WeChat: текст + до 9 фото или 1 видео (≤ 10 мин), лайки, комментарии, видимость по тегам |
| 👤 Профиль пользователя | Страница контактного профиля с двусторонним управлением приватностью Моментов |
| 📰 Лента | Публичная лента в стиле Xiaohongshu — двухколоночная кладка, анонимные публикации, лайки и комментарии |
| 🏷️ Теги друзей | Назначение нескольких тегов друзьям (палитра из 12 цветов), фильтрация контактов по тегу |
| 🗂️ Объектное хранилище R2 | Cloudflare R2 для изображений/голосовых файлов — опциональный публичный CDN URL |
| 🔑 Двухфакторная аутентификация (2FA) | TOTP совместимый с Google Authenticator, 8 кодов восстановления, проверка при входе |
| 📷 Сканирование и обмен QR-кодами | Сканируйте QR-коды для добавления друзей или вступления в группы с настраиваемым сроком действия |
| 🏗️ Самостоятельный хостинг | Docker Compose, Zeabur в один клик или фронтенд на Vercel |
| 🌐 Настройки прокси | Поддержка SOCKS5 / HTTP / HTTPS прокси — настройка на страницах входа и настроек с указанием адреса сервера, порта, имени пользователя и пароля для ограниченных сетевых сред |
| 🛡️ Модерация контента | Жалобы пользователей (6 категорий) + блокировка пользователей (мгновенное скрытие постов/сообщений) + Условия использования (EULA) |
| 🔧 Панель администратора | Встроенная веб-панель администратора (/admin, путь настраивается), защита паролем, проверка жалоб, удаление нарушающего контента, бан пользователей — поддержка 8 языков |
Новое в v2.3.9
- Исправлены старые односторонние записи дружбы, из-за которых отображалось «Уже в друзьях», но контакт оставался невидимым и недоступным для чата; повторное добавление теперь восстанавливает обе стороны связи и сразу обновляет список контактов.
Новое в v2.3.8
- Исправлена неработающая кнопка «Назад» после запуска камеры QR-сканера; при закрытии камера теперь сразу останавливается и освобождается.
- Исправлено повреждение связи при повторной заявке уже добавленному другу; результаты поиска теперь показывают «Уже в друзьях».
- После сквозного шифрования исходящие личные сообщения сразу получают шифротекст в оптимистическом объекте, поэтому открытый текст не сохраняется в ожидании подтверждения сервера.
- Голосовые сообщения автоматически останавливаются на 120 секундах; результат изменения голоса имеет тот же предел.
- Во время записи и звонков экран остаётся включённым, а при уходе со страницы устройства и таймеры освобождаются.
- Android также защищает ключи и кэши через Android Keystore и AES-256-GCM; веб-клиент сохраняет браузерную модель хранения.
Восстановление сеанса и надёжность сообщений
PaperPhonePlus раздельно обрабатывает локальное состояние аккаунта, соединение реального времени и синхронизацию сообщений. Открытый WebSocket считается готовым только после ответа сервера auth_ok. Двусторонний ping/pong обнаруживает полуоткрытые соединения после смены VPN/IP, переключения Wi-Fi/мобильной сети или приостановки приложения.
- Access Token действует 30 минут. Refresh Token устройства действует 90 дней и продлевается при активном использовании, поэтому пароль повторно вводить не требуется.
- Устройства, уже вошедшие через старую версию, обновляются автоматически, пока прежний Token действителен. Если он уже истёк, потребуется один последний ручной вход.
- Каждое исходящее сообщение имеет стабильный
client_msg_id. Сообщения без ACK остаются в постоянной локальной очереди и повторно отправляются с тем же ID; уникальное ограничение сервера предотвращает дублирование. - Каждое сохранённое сообщение имеет монотонно растущий
server_seq. После аутентификации, переподключения и возврата на передний план клиент догружает пропущенные сообщения по курсору. - Явный выход и отзыв устройства аннулируют долгосрочную серверную сессию. Обычные сетевые ошибки и смена IP её сохраняют.
Important
При обновлении сначала разверните server, затем клиенты. Server автоматически применяет и проверяет миграцию надёжности и не запускается при отсутствии обязательных столбцов. Перед обновлением production создайте резервную копию MySQL.
Технологический стек
Бэкенд (server/)
Rust (Axum 0.8) — Высокопроизводительный асинхронный веб-фреймворк
sqlx + MySQL 8.0 — Хранение данных пользователей/сообщений
deadpool-redis + Redis 7 — Онлайн-статус + маршрутизация между узлами
aws-sdk-s3 — Файловое хранилище Cloudflare R2 (S3-совместимый API)
argon2 + jsonwebtoken аутентификация
Фронтенд (client/)
React 19 + TypeScript + Vite 6
Zustand управление состоянием
libsodium-wrappers-sumo (WebAssembly — Curve25519 / XSalsa20-Poly1305)
WebRTC API — видео / голосовые звонки
Web Audio API — изменение голоса в реальном времени (аудиоцепочка ScriptProcessorNode)
PWA: manifest.json + Service Worker
Криптографический уровень
Бессостоянный ECDH + XSalsa20-Poly1305 — эфемерная пара ключей для каждого сообщения
Четырёхуровневое хранение ключей: memory → localStorage → sessionStorage → IndexedDB
Все приватные ключи хранятся только на устройстве — никогда не отправляются на сервер
📖 Подробное руководство по развёртыванию (中文) | Deployment Guide (English) — Полные пошаговые инструкции по гибридному развёртыванию Zeabur + Vercel, локальному развёртыванию Docker Compose + Nginx, а также настройке адреса сервера в клиентских приложениях.
Вариант 0: Zeabur — облачное развёртывание в один клик
Сетевое ограничение Zeabur: шаблон развёртывает LiveKit через WebSocket/API 7880 и ICE/TCP 7881. Сейчас Zeabur не предоставляет UDP-порты сервисов, поэтому звонки 1:1 и конференции используют TCP. Для производственных звонков используйте LiveKit Cloud или виртуальную машину с поддержкой UDP.
Настройка Nginx на сервере
Используйте двухдоменную конфигурацию deploy/nginx/paperphone-plus.conf. Замените api.example.com и meeting.example.com, получите TLS-сертификаты, скопируйте файл в /etc/nginx/sites-available/paperphone-plus, включите его и выполните sudo nginx -t && sudo systemctl reload nginx. На сервере задайте LIVEKIT_URL=wss://meeting.example.com. Nginx проксирует только API и WebSocket; TCP 7881 и UDP 7882 необходимо открыть напрямую в системном и облачном межсетевых экранах.
Tip
Продвинутый вариант: гибридное развёртывание Zeabur + Vercel После развёртывания на Zeabur вы можете вручную удалить сервис client и развернуть фронтенд на Vercel (см. Вариант 2 ниже). Таким образом сервер/MySQL/Redis размещаются на Zeabur, а фронтенд ускоряется глобальной CDN Vercel. Фронтенд не требует переменных окружения на Vercel — пользователи просто вводят адрес бэкенд-сервера на странице входа.
Вариант 1: Docker Compose (Рекомендуется)
git clone <repo-url> && cd paperphone-plus
cp server/.env.example server/.env
# Отредактируйте: DB_PASS / JWT_SECRET / LIVEKIT_URL и т.д.
docker compose up -d
open http://localhost
Вариант 2: Фронтенд на Vercel
# 1. Сделайте форк этого репозитория
# 2. Импортируйте в Vercel: Root Directory = client/, Build = npm run build, Output = dist/
# Переменные окружения не требуются
# 3. Разверните бэкенд через Docker или Zeabur
# 4. Откройте фронтенд, развёрнутый на Vercel, и введите адрес бэкенд-сервера на странице входа
# например https://your-server.zeabur.app
Вариант 3: Локальная разработка
# Бэкенд (Rust)
cd server && cp .env.example .env && cargo run --release
# Фронтенд (React)
cd client && npm install && npm run dev
Изменение голоса
Голосовые сообщения, звонки 1:1 и групповые звонки поддерживают изменение голоса в реальном времени с 3 режимами на выбор:
| Режим | Скорость | Эффект |
|---|---|---|
| 🐢 Медленный | 0.8x | Более глубокий, низкий голос — идеально для анонимности |
| 🔊 Обычный | 1.0x | Оригинальный голос, без обработки |
| 🐇 Быстрый | 1.2x | Более высокий голос — весело и игриво |
Как это работает: Используется Web Audio API для построения цепочки обработки звука (AudioContext → MediaStreamSource → ScriptProcessorNode → MediaStreamDestination), которая регулирует высоту тона/скорость микрофонного ввода в реальном времени.
- Голосовые сообщения: Выберите режим голоса во время записи. Экспортированный файл
.webmуже содержит голосовой эффект — получатели не могут восстановить оригинальный голос, что обеспечивает настоящую анонимную переписку - Звонки 1:1 / групповые: Нажмите кнопку изменения голоса во время звонка для переключения между режимами. Обработанная аудиодорожка заменяет оригинальную через
LiveKit LocalAudioTrack.replaceTrack()
Серверная настройка не требуется. Изменение голоса работает полностью на стороне клиента.
Переменные окружения
| Переменная | Описание | По умолчанию |
|---|---|---|
PORT | Порт сервера | 3000 |
JWT_SECRET | Ключ подписи JWT (измените в продакшене) | dev_secret |
DB_HOST / DB_PASS / DB_NAME | Подключение к MySQL | — |
REDIS_HOST / REDIS_PASS | Подключение к Redis | — |
R2_ACCOUNT_ID | ID аккаунта Cloudflare | — |
R2_ACCESS_KEY_ID | Ключ доступа API-токена R2 | — |
R2_SECRET_ACCESS_KEY | Секретный ключ API-токена R2 | — |
R2_BUCKET | Название бакета R2 | — |
R2_PUBLIC_URL | Публичный базовый URL R2 (опционально) | — |
LIVEKIT_URL | Публичный WebSocket URL LiveKit для всех звонков | — |
LIVEKIT_API_KEY | API Key, общий для сервера и LiveKit | — |
LIVEKIT_API_SECRET | API Secret, общий для сервера и LiveKit | — |
VAPID_PUBLIC_KEY | Публичный ключ Web Push VAPID (опционально) | — |
VAPID_PRIVATE_KEY | Приватный ключ Web Push VAPID (опционально) | — |
VAPID_SUBJECT | Контактный email VAPID (опционально) | mailto:admin@paperphoneplus.app |
FCM_PROJECT_ID | ID проекта Firebase (опционально, Capacitor Android) | — |
FCM_CLIENT_EMAIL | Email сервисного аккаунта Firebase (опционально) | — |
FCM_PRIVATE_KEY | Приватный ключ сервисного аккаунта Firebase (опционально, поддерживает как экранирование \n, так и реальные переводы строк; см. ниже) | — |
FCM_RELAY_SECRET | Секрет relay FCM push (опционально, установите на relay-хосте для активации эндпоинта) | — |
FCM_RELAY_URL | URL relay FCM push (опционально, self-hosted серверы указывают на relay-хост) | — |
FCM_RELAY_KEY | Ключ авторизации relay FCM push (опционально, должен совпадать с FCM_RELAY_SECRET relay-хоста) | — |
ONESIGNAL_APP_ID | OneSignal App ID (опционально) | — |
ONESIGNAL_REST_KEY | OneSignal REST API Key (опционально) | — |
ONESIGNAL_RELAY_SECRET | Секрет relay OneSignal push (опционально, установите на relay-хосте для активации эндпоинта) | — |
ONESIGNAL_RELAY_URL | URL relay OneSignal push (опционально, self-hosted серверы указывают на relay-хост) | — |
ONESIGNAL_RELAY_KEY | Ключ авторизации relay OneSignal push (опционально, должен совпадать с ONESIGNAL_RELAY_SECRET relay-хоста) | — |
NTFY_BASE_URL | URL сервера ntfy (опционально, по умолчанию используется публичный ntfy.sh) | https://ntfy.sh |
NTFY_TOKEN | Токен авторизации ntfy (опционально, для self-hosted серверов) | — |
APNS_TEAM_ID | Team ID разработчика Apple (опционально, нативный iOS push) | — |
APNS_KEY_ID | ID ключа авторизации APNS (опционально) | — |
APNS_PRIVATE_KEY | Содержимое приватного ключа APNS .p8 (опционально, поддерживает экранирование \n) | — |
APNS_BUNDLE_ID | Bundle Identifier iOS-приложения (опционально) | — |
APNS_SANDBOX | Режим песочницы APNS (опционально, true для разработки/TestFlight) | false |
APNS_RELAY_SECRET | Секрет push relay (опционально, установите на relay-хосте для активации эндпоинта) | — |
APNS_RELAY_URL | URL push relay (опционально, self-hosted серверы указывают на relay-хост) | — |
APNS_RELAY_KEY | Ключ авторизации push relay (опционально, должен совпадать с APNS_RELAY_SECRET relay-хоста) | — |
TELEGRAM_BOT_TOKEN | Токен Telegram-бота (опционально) | — |
STICKER_PACKS | Пользовательские стикер-паки (опционально, name:label) | 12 встроенных по умолчанию |
ADMIN_PATH | URL-путь панели администратора | /admin |
ADMIN_PASSWORD | Пароль панели администратора (измените в продакшене) | admin123 |
Обработка переводов строк в приватном ключе FCM
Поле private_key в JSON сервисного аккаунта Firebase содержит RSA-ключ в формате PEM, который требует реальных символов перевода строки (\n, ASCII 0x0A) между каждой 64-символьной строкой. Однако многие платформы развёртывания (Zeabur, Vercel, Railway, Docker) хранят переменные окружения как однострочные значения, преобразуя \n в буквальную двухсимвольную последовательность \ + n.
Это самая распространённая причина сбоя FCM push-уведомлений — парсер PEM молча завершается с ошибкой, и push-уведомления не отправляются без каких-либо записей в логах.
Сервер обрабатывает это автоматически: fcm.rs нормализует буквальные последовательности \n обратно в реальные переводы строк перед парсингом. Оба формата работают:
-
Однострочный (рекомендуется для облачных платформ): Вставьте необработанное значение
private_keyиз JSON-файла как есть, с экранированием\n:FCM_PRIVATE_KEY=-----BEGIN PRIVATE KEY-----\nMIIEvQ...\n-----END PRIVATE KEY-----\n -
Многострочный (для .env файлов): Оберните полное содержимое PEM в кавычки с реальными переводами строк:
FCM_PRIVATE_KEY="-----BEGIN PRIVATE KEY----- MIIEvQ... -----END PRIVATE KEY-----"
| Платформа | Рекомендуемый формат | Примечания |
|---|---|---|
| Zeabur | Однострочный (экранирование \n) | Вставьте значение JSON непосредственно в панели Variables |
| Docker / docker-compose | Любой | Используйте YAML | для многострочного формата; однострочный в .env |
| Vercel / Railway | Однострочный (экранирование \n) | Поля ввода обычно не поддерживают реальные переводы строк |
| Linux .env файл | Многострочный (в кавычках) | Убедитесь, что кавычки правильно закрыты |
Устранение неполадок: Если переменные FCM установлены, но push-уведомления Android не работают, проверьте логи сервера:
[FCM] No access token available→ Ошибка формата приватного ключа (проблема с переводами строк)[FCM] ✅ Push sent to user xxx→ Отправка FCM работает, проблема на стороне клиента- Логи FCM отсутствуют →
FCM_PROJECT_IDне установлен или нет токена в таблицеfcm_tokens
Push-уведомления ntfy (Android-устройства без Google Services)
Для Android-устройств без Google Mobile Services (Huawei, Xiaomi, OPPO, vivo и др.) PaperPhonePlus поддерживает push-уведомления через ntfy.
Настройка по умолчанию (без конфигурации): Используется публичный сервис ntfy.sh. Дополнительная настройка не требуется.
Опциональная конфигурация (для self-hosted серверов ntfy):
NTFY_BASE_URL=https://your-ntfy-server.com
NTFY_TOKEN=your_optional_auth_token
Порядок настройки для пользователя:
- Установите приложение ntfy (Google Play / F-Droid / Прямая загрузка)
- Откройте настройки PaperPhonePlus и найдите карточку «ntfy Push»
- Скопируйте отображаемое имя темы и подпишитесь на неё в приложении ntfy
- Нажмите «Зарегистрировать Push» для завершения регистрации
Примечание по безопасности: Уведомления ntfy содержат заголовки и краткие описания в открытом виде (не фактическое содержание сообщений). Для повышения безопасности рассмотрите возможность self-hosted сервера ntfy.
APNS Push (Нативное iOS-приложение)
APNS (Apple Push Notification Service) отправляет push-уведомления нативным iOS-приложениям, созданным с помощью Capacitor. Доступны два варианта конфигурации:
Вариант A: Прямая конфигурация (сервер разработчика приложения)
- Войдите в Apple Developer → Certificates, Identifiers & Profiles → Keys
- Нажмите + для создания нового ключа → отметьте Apple Push Notifications service (APNs) → Register
- Скачайте файл
.p8(⚠️ можно скачать только один раз!) и запишите Key ID - Запишите ваш Team ID со страницы Apple Developer Membership (10-символьный буквенно-цифровой код)
- Добавьте в
server/.env:
APNS_TEAM_ID=AB12CD34EF
APNS_KEY_ID=LH4Z9YN3P7
APNS_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\nMIGTAgEA...(содержимое файла .p8)...\n-----END PRIVATE KEY-----"
APNS_BUNDLE_ID=com.yourcompany.paperphoneplus
APNS_SANDBOX=false
APNS_SANDBOX: Установитеtrueдля сборок разработки/TestFlight,falseдля продакшена App Store.
Вариант B: Через Push Relay (self-hosted серверы)
Если вы используете чужое iOS-приложение (например, скачанное из App Store), у вас нет учётных данных Apple разработчика и вы не можете отправлять APNS push напрямую. Используйте Push Relay.
Как это работает:
┌──────────────────────┐ ┌─────────────────────────┐ ┌─────────┐
│ Self-hosted сервер │ HTTP │ Сервер разработчика │ APNS │ Apple │
│ (нет учётных данных │──────→│ (есть .p8 Key + Relay) │──────→│ ──→ 📱 │
│ Apple) │ │ │ └─────────┘
│ │ │ │
│ APNS_RELAY_URL=... │ │ APNS_TEAM_ID=... │
│ APNS_RELAY_KEY=... │ │ APNS_RELAY_SECRET=... │
└──────────────────────┘ └─────────────────────────┘
Шаг 1: Разработчик приложения включает эндпоинт Relay
На сервере разработчика приложения (который уже имеет учётные данные APNS) установите секрет relay:
# .env сервера разработчика приложения (уже содержит APNS_TEAM_ID и т.д.)
APNS_RELAY_SECRET=a_long_random_shared_secret
Это автоматически активирует эндпоинт push relay по адресу POST /api/push-relay/apns.
Шаг 2: Пользователь self-hosted сервера настраивает Relay
Self-hosted серверам нужны только две переменные — учётные данные Apple не требуются:
# .env self-hosted сервера
APNS_RELAY_URL=https://app-developer-server.com
APNS_RELAY_KEY=the_shared_secret_from_step_1
Как это работает:
- Self-hosted сервер получает офлайн-сообщение → запрашивает локальную таблицу
apns_tokensдля получения токенов iOS-устройств пользователя - Отправляет токены устройств + заголовок/тело push через HTTP POST на Relay
- Relay проверяет ключ, затем отправляет в Apple с использованием собственных учётных данных APNS
- Relay возвращает список устаревших токенов; self-hosted сервер автоматически очищает свою локальную базу данных
Приоритет: Локальные учётные данные APNS → Push Relay → пропуск (без уведомления). Если оба варианта настроены, приоритет имеет прямое локальное подключение.
Примечание по безопасности: Relay передаёт только заголовки и краткие описания push-уведомлений (например, «Вам пришло новое сообщение»), а не фактическое содержание сообщений. Токены устройств не могут использоваться для чтения данных пользователей.
Push Relay (Все каналы)
Для операторов self-hosted серверов, использующих чужое опубликованное приложение (например, из App Store/Google Play), у вас нет учётных данных push разработчика (Apple .p8 Key / сервисный аккаунт Firebase / API-ключ OneSignal).
Система Push Relay обеспечивает возможность ретрансляции для каналов APNS, FCM и OneSignal:
Разработчик приложения включает эндпоинты relay на своём сервере:
# .env сервера разработчика приложения
APNS_RELAY_SECRET=a_long_random_string
FCM_RELAY_SECRET=a_long_random_string
ONESIGNAL_RELAY_SECRET=a_long_random_string
Пользователи self-hosted серверов указывают только URL relay и ключ — учётные данные push-сервисов не требуются:
# .env self-hosted сервера
# APNS (нативный iOS push)
APNS_RELAY_URL=https://app-developer-server.com
APNS_RELAY_KEY=shared_secret
# FCM (нативный Android push)
FCM_RELAY_URL=https://app-developer-server.com
FCM_RELAY_KEY=shared_secret
# OneSignal (приложения, обёрнутые через Median.co)
ONESIGNAL_RELAY_URL=https://app-developer-server.com
ONESIGNAL_RELAY_KEY=shared_secret
Приоритет: Локальные учётные данные → Push Relay → пропуск (без уведомления). Если оба варианта настроены, приоритет имеет прямое локальное подключение.
Официальный Push Relay
Операторы self-hosted серверов могут использовать официальный push relay для включения push-уведомлений iOS/Android без настройки каких-либо учётных данных push:
# 2026-05-18
APNS_RELAY_URL=https://619.chat
APNS_RELAY_KEY=EzmpqftbsENaRUO6BTABxLV96q7RuEDyokXJr1DWdDjL54cLg7yXVUQqydCQvxrX
FCM_RELAY_URL=https://619.chat
FCM_RELAY_KEY=EzmpqftbsENaRUO6BTABxLV96q7RuEDyokXJr1DWdDjL54cLg7yXVUQqydCQvxrX
ONESIGNAL_RELAY_URL=https://619.chat
ONESIGNAL_RELAY_KEY=EzmpqftbsENaRUO6BTABxLV96q7RuEDyokXJr1DWdDjL54cLg7yXVUQqydCQvxrX
Добавьте эти строки в файл .env вашего self-hosted сервера.
Лицензия
Этот проект лицензирован под GNU Affero General Public License v3.0 (AGPL-3.0).