Релей для передачи подписки на Apple TV / Android TV из любой сети.
Сервер хранит два непрозрачных блоба с TTL 5 минут и не может прочитать передаваемые данные: ключ выводится на устройствах из 8-значного кода по протоколу CPace, релей его вывести не может.
| Метод | Путь | Кто вызывает |
|---|---|---|
POST |
/pair/init |
TV — занимает код |
GET |
/pair/{code} |
Sender — читает {sid, ya} |
POST |
/pair/{code}/send |
Sender — кладёт шифротекст |
GET |
/pair/{code}/result |
TV — забирает (long-poll 30 с) |
Служебные: /healthz, /readyz.
Ключевой лимит — 5 попыток на код, дальше код сгорает целиком. Смена IP не помогает: счётчик привязан к коду. Именно поэтому 8 знаков достаточно — офлайн-перебор невозможен, каждая попытка требует обращения к серверу.
Дополнительно: 300 запросов/мин на IP и тело до 64 KiB.
Отдельного лимита на создание кодов нет намеренно: за одним внешним адресом (CGNAT оператора, общежитие) могут быть сотни устройств, и почасовой лимит отрезал бы часть пользователей. От заполнения Redis защищают общий лимит и вытеснение записей по TTL.
npm install
redis-server --port 6379 --save ''
npm test
npm startdocker compose up -dПоднимет релей на :8080 и Redis рядом. Ничего больше не нужно — сервис не
хранит состояния между перезапусками, кроме активных пейрингов (5 минут).
Готовый образ: ghcr.io/incy-dev/incy-tv-relay:main.
Смотреть стоит два файла:
src/app.js— обработчики: тела запросов только валидируются по длине и передаются в хранилище как есть;src/store.js— Redis: два поля с TTL, никакой расшифровки.
Ключ шифрования выводится на устройствах из 8-значного кода по протоколу
CPace. Сервер видит только публичные значения ya и yb, из которых секрет
не восстанавливается.