Собственный ретранслятор
Ретранслятор переносит зашифрованное содержимое между вашими устройствами. Он не видит ничего, кроме хеша комнаты и шифротекста, и ничего не хранит — но верить на слово не обязательно, потому что вы можете запустить его сами. Это один файл на Python без базы данных.
Запустите
docker run -p 8000:8000 ghcr.io/akshaynikhare/realtimeclipboard-relay
Или из клона репозитория, вообще без контейнера:
pip install -r backend/requirements.txt
python -m uvicorn main:app --app-dir backend --port 8000
Это весь ретранслятор целиком. Ни базы данных, ни тома, ни шага миграции, и резервировать нечего — каждая сессия живёт в памяти и исчезает вместе с процессом, и это замысел, а не ограничение.
Проверьте, что он работает
curl http://localhost:8000/health
В ответ приходит идентификатор экземпляра. Это поле не украшение: именно по нему клиент понимает, что балансировщик посреди сессии перекинул его на другую реплику, — а это и есть тот отказ, с которым такая схема должна обращаться осторожно; смотрите раздел о репликах ниже.
Направьте приложение на него
- Откройте приложение и перейдите в Настройки → Ретранслятор.
- Введите адрес своего ретранслятора —
ws://localhost:8000для локальной проверки,wss://clip.example.comдля настоящего. - Как только он ответит, в строке состояния появится Подключено. Сделайте то же самое на втором устройстве и синхронизируйте между ними одну запись; публичный ретранслятор при этом не задействован.
Клиент командной строки принимает --relay или
REALTIMECLIPBOARD_RELAY для того же самого.
TLS и настоящее доменное имя
cd deploy
REALTIMECLIPBOARD_DOMAIN=clip.example.com docker compose up -d
В каталоге deploy лежит файл compose, который ставит перед ретранслятором Caddy и автоматически получает сертификат, а рядом с ним — чарт Helm для Kubernetes.
На практике TLS не опция. Браузеры дают
crypto.subtle только в защищённом контексте, поэтому
страница, отданная по обычному HTTP с адреса в локальной сети, не сможет
даже вывести ключ — приложение там не заработает, что бы ни делал
ретранслятор.
Прежде чем масштабировать
Зафиксируйте одну реплику или дайте ему Redis. Комната живёт в
памяти того процесса, который её обслуживает, поэтому две реплики за
round-robin балансировщиком разводят две половины одной сессии по разным
машинам, и ни одна не видит другую. Выглядит это как молча не доходящее
содержимое, похоже на ошибку клиента и ею не является. Чарт Helm ровно
поэтому отказывается принимать replicaCount > 1 без
Redis.
Заметки о собственном сервере
полностью разбирают переменные окружения и путь к отказоустойчивости.
Когда не работает
Подключено, но ничего не приходит
Почти всегда это больше одной реплики без общего состояния — смотрите
выше. Сравните идентификатор /health с каждого устройства:
если они разные, это и есть весь ответ.
Приложение вообще до него не достаёт
Два кандидата. Content-Security-Policy самой страницы перечисляет
источники ретрансляторов, к которым ей разрешено подключаться, поэтому в
собственной копии статического сайта нужно обновить
connect-src в index.html,
app.html и на каждой странице внутри
src/pages/. А если вы пользуетесь публичным сайтом со своим
ретранслятором, тамошняя политика его не пропустит — поднимайте обе
половины у себя.
Подключается и тут же отваливается, снова и снова
Обратный прокси не пробрасывает апгрейд до WebSocket. Клиент после этого
откатывается на server-sent events плюс POST и продолжает работать, но
медленнее, — поэтому проблема выглядит как вялость, а не как отказ.
Пробросьте Upgrade и Connection.
Простаивающая сессия исчезает
Пустые комнаты убираются. Так и задумано — ретранслятору, который ничего не хранит, нечего утекать.
Как его выключить
docker compose down # or: docker rm -f <container>
Удалять нечего. Всё, что держал ретранслятор, было в памяти, и это был шифротекст.
Полные эксплуатационные заметки, включая таблицу переменных окружения и путь к отказоустойчивости: docs/SELF-HOSTING.md. Все платформы: руководство по установке. Что-то здесь неверно или устарело? Заведите issue.