wraithbound-launcher-server работает на серверной инфраструктуре. Игрок его
не скачивает: в дистрибутив входят Electron и клиентские native binaries, но не
control-plane и не приватные ключи.
Зона ответственности
Control-plane отдаёт bootstrap, список доступных сборок, подписанные manifests, maintenance и минимальную версию лаунчера. Сами JAR, assets и Java проходят напрямую через CDN, поэтому API не становится файловым прокси. Сайт остаётся владельцем аккаунтов, OAuth, групп, банов и платежей. Административный UI обращается только к tRPC сайта. Закрытый bearer token остаётся в Nest-процессе, а browser никогда не вызывает admin gRPC напрямую. Публичные сайт/tRPC маршруты не смешиваются с протоколом лаунчера.Модули
Новая бизнес-логика добавляется в сервисы и repository/gateway, а не в
main.rs или transport handlers.
Рабочая композиция создаёт каталог и maintenance через CatalogStorage, поэтому
выбор filesystem или S3 остаётся на границе конфигурации. Упрощённые
filesystem-конструкторы существуют только в тестовой сборке и не попадают в
production binary.
Каталог релизов
Control-plane работает с неизменяемыми manifest-envelope и отдельно хранит указатель на активный релиз:build_id, release_id и одинаковое поколение envelope/payload. При первой
активации PublishBuild создаёт запись сборки из подписанных полей manifest;
для существующей сборки обновляет её метаданные и activeReleaseId.
RollbackBuild тем же способом выбирает ранее установленный подписанный
release. Игровые файлы и manifest при этом не переписываются.
InstallBuildRelease является отдельной authenticated операцией: она принимает
envelope от publisher/CI, проверяет его и атомарно сохраняет, но не делает
релиз активным. Повтор тех же bytes идемпотентен; другое содержимое под уже
существующим releaseId отклоняется. Благодаря этому publisher может работать
на отдельной машине без прямого доступа к Docker volume каталога.
Локальный запуск
Production
deploy/docker/compose.yml; hardened unit для
запуска без Docker — в deploy/systemd, а проверенный маршрут публичного
gRPC-Web edge — в deploy/nginx. Публичный hostname
рекомендуется закрепить как launcher-api.wraithbound.com; контейнер слушает
loopback, а TLS завершается в Nginx Proxy Manager или другом reverse proxy.
Перед первым запуском заполните deploy/docker/.env, смонтируйте secret-файлы,
trust registry и каталог publisher-конфигураций, затем выполните:
/health/ready.
Read-only проверку AWS credentials и прав на выбранный prefix можно выполнить
без запуска listener и без private signing key:
catalog.json, выполняет полный paginated inventory и выводит
только backend, namespace, число объектов и общий размер. Запись и удаление S3
objects не выполняются.
Через публичный edge доступен только wraithbound.launcher.v1.LauncherService.
Маршрут wraithbound.admin.v1.LauncherAdminService возвращает 404: publisher
и CI обращаются к нему через приватную сеть или SSH tunnel. Это не позволяет
случайно превратить admin bearer token в браузерный credential.
Текущая модель хранения рассчитана на один активный экземпляр control-plane:
изменения catalog.json, operations.json и maintenance сериализуются внутри
процесса, но не используют распределённую блокировку S3. До добавления lease
или транзакционного metadata-store нельзя запускать несколько пишущих replicas
для одного WRAITHBOUND_S3_PREFIX.
Текущий этап
Bootstrap, S3-каталог, проверка signed manifests, maintenance, trust bundle, атомарная активация, rollback, постоянный журнал операций и health endpoints работают.PrepareBuild ставит allowlisted buildId в ограниченную очередь и
запускает publisher по серверному <buildId>.toml. Пути из браузера не
входят в RPC-контракт. Пока job находится в очереди или worker, повторный PrepareBuild
для того же build id отклоняется, поэтому два publisher-процесса не меняют одну
сборку одновременно. История операции сохраняется в том же storage backend и
восстанавливается после рестарта. Операции, чей последний state был queued
или running, получают отдельное terminal-событие failed/interrupted: UI не
показывает погибший вместе с процессом worker как бесконечно работающий.
Worker читает JSONL-события publisher по мере выполнения и сохраняет только
разрешённые этапы подготовки профиля, assets и Java runtime. Служебные детали
publisher, включая локальные пути, в журнал операций не переносятся. stderr
полностью вычитывается, чтобы дочерний процесс не заблокировался, но в памяти и
серверном warning сохраняются не более 16 КиБ диагностики.
Каждый worker ограничен WRAITHBOUND_PUBLISHER_TIMEOUT_SECONDS (по умолчанию
1800 секунд). При зависании launcher-server завершает publisher, сохраняет
terminal-событие failed/timeout и освобождает reservation сборки. Production
preflight принимает значения от 60 секунд до 24 часов.
In-process contract tests проверяют admin bearer middleware, отбрасывание
небезопасных buildId/releaseId и полный путь установки подписанного
envelope до активации и записи истории. Они не требуют запуска listener или S3.
Сетевой smoke-test поднимает launcher-server на свободных loopback-портах и
проверяет health, отказ без admin credential, установку/активацию подписанного
release и его публичное получение. После Rust-клиента тот же процесс опрашивает
Node-only TypeScript admin SDK: storage, maintenance, releases, операции,
блокировка/разблокировка сборки и
реестр ключей должны совпасть с состоянием сервера.
pwsh, не использует
системный proxy для loopback и удаляет временный каталог после проверки.