Собственный ретранслятор

Ретранслятор переносит зашифрованное содержимое между вашими устройствами. Он не видит ничего, кроме хеша комнаты и шифротекста, и ничего не хранит — но верить на слово не обязательно, потому что вы можете запустить его сами. Это один файл на 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

В ответ приходит идентификатор экземпляра. Это поле не украшение: именно по нему клиент понимает, что балансировщик посреди сессии перекинул его на другую реплику, — а это и есть тот отказ, с которым такая схема должна обращаться осторожно; смотрите раздел о репликах ниже.

Направьте приложение на него

  1. Откройте приложение и перейдите в Настройки → Ретранслятор.
  2. Введите адрес своего ретранслятора — ws://localhost:8000 для локальной проверки, wss://clip.example.com для настоящего.
  3. Как только он ответит, в строке состояния появится Подключено. Сделайте то же самое на втором устройстве и синхронизируйте между ними одну запись; публичный ретранслятор при этом не задействован.

Клиент командной строки принимает --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.