0060. Хаб ProAnimaStudio: одобрение обновлений, подпись релизов, статистика и обратная связь
Статус: accepted. Owner: Ian Panaev. Дата: 2026-10-03. Дополняет ADR 0038 (источник обновлений) и ADR 0059.
Контекст
Установки Arkvory брали последний стабильный релиз прямо с GitHub. С этим было три проблемы:
- Выпуск нельзя было раскатить постепенно или остановить: опубликованная версия сразу становилась кандидатом для всех установок.
- Репозиторий приватный, поэтому каждой установке клиента нужен был GitHub-токен с доступом к нему.
- Целостность держалась на SHA-256 из того же релиза. Кто получит доступ к публикации, мог бы выпустить файл, который поставят все установки с автообновлением.
У студии есть свой сервис pas-Hub, он ведёт обновления, обратную связь и анонимную статистику приложений ProAnimaStudio. Владелец выбрал (2026-10-03):
- хаб — основной источник обновлений, при его недоступности — GitHub;
- хаб доработан под приватный репозиторий;
- ключ подписи — файл на рабочей станции выпуска;
- статистика включена по умолчанию и выключается.
Решение
Подпись. Релиз подписывается ключом Ed25519 в формате minisign (тот же, что у Tauri).
- Подписывается манифест
arkvory-release.json: в нём SHA-256 архива и установщика. release:localпишет рядомarkvory-release.json.sigиlatest.jsonдля хаба. Вlatest.jsonпо платформамlinux-x86_64иwindows-x86_64лежат подпись в base64 и ссылка на манифест.- Черновик, созданный GitHub Actions, подписывается на рабочей станции командой
npm run release:sign -- sign-draft <версия>. - Закрытый ключ хранится только в
~/.proanima/arkvory-release-signing.key(или вARKVORY_SIGNING_KEY_FILE). Открытые ключи встроены вapps/deploy/src/release-keys.ts. Ротация: релиз N несёт старый и новый ключ, следующие подписаны новым.
Проверка. Любой релиз из сети — от хаба или с GitHub — ставится только с подписью встроенного ключа. Цепочка: подпись → манифест → SHA-256 архива. Локальный каталог (--artifact, пакеты) — выбор оператора; подпись рядом с ним, если есть, тоже проверяется.
Хаб решает. Порядок проверки обновлений:
- Планировщик (раз в 6 часов) и
updateспрашиваютGET /v1/arkvory/update/{os}/{arch}/{текущая}?channel=и передаютX-Install-Id. - Хаб отвечает версией, которую человек продвинул в канал
stableилиbeta, с учётом доли раскатки и паузы, либо204. - Файлы идут через
/v1/arkvory/download/…: хаб своим токеном получает у GitHub короткоживущую подписанную ссылку и отдаёт её редиректом. Токен хаба установкам не передаётся. GitHub-токен на установке клиента больше не нужен. - Явная версия (
--version, pin) берётся через хаб без решения о раскатке, но с той же проверкой подписи.
Обход через GitHub — только если хаб недоступен. Сеть, тайм-аут или 5xx — тогда читается последний стабильный релиз GitHub (нужен токен для приватного репозитория). Ответ хаба 4xx, отсутствующий файл или неверная подпись — ошибка, а не повод идти в обход.
Статистика. Включена по умолчанию, выключается arkvory configure --statistics off или в консоли. Отправляется:
- случайный id установки (
config/install-id) при проверке обновлений; - событие
updatedпосле установленного обновления.
Без статистики id не отправляется; хаб тогда отдаёт версию, только когда она раскатана на всех. Версия, ОС и архитектура всё равно видны хабу в адресе проверки. Хаб не хранит IP, имена и содержимое.
Обратная связь. Любой вошедший пользователь отправляет из консоли текст, адрес для ответа и до 6 снимков экрана: вставка из буфера, перетаскивание или выбор файла. Ещё прикладывается журнал страницы: ошибки, предупреждения и неудачные запросы к API; заголовки авторизации и длинные строки, похожие на секреты, маскируются.
- Маршрут
POST /api/v1/feedback(JSON, лимиты хаба) проверяет запрос и пересылает его в хаб формой multipart. - Администратор может приложить журнал API и сводку системы: последние ≈1,5 МБ записей журнала процесса, у которых закрытый список полей, плюс версии, схему, состояние обновлений и зеркал, без адресов и секретов. Он видит их в форме до отправки;
GET /api/v1/feedback/attachmentsотдаёт их только администратору. - Почту отправляет хаб через SMTP проекта; пароль почтового ящика хранится только в хабе.
- Адрес хаба для API —
ARKVORY_HUB_URL: не задан — хаб студии, пустой — обратная связь выключена. - Ошибки:
unavailable/feedback_disabled,unavailable/hub_unreachable,rate_limited/feedback_attempts(сRetry-After).
Настройка. arkvory configure --hub-url <https://…> | --hub-off, --update-channel stable|beta, --statistics on|off пишут config/hub.json; адрес хаба также попадает в runtime.json для API. Снимок обновлений получил необязательные поля statistics и channel, запрос configure — необязательный statistics. Изменения контракта аддитивные.
Последствия
- Выпуск: собрать и проверить (
release:local), опубликовать черновик, затем одобрить версию для канала и доли установок в консоли хаба. Без одобрения установки её не берут, пока хаб доступен. - Потеря ключа подписи означает, что существующие установки не примут новые релизы, кроме как вручную из файла. Резервная копия ключа — обязанность владельца.
- Хабу нужен fine-grained токен GitHub с Contents: read на
ProAnima/Arkvory(secrets/github_tokenв деплое хаба). В консоли хаба нужен проектarkvoryс SMTP-настройками почтового ящика. - Не сделано: подпись нативных пакетов
.deb,.rpmи.exe(их проверяет оператор, ставящий пакет), отправка обратной связи из CLI.
Проверки
tests/release-signing.test.mjs:- подпись и проверка;
- подмена байтов или комментария, чужой ключ, ключ с чужим id;
- папка релиза с
.sigиlatest.json; - отказ при ключе, неизвестном установкам.
tests/hub-updates.test.mjs(поддельный хаб на loopback):- одобренная версия, подпись и SHA-256;
X-Install-Idтолько при включённой статистике,204;- чужая подпись, несовпадающий манифест;
- правила обхода через GitHub;
- настройки и id установки;
- событие
updated.
tests/feedback.test.mjsиtests/integration/feedback.test.mjs:- лимиты;
- форма multipart для хаба;
- журнал сервера только администратору;
- ответы хаба в контракте ошибок;
- кольцевой буфер журнала, маскировка.
tests/browser/feedback.mjs:- вставка, выбор и удаление снимков;
- предпросмотр вложений, отправка;
- RU/EN, обе темы, 390 и 1440 px, скрытие после выхода.
tests/browser/updates.mjs: переключатель статистики.tests/update-monitor.test.mjs: статистика и канал в снимке.- pas-Hub
tests/api.rs: приватный репозиторий — синхронизация, выдача версии, подписанная ссылка, токен не уходит в хранилище.