first cycle lab release

This commit is contained in:
2026-10-04 00:30:09 +07:00
parent 85f6897365
commit d134daafd8
92 changed files with 5623 additions and 9 deletions
+118
View File
@@ -0,0 +1,118 @@
# Backup и восстановление — Phase 5
`./mcpack backup` работает с полным релизом и runtime, который уже связан
с ним через `scripts/server.sh`. Нужны Node/npm dependencies, закреплённый
Compose, Docker daemon, GNU tar и zstd. Оператор должен читать весь runtime
и сохранять владельцев при распаковке: запускать от владельца данных или
с необходимыми правами. Проверка UID/GID не пропускается.
## Создание snapshot
```bash
export MC_ROOT=/absolute/server-root
export MC_RELEASE_DIR=/absolute/full-release
# MC_ENV_FILE — необязательный абсолютный путь, по умолчанию MC_ROOT/.env.
./mcpack backup --wait-timeout 120
```
Команда удерживает общую `.mcpack-runtime.lock`, проверяет релиз/settings,
останавливает сервер через Compose и подтверждает остановку по Docker state.
После этого архивирует **весь `MC_ROOT/runtime` без исключений**: world,
данные модов вне world, конфиги, server.properties, whitelist/operators,
логи, cache, downloads и установленные server/mod JAR. Для NeoForge безопасные
исключения пока не подтверждены. Releases, snapshots и host `.env` находятся
вне runtime и не входят в архив; внутри snapshot хранится отдельная полная
копия исходного релиза, включая его `.env.example`.
Результат находится в `MC_ROOT/backups/<UTC timestamp>-<UUID>/`:
```text
runtime.tar.zst
metadata.json
SHA256SUMS
release/ # исходный полный релиз с собственными checksums
```
Metadata фиксирует исходный релиз, SHA256 manifest/checksum list, закреплённый
image, целевой релиз (если передан `--target-release /absolute/release`),
прежнее состояние сервера, подтверждение остановки, время, исключения,
архив SHA256/размер и inventory путей/хешей/размеров/mode/UID/GID runtime.
`--target-release` только проверяет и записывает назначение будущего deployment;
команда backup сама не применяет новый релиз.
Перед публикацией выполняются tar/zstd listing, проверка типов/путей и
пробная распаковка с точным сравнением inventory. Checksums покрывают все
файлы snapshot и сохранённого релиза. Каталог публикуется атомарным rename
внутри backups; незавершённый snapshot не появляется под окончательным именем.
При успехе ранее запущенный сервер возвращается на тот же релиз через
`up --wait`. Остановленный сервер или отсутствующий контейнер не запускаются.
Ошибка backup возвращает non-zero. При ошибке после успешной остановки сервер
остаётся остановленным; оператор получает ошибку и status в
`MC_ROOT/.mcpack-backup-operation.json`. Ошибка resume сохраняет опубликованный
проверенный snapshot и пытается снова остановить сервер. Новый deployment
должен использовать `backupBeforeApply` из `platform/backups.mjs`: callback
применения допускается только после публикации snapshot и под той же блокировкой.
Этот gate проверен тестами и используется SSH deployment Phase 7 под общей
блокировкой всей операции: [deploy.md](deploy.md).
Snapshot не удаляется автоматически. Нужны свободное место под полный runtime,
пробную распаковку и копию релиза; операция завершается ошибкой при нехватке места.
Символические/жёсткие ссылки и специальные файлы в runtime сейчас отклоняются.
Команды вне wrapper не участвуют в блокировке; во время backup не изменять
runtime вручную и не запускать контейнер прямым Compose.
## Восстановление в отдельный каталог
```bash
./mcpack restore \
--backup /absolute/server-root/backups/TIMESTAMP-UUID \
--destination /absolute/restored-server
```
Destination должен **не существовать**. Команда сначала проверяет snapshot,
распаковывает во временный соседний каталог, сверяет bytes/mode/UID/GID,
проверяет сохранённый релиз и публикует новый root атомарным rename.
Исходный runtime не изменяется. Результат:
```text
restored-server/runtime/
restored-server/release/
restored-server/.mcpack-runtime.json
restored-server/restore-report.json
```
Host settings и EULA acceptance из snapshot не восстанавливаются; сервер
автоматически не запускается. Для ручного recovery подготовить `.env` из
`release/server/.env.example`, задать прежние UID/GID, подходящие memory/port
и принятие EULA. Используется прежний стабильный Compose project из binding.
Прежде чем запускать восстановленную копию под тем же project, остановить
и удалить **старый контейнер** через wrapper (данные старого root сохраняются):
```bash
MC_ROOT=/absolute/original-server \
MC_RELEASE_DIR=/absolute/original-release scripts/server.sh down
# На данном host settings уже настроены и EULA принята:
export MC_ROOT=/absolute/restored-server
export MC_RELEASE_DIR="$MC_ROOT/release"
export MC_ENV_FILE=/absolute/prepared-host-settings.env
scripts/server.sh up -d --wait --wait-timeout 120
scripts/server.sh exec -T minecraft mc-health
scripts/server.sh logs --tail 100
```
Не заменять binding или release на более новую версию: восстановленный мир
нужно запускать с сохранённым исходным релизом. Если прежний контейнер ещё
использует другой root, wrapper отклонит запуск. Это ручное восстановление
в отдельный root; переключение active release и rollback — последующие фазы.
## Область проверки
Проверен local Linux/WSL Docker и NeoForge 21.1.252 / Minecraft 1.21.1.
Primary Phase 0 runtime пока управляется старым spike wrapper без full-release
binding, поэтому его миграция не выполняется этой командой. Off-host копии,
retention, автоматический rollback и защита от внезапного отключения питания
во время записи (fsync) остаются последующими улучшениями.
Реальный опыт восстановления и свидетельства: [phase5-results.md](phase5-results.md).
+94
View File
@@ -0,0 +1,94 @@
# Build contract — Phase 2
Из корня проекта, с Node.js 22+ и npm:
```bash
npm ci --ignore-scripts
scripts/spike-tools.sh
./mcpack build
```
Дополнительные параметры:
```bash
./mcpack build --config platform/config.yaml
./mcpack build --release
```
`--config` выбирает YAML manifest; относительные пути pack/output внутри
него считаются от корня репозитория. По умолчанию build разрешает dirty
worktree и записывает это в manifest. `--release` требует чистого worktree.
Команды не делают commit/tag/deploy и не запускают Minecraft.
Имя, версия, Minecraft и loader читаются из TOML packwiz. Полный релиз
создаётся в `<output>/<name>-<version>/` и содержит:
```text
<name>-<version>.mrpack
server/compose.yaml
server/.env.example
server/config/
server/README.md
manifest.json
SHA256SUMS
```
Релиз той же версии не перезаписывается. Для изменённого source нужно выбрать
новую версию pack, явно выполнить packwiz refresh, затем build. Для локального
эксперимента с прежней версией можно выбрать другой output directory через
отдельный config. Не изменять опубликованный release directory вручную.
## Pipeline
1. Parse/validate YAML и TOML: обязательные поля, индексы/hash, explicit side,
дубликаты mod targets/project IDs, пути, подтверждённый loader/image.
2. Проверить SHA256 packwiz/Compose по lock и Git state/известные secret paths.
3. Получить lock конкретной версии и временный каталог в output directory.
4. Скопировать pack; выполнить packwiz refresh/export. Любое изменение
metadata после refresh останавливает build, исходники не исправляются.
5. Проверить экспорт, сформировать серверные файлы и выполнить Compose config.
6. Записать manifest, покрыть SHA256SUMS все release files кроме SHA256SUMS;
проверить checksums и неизменность build inputs.
7. Атомарно переименовать готовый временный релиз; убрать временные файлы/lock.
При обычной ошибке partial release не остаётся. После принудительного убийства
процесса может остаться `.build-*.lock`/временный каталог: удалять их только
убедившись, что соответствующий build уже не работает.
Детерминированными являются имя и размещение артефактов. Побайтовая
воспроизводимость ZIP не обещается: packwiz может включать timestamps.
## Server payload
Compose образ закреплён literal digest из toolchain lock. Memory/port defaults
берутся из platform config; local env может переопределять их при запуске.
Runtime и release подключаются через абсолютные `MC_ROOT` и `MC_RELEASE_DIR`.
Release не зависит от checkout или его `.tools/`. Используется стабильное
Compose project name `minecraft-lab-spike`, как в текущем локальном runtime.
`server/config/` включается в релиз и монтируется как `/config`: его файлы
имеют приоритет над соответствующими pack defaults. Локальный исходный
Compose spike по-прежнему включает этот слой только явным override.
Server properties генерируются environment, отдельного source-файла нет.
`.env`, активный локальный compose override, SSH target и platform YAML
не копируются в release. Публичный `.env.example` имеет EULA=FALSE.
Проверка известных secret filenames не является универсальным secret scanner.
## Manifest v1
- `pack`: name/version, Minecraft, loader ID/version.
- `git`: commit и dirty flag; generated output исключается из dirty check.
- `tools`: packwiz commit/hash, Compose version/hash, Node version/npm lock hash.
- `server`: image, подтверждённая Java major, defaults и project name.
- `source_files`: относительные пути inputs и SHA256, без содержимого config.
- `managed_files.client/server`: runtime paths, owner (`pack`/`server`),
hashes и политика replace; server override помечает заменённого владельца.
- `runtime_policy`: неизвестные файлы preserve, defaultconfigs seed-only.
- `release_files`: hashes payload без manifest и SHA256SUMS, чтобы избежать
циклического хеширования. SHA256SUMS дополнительно покрывает manifest.
Ownership описывает контракт для будущего deploy; Phase 2 сама ничего
не удаляет из runtime. Совместимость Java проверяется по записи Phase 0
в `platform/compatibility.json`, а не новым запуском Docker при каждом build.
Новая комбинация требует отдельного spike и записи результата.
+171
View File
@@ -0,0 +1,171 @@
# SSH deployment — Phase 7
`./mcpack deploy` применяет готовый полный immutable release через SSH/rsync.
Если artifact ещё не создан, команда вызывает clean `build --release`;
если уже есть, проверяет его checksums и provenance. Development artifact
с `git.dirty=true` отклоняется, даже если текущий worktree уже чистый.
## Подготовка цели
На host нужны Linux x86_64, Node.js 22+, доступный Docker daemon, rsync,
GNU tar и zstd. Pinned Compose и небольшой Node agent с закреплённым YAML
parser передаются из локального bootstrap/dependencies; системный Docker
не устанавливается. У оператора должны быть права на весь runtime и его
владельцев; выбранные UID/GID согласовать с владельцем каталога.
В `platform/config.yaml` выбрать SSH host/alias и **отдельный root сборки**:
```yaml
server:
host: crab@192.168.1.61
directory: /home/crab/workspace/minecraft-lab/minecraft-lab-neoforge-spike
memory: 2G
port: 25565
```
Host `.env` готовится отдельно, вне releases, с EULA/UID/GID/memory/port
и стабильным MC_PROJECT_NAME. Runtime startup читает именно этот файл;
build defaults не перезаписывают host settings. Первое применение требует
пустого runtime без старого binding; существующий неопознанный мир не
принимается автоматически. У разных сборок разные roots, projects и ports.
SSH host key должен быть известен заранее. Используются BatchMode,
StrictHostKeyChecking=yes и keepalive. Ключи/порты задать в SSH config;
password-only сессия может использовать заранее аутентифицированный
ControlMaster. Пароль не записывается в config, env, artifact или agent.
```bash
./mcpack deploy --artifact /absolute/full-release
# Порт/ControlMaster/known_hosts в отдельном SSH config:
./mcpack deploy --artifact /absolute/full-release --ssh-config /absolute/ssh-config
# Node из nvm, которого нет в non-interactive SSH PATH:
./mcpack deploy --artifact /absolute/full-release \
--ssh-config /absolute/ssh-config \
--remote-node /home/crab/.nvm/versions/node/v26.10.0/bin/node
```
`--config FILE` выбирает другой platform config. Timeout берётся из его
`deploy.healthcheck_timeout`; он должен быть больше 10 секунд stability.
Первый image/dependency download может требовать 600+ секунд. IPv6 host
и другие сложные адреса сейчас задавать через SSH alias.
## Выполнение и state
Под SSH удерживается одна `.mcpack-runtime.lock` на всё получение релиза,
backup и применение. Перед передачей проверяются незавершённый deployment,
source/binding, target version collision, host settings/права и свободное место.
Релиз передаётся rsync в `releases/.incoming-*`, проверяется на host и
публикуется atomic rename. Сбой передачи не останавливает исходный сервер.
Agent в `.mcpack/agent-UUID/` также проверяется по полной checksum coverage.
Перед применением проверяется каждый replace-managed файл прошлого release.
При изменении/missing file или занятом новом managed path возвращается конфликт.
Для небольших server-owned text configs выводится content diff, для остальных
файлов — expected/actual hash. Неуправляемые файлы не удаляются. Нужно перенести
полезные правки в source и построить новую версию; флага force для молчаливого
перетирания конфигов нет.
Если тот же release уже активен, выполняется `health` без restart; успешный
повторный deploy возвращает noop. Одинаковая версия с другим manifest/checksums
отклоняется до остановки. Если активный release unhealthy, повторный deploy
не перезапускает его автоматически.
При смене релиза исходный сервер останавливается, подтверждается остановка,
создаётся verified snapshot всего runtime и исходного release. Только после
этого apply callback меняет binding, удаляет отсутствующие в новом релизе
известные replace-managed файлы и запускает target. Конфликты проверяются
повторно непосредственно перед apply. Image применяет pack/server overrides
при запуске; runtime-generated/unmanaged данные сохраняются.
После стабильного mc-health/status query обновляются previous-release и
active-release, каждый через atomic rename. Deployment state записывается
перед каждым этапом и связывает source/target identities, прежние pointers,
backup path и факт apply. Пара указателей не является единым filesystem
transaction: interrupted `committing` требует просмотра state перед recovery.
## Ошибка и ручное восстановление
Любой failed/incomplete `deployment-state.json` блокирует следующую операцию.
При прерывании SSH во время upload worker сохраняет failed state и освобождает
lock; при принудительном убийстве worker/power loss lock может остаться.
Не удалять stale lock, пока соответствующий host process действительно жив.
После ошибки **до apply** source/runtime binding не переключаются. При ошибке
backup сервер может остаться остановленным; проверить logs и backup operation.
После подтверждения неизменности мира можно запустить исходный release.
Failed state нужно сохранить для аудита и явно разрешить только после recovery.
Если `applied=false`, binding совпадает с active manifest, причина исправлена
и исходный сервер проверен health, сохранить failed record под отдельным именем
в server root, затем повторить deploy. Для ручного config edit новый clean
release должен содержать ровно текущие bytes этого managed файла — такой
осознанный перенос разрешается. Любое другое содержимое остаётся конфликтом.
Пример архивирования **только после перечисленных проверок на host**:
```bash
mv "$MC_ROOT/deployment-state.json" \
"$MC_ROOT/deployment-state-resolved-$(date -u +%Y%m%dT%H%M%SZ).json"
```
Не применять этот способ к `applied=true`, неизвестному binding или
interrupted committing: сначала восстановить/завершить реальное состояние.
После ошибки **apply/startup** target останавливается, source pointers не
продвигаются, binding остаётся на фактически применённом target и snapshot
сохраняется. Старый pack автоматически не запускается на изменённом мире.
Восстановить snapshot в новый root вместе с сохранённым source release по
[backups.md](backups.md), подготовить host settings, удалить старый контейнер,
запустить восстановленную копию и проверить health. Не менять binding вручную,
чтобы обойти защиту существующего мира.
Для первого неудачного deploy без snapshot использовать state/release/diagnostics:
пустой мир можно повторно инициализировать отдельно либо явно завершить запуск
того же target. При ошибке `committing` сначала проверить actual binding,
Docker health и оба pointer files. Snapshot recovery реализован в
[Phase 8](rollback.md). Простое удаление failed state не является recovery.
## Текущий проверенный host
На `crab@192.168.1.61:2228` подготовлен root
`/home/crab/workspace/minecraft-lab/minecraft-lab-neoforge-spike`.
Release 0.0.5 healthy (обновление публикации порта после Phase 7), UID/GID 1000:1000, memory 2G,
MC_PROJECT_NAME=minecraft-lab-neoforge-spike. Offline mode включён.
Clean release собран в отдельном checkout и опубликован локально:
```bash
./mcpack deploy \
--artifact dist/minecraft-lab-neoforge-spike-0.0.5 \
--ssh-config .tools/phase7-ssh-target/config \
--remote-node /home/crab/.nvm/versions/node/v26.10.0/bin/node
```
SSH config хранит только соединение/known_hosts/ControlPath. После окончания
ControlMaster для password login сначала заново аутентифицировать сессию:
```bash
ssh -F .tools/phase7-ssh-target/config minecraft-lab-target true
```
Host settings теперь содержат `MC_BIND_ADDRESS=0.0.0.0`; порт 25565
публикуется на всех IPv4-интерфейсах host. В Prism подключаться напрямую
к **192.168.1.61:25565**, без SSH tunnel. Локальный Phase 0 сервер остаётся
отдельным на 127.0.0.1:25565. Default для новых host остаётся 127.0.0.1;
LAN bind выбирается явно в host .env. Проверка — [network-results.md](network-results.md).
Сохранённый remote agent содержит `scripts/server.sh`. Для lifecycle на host
выбрать этот agent, задать абсолютные MC_ROOT/MC_RELEASE_DIR, указать nvm Node
в PATH и выполнить wrapper:
```bash
export PATH=/home/crab/.nvm/versions/node/v26.10.0/bin:$PATH
export MC_ROOT=/home/crab/workspace/minecraft-lab/minecraft-lab-neoforge-spike
export MC_RELEASE_DIR="$MC_ROOT/releases/$(cat "$MC_ROOT/active-release")"
# Подставить один из проверенных каталогов .mcpack/agent-*:
/absolute/agent/scripts/server.sh health
/absolute/agent/scripts/server.sh logs --tail 100
```
При failed deployment брать фактически применяемый release из state/binding,
а не автоматически active-release. Agents/releases/backups автоматически
не удаляются. Свидетельства и границы проверок — [phase7-results.md](phase7-results.md).
+237
View File
@@ -0,0 +1,237 @@
# Phase 0 — integration spike
Дата: 2026-10-03. Статус: **предварительный Fabric-эксперимент проверен; целевой Phase 0 должен проверить NeoForge/Forge без аккаунтов**.
GUI-проверки не заменяются проверкой ZIP или Minecraft status query.
## Окружение и версии
Arch Linux x86_64 в WSL2, kernel `6.18.40.1-microsoft-standard-WSL2`.
Изначально отсутствовали Linux packwiz, Docker, Compose, Go и Java.
В Windows обнаружен каталог PrismLauncher; аккаунты и существующие instances
не читались и не изменялись.
| Компонент | Зафиксированное значение |
| --- | --- |
| Minecraft | 1.21.1 |
| Fabric Loader | 0.19.5 |
| Fabric installer/launcher | 1.1.2, наблюдаемое значение в .fabric-manifest.json |
| Fabric API | 0.116.17+1.21.1, Modrinth `Mys3P7lK`, both |
| Mod Menu | 11.0.5, Modrinth `6lgOkclV`, client |
| Text Placeholder API | 2.4.2+1.21, Modrinth `U5bhVym2`, client в этой сборке |
| packwiz commit | `ef87d964f8cbd52b3b13ea42453ef322290e2b9e` |
| packwiz GitHub Actions run | `34043101039`, Linux 64-bit x86 |
| packwiz binary SHA-256 | `7f8ed641be0c28988895651e3270cc9ad4b40c436e3d5cf9b3bcdb718b4369c6` |
| Prism Launcher | 11.0.3-main, commit d393323fdc52ef645c48a04ca73aff2bbe24ffc9 (из локального лога) |
| JVM сервера | Temurin 21.0.12.1+1-LTS |
| Docker Engine | 29.8.2, storage driver overlayfs |
| Compose | v5.6.0, локальный официальный binary |
| itzg image digest | `sha256:99dc8b37f97c3b95915452180bbedf29a7ac8252932e372ce6e5b6e383a5d1e2` |
| itzg source revision | `beed693663b4c963d1bb4af1101855eccbc6a37b` |
| itzg image creation | 2026-10-02T12:03:38.864Z |
Образ первоначально скачан по тегу java21; Compose использует только digest.
JVM проверена без запуска Minecraft: Temurin OpenJDK 21.0.12.1+1-LTS.
Команда: `docker run --rm --entrypoint java <закреплённый-image> -version`.
## Фактически выполнено
1. Получены точные версии из официальных GitHub, Fabric и Modrinth API.
2. Скачан официальный packwiz из привязанного к commit workflow run;
SHA-256 архива и binary закреплены в `toolchain.lock.env`.
3. Установлен Docker в WSL (`pacman -S --needed --noconfirm docker`),
daemon запущен командой `systemctl start docker`, без enable при загрузке.
4. Скачан и проверен SHA-256 официального Compose v5.6.0 в `.tools/`.
5. Создан source pack и добавлены закреплённые моды через packwiz.
6. Для Text Placeholder API сторона явно изменена с both на client:
здесь библиотека нужна только client-only Mod Menu.
7. Обновлён source index, выполнен export в временной копии source.
8. Проверены содержимое ZIP, версии dependencies и env каждой записи.
9. `scripts/spike-server.sh config --quiet` и `bash -n scripts/*.sh` успешны.
10. После явного принятия EULA пользователем выставлен MC_EULA=TRUE в игнорируемом server/.env.
11. Первый запуск, status healthcheck, side filtering и bind mounts проверены на реальном сервере.
12. Проверены recreate, смена версии мода, удаление модов, конфиги и datapack на существующем мире.
13. Startup с отсутствующим mrpack вернул 1; hash level.dat при ошибке не изменился.
14. Исходный snapshot восстановлен в отдельный каталог; world/config совпали до запуска, сервер 0.0.1 снова healthy.
15. `git diff --check` успешен; .tools, артефакты, world, backups и .env игнорируются Git.
Артефакт:
```text
dist/minecraft-lab-spike-0.0.1/minecraft-lab-spike-0.0.1.mrpack
SHA-256: 923fa6e5b54921b7b19925bbe634d91e09b113ccd512f1edaaeab818de3a9338
```
ZIP содержит `modrinth.index.json` и `overrides/config/spike-marker.txt`.
Fabric API помечен required на обеих сторонах; Mod Menu и Text Placeholder API
имеют `server: unsupported`. На установленном сервере подтверждено отсутствие обоих client-only JAR.
Маркер проверяет копирование произвольного конфига; его не читает игровой мод.
## Команды создания и воспроизведения
Первоначальный init из `pack/`:
```bash
../.tools/packwiz/packwiz --cache ../.tools/packwiz/cache --config ../.tools/packwiz/config.toml init \
--name minecraft-lab-spike --version 0.0.1 --author minecraft-lab \
--mc-version 1.21.1 --modloader fabric --fabric-version 0.19.5 -y
../.tools/packwiz/packwiz --cache ../.tools/packwiz/cache --config ../.tools/packwiz/config.toml modrinth add --project-id P7dR8mSH --version-id Mys3P7lK -y
../.tools/packwiz/packwiz --cache ../.tools/packwiz/cache --config ../.tools/packwiz/config.toml modrinth add --project-id mOgUt4GM --version-id 6lgOkclV -y
# Явно изменить side в placeholder-api.pw.toml на client.
../.tools/packwiz/packwiz --cache ../.tools/packwiz/cache --config ../.tools/packwiz/config.toml refresh
```
В новом checkout достаточно уже закоммиченного source:
```bash
scripts/spike-tools.sh
scripts/spike-build.sh
scripts/spike-server.sh config --quiet
bsdtar -xOf dist/minecraft-lab-spike-0.0.1/minecraft-lab-spike-0.0.1.mrpack modrinth.index.json
```
Для изменения source packwiz запускается из `pack/` с локальными cache/config,
как в командах выше. Проверка SHA-256 артефакта выполняется из его каталога:
```bash
cd dist/minecraft-lab-spike-0.0.1
sha256sum -c SHA256SUMS
```
Build v0 не создаёт production release manifest, Git tags или удалённый deployment.
Игровые версии читаются из pack.toml; выбор MC_PACK — ссылка на готовый артефакт.
## Матрица проверки
| Проверка | Статус |
| --- | --- |
| packwiz описывает и экспортирует сборку | PASS |
| ZIP содержит закреплённые dependencies и side metadata | PASS |
| Compose config валиден | PASS |
| Prism импортирует и запускает pack | PASS: instance 0.0.1, Minecraft/Fabric и три JAR подтверждены |
| Сервер устанавливает локальный mrpack и становится healthy | PASS: mc-health, 1.21.1, online=0 |
| На сервере нет client-only JAR | PASS: только fabric-api JAR |
| World и config находятся в host bind mount | PASS |
| World переживает recreation контейнера | PASS: level.dat hash до запуска совпал |
| Обновление удаляет старый mod JAR | PASS: 0.116.17 → 0.116.16; затем удалены все JAR |
| Конфиги: pack defaults, server override, ручная правка | PASS: override победил; update перезаписал ручную правку |
| Backup/restore возвращает world и прежний release | PASS: восстановлен baseline и pack 0.0.1 |
| Startup failure возвращает non-zero | PASS: отсутствующий mrpack, exit 1; world не изменён |
| Клиент Prism подключается к offline-серверу | PENDING: повторная попытка после online-mode=false |
| Datapack на существующем мире | PASS: file/phase0 автоматически загружен в 0.0.2 |
| NeoForge/Forge defaultconfigs | НЕ ПРОВЕРЕНО: другой loader |
## Порядок серверных проверок
После принятия EULA скопировать `.env.example` в `.env` и выставить MC_EULA=TRUE.
Сервер изолирован локальным адресом, без публикации RCON. Первоначально ошибочно требовался online-mode=true; по уточнению пользователя переключён на online-mode=false и enforce-secure-profile=false. Microsoft/Mojang-аккаунт не требуется.
```bash
scripts/spike-server.sh up -d --wait --wait-timeout 600
scripts/spike-server.sh ps
scripts/spike-server.sh logs --tail 100
scripts/spike-server.sh exec -T minecraft java -version
scripts/spike-server.sh exec -T minecraft mc-health
ls runtime/mods runtime/config runtime/world
```
Затем остановить сервер и записать hash `runtime/world/level.dat`.
Recreate выполняется при остановленном сервере:
```bash
scripts/spike-server.sh stop
sha256sum runtime/world/level.dat
scripts/spike-server.sh create --force-recreate
sha256sum runtime/world/level.dat
scripts/spike-server.sh start
```
Два hash до запуска должны совпасть. После старта world может штатно измениться;
это не потеря данных. Дополнительно проверить UUID/seed и игровой маркер из клиента.
Для воспроизведения вариантов на новом checkout после исходного build выполнить `scripts/spike-variants.sh`. Скрипт создаёт source-копии в .tools/repro-variants и артефакты 0.0.2/0.0.3; runtime не меняет. Повторный запуск при существующих каталогах прекращается без перезаписи.
0.0.2: Fabric API fpB2VatZ (0.116.16+1.21.1), marker v2, статический datapack из testdata/spike-datapack. 0.0.3: удалены Mod Menu, Text Placeholder API и Fabric API, остаётся loader и config marker. Исходный pack/ всё время оставался на 0.0.1.
Перед сменой MC_PACK обязательно остановить сервер и создать snapshot.
Проверить удаление прежнего JAR и фактическое состояние marker.
Возврат старого pack выполнять вместе с соответствующим snapshot.
Минимальная ручная процедура backup (в корне, после stop):
```bash
mkdir -p backups
task_snapshot="backups/$(date -u +%Y%m%dT%H%M%SZ)-$(date +%s%N).tar.zst"
tar --exclude=./.gitkeep -C runtime -I zstd -cf "$task_snapshot" .
tar -I zstd -tf "$task_snapshot" > /dev/null
sha256sum "$task_snapshot" > "$task_snapshot.sha256"
# Записать рядом исходный MC_PACK и SHA256SUMS релиза.
```
Restore: сервер остановлен; сначала сохранить текущее состояние. Проверить hash
архива, распаковать в новый каталог рядом с runtime и проверить файлы.
Переименовать текущий runtime в сохранённый каталог, новый — в runtime;
выбрать исходный MC_PACK и запустить с healthcheck. Не распаковывать поверх мира.
Этот ручной сценарий проверен: snapshot phase0-before-update восстановлен в отдельный каталог, исходный runtime сохранён в .tools/runtime-before-restore, затем запущен pack 0.0.1.
Prism: импортировать исходный .mrpack, запустить Minecraft, проверить Mod Menu,
зайти на localhost:25565, поставить различимый игровой маркер и проверить его
после recreation. Записать версию Prism, JVM клиента и фактический результат.
## Фактические результаты переходов
- Первый запуск: Minecraft 1.21.1, Fabric 0.19.5, JVM Temurin 21.0.12.1+1; mc-health отвечает version=1.21.1 online=0 max=20.
- Docker inspect: /data — rw bind на runtime/, /modpacks — ro bind на dist/. Named volumes отсутствуют.
- Recreate: до повторного запуска SHA-256 level.dat остался `32832255e84e2d8b4e324527d21b233b0b0cc4615ae17ee0296e7e8c5d4d9728`.
- Update 0.0.1 → 0.0.2: только fabric-api-0.116.16+1.21.1.jar, старый 0.116.17 удалён; manual-runtime-v1 заменён на pack-default-v2.
- Datapack: лог «Found new data pack file/phase0, loading it automatically», затем «[Phase0] datapack-v2-loaded».
- Optional override: `MC_PACK=minecraft-lab-spike-0.0.2/minecraft-lab-spike-0.0.2.mrpack scripts/spike-server.sh -f server/compose.spike-override.yaml up -d --wait --wait-timeout 120`; marker стал server-override-v1.
- Update → 0.0.3 без override: runtime/mods пуст, управляемые файлы прежнего datapack удалены, unmanaged-probe.txt сохранился.
- Failure: MC_PACK=missing-phase0.mrpack, Compose --wait вернул 1; контейнер exited(1), hash мира остался прежним.
- Restore: sha256sum snapshot проверен, распаковка в .tools/runtime-restored, сравнение world/config с baseline успешно; прежний runtime сохранён, восстановленный переименован в runtime. После запуска исходного pack 0.0.1 сервер healthy.
Каждый изменяющий тест предварялся корректной остановкой и snapshot. Пять архивов в backups/ имеют SHA-256 и JSON metadata: исходный артефакт, image digest, server override и время; исключений нет. Имена phase0-* относятся к однократному запуску этого эксперимента. Для обычных backups использовать уникальные ID из примера выше.
Артефакты тестовых переходов (ZIP timestamps могут отличаться при повторном export):
| Релиз | SHA-256 |
| --- | --- |
| 0.0.2 | ca10d1faffa8a71ee58431f7f3f2ee88be4c7572e035cac8824331129b701fab |
| 0.0.3 | ced6fe830055ce3f0ae2d1c4c14e0fb41b5a8580bdaf5cac3010b0bdc8a9ceba |
Финальное состояние: сервер запущен с исходным pack 0.0.1 и восстановленным baseline, optional override выключен. Импорт и запуск Prism подтверждены локальными instance metadata и client log. Подключение не завершилось: Failed to log in: The authentication servers are currently not reachable. В client log sessionHost/servicesHost=https://nope.invalid и UnknownHostException: nope.invalid; launcher log содержит четыре -Dminecraft.api.*.host с этим адресом. В pack и instance/global JvmArgs такого override нет, переменные JAVA_TOOL_OPTIONS/JDK_JAVA_OPTIONS/_JAVA_OPTIONS Windows этих overrides не содержат. Для offline-клиента эти параметры не являются ошибкой настройки: изначальное требование online-mode=true противоречило цели пользователя. Требование аккаунта снято; нужен offline-server login. Account files и токены не читались.
## Обнаруженные ограничения и изменения архитектуры
- packwiz автоматически добавил Text Placeholder API и выставил both, хотя он
нужен этой сборке только на клиенте. Metadata проекта не заменяет проверку
конкретного состава зависимостей. Свой resolver не нужен.
- В Arch package index Compose 5.5.1 ссылался на отсутствующий файл (HTTP 404).
Не обновлялась вся система: Docker установлен отдельно, Compose скачан локально.
- В минимальном окружении отсутствует cmp; build сравнивает SHA-256 source и
refresh-копии вместо дополнительной системной зависимости.
- Network-вызовы sandbox требуют разрешения. Подстановка latest не используется
в воспроизводимых командах; закреплены результаты первоначального выбора.
- GitHub Actions artifacts имеют ограниченный срок хранения. Bootstrap по run ID
пригоден для spike, но не для долговременного release tooling. Для него нужен
build packwiz из закреплённого commit или сохранённый binary с checksum.
- Пакет содержит ссылки на Modrinth downloads, а не все JAR; offline install
не доказан. Образ также загружает Minecraft и loader при первом старте.
- Windows Test-NetConnection 127.0.0.1:25565 вернул TcpTestSucceeded=True после восстановления. GET sessionserver hasJoined без сессии возвращает 204 из Windows, WSL и контейнера; GET api.minecraftservices.com/publickeys возвращает 200 из Windows и контейнера. Эти пробы не передавали токены и не подтверждают login, но исключают общую недоступность этих endpoints.
- Минимальная сборка использует Fabric API, не добавляя новые блоки/предметы. Она доказывает установку loader и модов; сетевую совместимость content-модов понадобится проверить на реальной сборке.
- Fabric был неверно выбран вместо целевых NeoForge/Forge. Его результаты сохраняются только как предварительный эксперимент. Целевой Phase 0 ещё требует modpack на NeoForge/Forge, defaultconfigs и подключения offline-клиента.
- При смене mrpack управляемый config заменяется, включая ручную правку. Неуправляемый файл сохраняется. Требуется проверка drift в будущей платформе до применения пакета.
- Server override через /config с SYNC_SKIP_NEWER_IN_DESTINATION=false применяется после modpack defaults и побеждает их. Это намеренная политика теста, не универсальная политика production.
- Datapacks нужны в world/datapacks, а не в корневом /data/datapacks. В варианте 0.0.2 проверен путь overrides/world/datapacks/phase0. Это статические исходники datapack, не snapshot мира. Если сохранить предложенный pack/datapacks layout, нужен явный перенос в целевой world path; world name должен учитываться.
- Важное ограничение воспроизводимости: local mrpack закрепляет loader, но не версию Fabric installer/launcher. В runtime наблюдается 1.1.2. В закреплённом образе TYPE=MODRINTH вызывает install-modrinth-modpack напрямую, не передавая FABRIC_LAUNCHER_VERSION; в help этого helper нет соответствующего параметра. Поэтому простое добавление Fabric env в Compose не доказывает фиксацию installer. Перед production release нужно выбрать проверенный способ фиксировать/сохранять launcher или явно ограничить обещание воспроизводимости. Собственный mod resolver для этого не нужен.
- Production deployment и автоматический rollback остаются вне scope Phase 0.
Официальные источники:
- [packwiz installation](https://packwiz.infra.link/installation/)
- [packwiz Modrinth export](https://packwiz.infra.link/tutorials/hosting/modrinth/)
- [itzg local mrpack](https://docker-minecraft-server.readthedocs.io/en/latest/types-and-platforms/mod-platforms/modrinth-modpacks/)
- [itzg healthcheck](https://docker-minecraft-server.readthedocs.io/en/latest/misc/healthcheck/)
- [Modrinth format](https://support.modrinth.com/en/articles/8802351-modrinth-modpack-format-mrpack)
## Уточнение цели пользователем
Собирать пользовательские модпаки NeoForge/Forge, распространять клиентский артефакт и запускать сервер без проверки аккаунтов. ONLINE_MODE=false, ENFORCE_SECURE_PROFILE=false. Смена auth режима предварена backup phase0-before-offline-mode. Проверка Microsoft login не входит в acceptance criteria.
+85
View File
@@ -0,0 +1,85 @@
# Healthcheck — Phase 6
Проверенная готовность доступна через `scripts/server.sh` с теми же абсолютными
MC_ROOT/MC_RELEASE_DIR и host settings, что описаны в [runtime.md](runtime.md).
```bash
export MC_ROOT=/absolute/server-root
export MC_RELEASE_DIR=/absolute/full-release
scripts/server.sh up -d --wait --wait-timeout 120
# Проверить уже созданный контейнер без запуска/перезапуска:
scripts/server.sh health --wait-timeout 120
# Первый запуск с загрузкой dependencies может требовать больше времени:
scripts/server.sh up --wait --wait-timeout 600
```
`up/start/restart --wait` и `health` возвращают 0 только после штатного Docker
healthcheck `mc-health` и свежих успешных Minecraft status queries. Running
container и TCP connect сами по себе не подтверждают готовность.
По умолчанию окно стабильности — 10 секунд. На всём окне должны проходить
queries, контейнер не должен меняться или перезапускаться. Неудачный query
или health status, отличный от healthy, сбрасывает окно; проверки повторяются
до deadline. Пример с явно выбранным окном:
```bash
scripts/server.sh health --wait-timeout 30 --stable-seconds 3
scripts/server.sh restart --wait --wait-timeout 120 --stable-seconds 10
```
Timeout — целое положительное число секунд; он должен быть больше окна
стабильности. Если флаг не задан, берётся `deploy.healthcheck_timeout` из
`platform/config.yaml` (сейчас 120). Timeout учитывает Compose launch и
ожидание readiness; проверка входных данных и сбор диагностических логов
выполняются отдельно. Docker queries ограничены оставшимся временем, отдельный
status query — также максимум 10 секундами. Опрос выполняется примерно раз
в секунду. `up --wait` запускается detached; сочетание с attach/abort options
отклоняется. Флаги поддерживают и `--wait-timeout=120`.
Обычный `up -d` без `--wait` по-прежнему означает только запуск Compose и
не гарантирует готовность. Backup resume из Phase 5 использует `up --wait`
и теперь проходит новую проверку перед возвращением success.
## Ошибки и сохранённые свидетельства
Non-zero возвращается для timeout, crash/exited, paused, restart loop,
замены контейнера, отсутствующего container/healthcheck, ошибки Docker query
и ошибки Compose launch. Наблюдаемый restart или изменение StartedAt/RestartCount
во время ожидания считается нестабильным запуском и завершается ошибкой;
количество перезапусков до начала проверки используется как baseline.
На неудачном запуске выводятся пути:
```text
MC_ROOT/diagnostics/health-TIMESTAMP-UUID.json
MC_ROOT/diagnostics/health-TIMESTAMP-UUID.log
```
Report содержит причину, elapsed/timeout/stability, последнее Docker state
(включая exit code/OOM/restart count), результат последнего status query,
project и SHA256 release manifest. При отказе самого Compose сохраняются
его exit/error/signal; readiness ещё не проверена. Log содержит последние
100 строк Compose logs. Если получение логов тоже не удалось, это записывается
в log. Диагностика имеет mode 600, каталог создаётся с mode 700.
Recovery state сообщает, что runtime и binding сохранены, active release
не переключался, automatic rollback не выполнялся. Поле backup_snapshot
ссылается на snapshot последней записанной backup operation, если он есть;
null не означает, что других snapshots нет. Их можно найти в MC_ROOT/backups.
Healthcheck не удаляет runtime, не распаковывает backup и не переключает релиз.
После timeout контейнер может продолжать startup; исправить причину/увеличить
timeout и повторить `health`. После crash можно запустить тот же релиз через
`up --wait`. При restart loop сначала изучить report/log и остановить сервер
через `scripts/server.sh stop`. Восстановление — [backups.md](backups.md).
Startup и отдельный `health` удерживают общую lifecycle lock до завершения
проверки. Другой wrapper не сможет одновременно выполнить backup/restart/down.
Контейнер другого runtime root отклоняется до запроса игрового протокола.
Прямые Docker/Compose команды и ручное изменение данных обходят эту блокировку.
Это ограниченная проверка готовности после запуска, не постоянно работающий
watchdog. Ручное подключение клиента и состав модов проверяются отдельно;
SSH deployment и переключение active-release используют эту проверку
в Phase 7: [deploy.md](deploy.md).
Реальные проверки crash/restart loop/timeout — [phase6-results.md](phase6-results.md).
+52
View File
@@ -0,0 +1,52 @@
# Управление модами — Phase 3
Из корня репозитория, после установки npm dependencies и packwiz:
```bash
./mcpack add mouse-tweaks
./mcpack remove mouse-tweaks
./mcpack update sophisticated-backpacks
./mcpack update --all
```
`add` использует Modrinth; принимает slug, project ID, URL или одну строку
поиска в кавычках. Конкретная версия выбирается штатным version URL:
```bash
./mcpack add https://modrinth.com/mod/sophisticated-backpacks/version/pJxNzk4X
```
`remove` и `update <mod>` используют имя metadata, понятное packwiz, например
`sophisticated-backpacks`. Wrapper сохраняет stdin/stdout/stderr packwiz,
его prompts и код завершения. По умолчанию автоматического согласия нет.
`--yes` или `-y` явно передаёт packwiz `--yes`; при поиске packwiz может
выбрать первый результат, поэтому для точного выбора использовать slug/ID/URL.
Каждая команда поддерживает `--config FILE`. Config выбирает pack directory
относительно корня проекта; cwd вызывающей стороны не меняет выбор pack.
Проверяются config, пути и SHA256 закреплённого packwiz. Аргументы передаются
массивом без shell, строки поиска не исполняются как команды.
Команды изменяют source metadata/indexes через packwiz. Они не повышают
версию сборки, не запускают build, не изменяют готовые релизы и runtime.
Перед новым build выбрать новую `version` в `pack/pack.toml`, поскольку
существующий релиз той же версии не перезаписывается. Например, следующим
после 0.0.4 может быть 0.0.5. Правила build — [build.md](build.md).
Side metadata и dependencies остаются ответственностью packwiz; wrapper
не переписывает resolver и не удаляет автоматически оставшиеся dependencies.
Применение модов к уже работающему серверу относится к следующим фазам.
## Проверка
```bash
npm test
bash scripts/verify-phase3.sh
```
Вторая команда требует сети для Modrinth и работает в одноразовом локальном
Git clone внутри `.tools/`, с копиями проверенных инструментов и установленными
npm dependencies. Удаляет Backpacks, добавляет 3.26.6.2174, обновляет до
3.26.7.2182 отдельно и через `--all`. После каждого состояния выпускает
полный тестовый релиз с Compose/manifest/checksums. Основной pack и runtime
не меняются. Network/provider errors возвращаются как ошибки packwiz.
+36
View File
@@ -0,0 +1,36 @@
# Remote game port: 0.0.5
2026-10-03 удалённый NeoForge сервер переведён с loopback на полноценную
IPv4-публикацию порта. Подключение клиента теперь **192.168.1.61:25565**,
SSH tunnel не нужен.
- Source pack version поднята до 0.0.5; новый clean immutable release содержит
`${MC_BIND_ADDRESS:-127.0.0.1}:${MC_PORT:-25565}:25565` в Compose.
- Remote host `.env`: `MC_BIND_ADDRESS=0.0.0.0`; локальный default — 127.0.0.1.
Значение проверяется как IPv4 и берётся из host settings, а не ambient shell.
- Release построен через `scripts/build-clean-release.sh`: отдельный source
snapshot/Git checkout, clean build и проверенная atomic публикация в dist.
Основной Git worktree не коммитился; старые immutable releases не изменялись.
- Перед update создан verified backup исходного runtime и полного release 0.0.4:
`backups/20261003T161610568Z-ca83dc50-0582-4ac7-b98a-353dc957f486`
внутри remote server root. В snapshot сохранены 13 файлов мира и остальные
persistent данные; host `.env` остаётся вне snapshot.
- Deployment complete: active=0.0.5, previous=0.0.4, health=healthy.
Тот же `/runtime` bind используется после recreate. Server override marker
остался `server-override-v1`; состав/версии модов не менялись.
- Docker inspect подтверждает HostIp=0.0.0.0, HostPort=25565.
- Прямой Minecraft status query из локального workspace к 192.168.1.61:25565
без tunnel вернул Minecraft 1.21.1, protocol 767, online=0/max=20.
- Локальный Phase 0 сервер на loopback:25565 остался healthy.
`npm test`: 58 pass. Проверены build port interpolation, bind validation
и приоритет host settings; backup/deployment/health regression tests проходят.
Bash/Node syntax и git diff --check пройдены. Дополнительный protocol checker:
```bash
node scripts/query-minecraft.mjs 192.168.1.61 25565
```
Свидетельства: `.tools/lan-port-evidence/deploy.log`, `direct-status.json`,
`remote-verification.json`, `primary-server-status.txt`, `.tools/lan-port-tests.log`.
Публикация проверена в LAN; router/NAT configuration этим изменением не выполнялась.
+51
View File
@@ -0,0 +1,51 @@
# Phase 1: каркас проекта
Phase 1 завершена для рабочей основы NeoForge 21.1.252 / Minecraft 1.21.1.
## Каркас
- `platform/config.yaml`: schema version, pack/output paths, server defaults,
будущий SSH deployment target. Host/directory не заданы.
- `platform/schemas/`: место для будущей схемы вместе с parser в Phase 2.
- `pack/`: проверенный packwiz source без изменения версий и состава модов.
- `server/`: Compose, `.env.example`, явно подключаемый override template.
- `scripts/`: минимальные spike-инструменты; основного CLI пока нет.
- `docs/`, `examples/`, `testdata/`: результаты и материалы воспроизведения.
- `dist/`, `runtime/`: только `.gitkeep` включается в source.
`server.properties` генерируется из Compose environment, поэтому отдельный
source-файл не создан. Дополнительные pack-директории создаются по мере
появления контента; пустые файлы не добавляются в экспорт. Makefile и команды
deploy/backup/rollback появятся в соответствующих фазах, без пустых заглушек.
Platform manifest пока не читается spike-скриптами. Текущий runtime использует
env-файлы; подключение manifest и validation относятся к Phase 2.
## Проверка на чистом clone
Из неигнорируемых файлов рабочего дерева создан временный Git snapshot в
`.tools/phase1-check.0Od3T1/source`, затем выполнен локальный `git clone` в
соседний `clone/`. Основной репозиторий не коммитился и не менял index.
В clone отсутствовали прежние `.tools/`, runtime state, backups и `.env`.
Пройдены:
1. `scripts/spike-tools.sh`: новые downloads packwiz и Compose;
архив/бинарники совпали с закреплёнными SHA256, Compose v5.6.0.
2. `packwiz refresh`: успешен, `git diff --exit-code -- pack` подтвердил
отсутствие изменений metadata после refresh/export/build.
3. `packwiz modrinth export`: успешен, ZIP содержит index и pack config.
4. `scripts/spike-server.sh config --quiet`: основной Compose валиден.
5. То же с `-f server/compose.override.yaml.example`: слой overrides валиден.
6. `scripts/spike-build.sh`: экспорт в новый `dist/` с SHA256SUMS успешен.
7. Bash syntax и `git diff --check`: успешны.
8. `git check-ignore`: runtime, dist, tools, backups, локальные `.env` и
активный override игнорируются; `.env.example` не игнорируется.
Проверка Compose не запускала сервер; активный мир и артефакт не изменены.
Точный порядок работы для нового checkout описан в корневом README.
Bootstrap использует закреплённый GitHub Actions artifact packwiz; такие
артефакты имеют ограниченный срок хранения. Для долгосрочного bootstrap
потребуется постоянное хранение проверенного бинарника либо сборка
закреплённого commit. Текущий URL проверен свежим скачиванием.
+47
View File
@@ -0,0 +1,47 @@
# Phase 2: build command
Phase 2 завершена. `./mcpack build` читает platform YAML, валидирует packwiz
source и создаёт полный релиз, не меняя source или активный runtime.
Node.js 22+ используется для orchestration с закреплёнными YAML/TOML parsers;
экспорт и dependency resolution остаются у packwiz.
## Выпущенный development artifact
`dist/minecraft-lab-neoforge-spike-0.0.4/` содержит `.mrpack`, `server/`,
`manifest.json`, `SHA256SUMS`. Все файлы кроме SHA256SUMS покрыты checksums,
включая manifest. Pack version увеличена с 0.0.1 до 0.0.4, чтобы сохранить
существующие spike-артефакты 0.0.1–0.0.3 без перезаписи. Состав модов,
Minecraft и NeoForge не менялись.
- `.mrpack` SHA256: `877b34bf582f9d06b9dbd2832db8a1b376a786be21826628229df52a6672cd52`.
- Manifest SHA256: `5933ed34f35473026a32f0c3eab9a8b86b6fb484461bac2f670e0a74930aaa0c`.
- Git commit: `85f6897365b8d4717d8670808d2223c3024d247f`, `dirty=true`.
Dirty flag отражает незакоммиченную текущую работу. Main repo не коммитился.
Development build допускает это; `--release` требует чистого worktree.
## Подтверждённые проверки
- Реальный `./mcpack build` с закреплённым packwiz: refresh/export успешны,
metadata в копии не менялась. Перед build версия явно обновлена и refresh
выполнен пользователю видимым отдельным шагом.
- Compose нового релиза валиден с абсолютными root/release bindings;
image literal digest и defaults взяты из lock/platform config.
- Checksums всех шести release files успешны.
- Manifest содержит Minecraft/loader, provenance инструментов, исходные
hashes и управляемые пути клиента/сервера. Mouse Tweaks отсутствует в
server ownership; server marker явно overrides pack marker.
- 12 автоматических тестов успешны. Используют настоящий Git и Compose,
контролируемый packwiz substitute для отказов и ZIP fixtures. Проверяют
чистый/dirty build, configurable output/defaults, checksums, отсутствие
local secrets/target, повторную сборку, stale source, export failure,
неподтверждённый loader, дубликаты, invalid YAML/paths, tracked secrets,
symlinks и tampered tool hash.
- Bash/Node syntax, `git diff --check` успешны.
- Активный NeoForge-сервер после build остаётся healthy на 0.0.1;
контейнер не пересоздавался, мир не менялся командой build.
Контракт и воспроизведение — [build.md](build.md). Full release Compose
проверен через `config`, запуск нового релиза в Phase 2 не выполнялся.
Deployment/cleanup/rollback по ownership manifest относятся к следующим
фазам. Build лишь фиксирует контракт.
+41
View File
@@ -0,0 +1,41 @@
# Phase 3: mod wrappers
Phase 3 завершена: `mcpack add`, `remove`, `update <mod>`, `update --all`
делегируют операции закреплённому packwiz. Config/путь source/бинарник
проверяются до вызова; оригинальные prompts, stdout/stderr и exit code
packwiz сохраняются. Add поддерживает штатные Modrinth version URLs.
## Реальная integration-проверка
`bash scripts/verify-phase3.sh` успешно выполнен в тестовом clone
`.tools/phase3-check.m9vdq6/clone`. Лог: `.tools/phase3-integration.log`;
diff source: `.tools/phase3-check.m9vdq6/source-changes.diff`.
1. Remove Backpacks удалил `.pw.toml` и обновил index.
2. Add version URL создал metadata для 3.26.6.2174 / `pJxNzk4X`;
уже установленный Core сохранился.
3. Update одного мода заменил Backpacks на 3.26.7.2182 / `Igmp9PwW`.
4. Для update --all снова установлена старая версия. Команда реально
обновила её, после чего metadata вернулась к 3.26.7.2182.
5. После каждого состояния успешен full build: `phase3-remove`,
`phase3-add`, `phase3-update`, `phase3-all`. Для всех прошли Compose
config и SHA256SUMS, source metadata осталась консистентной.
Тестовые versions менялись только в clone, чтобы сохранить неизменяемые
готовые релизы. Основной pack остаётся 0.0.4 с прежними тремя модами;
активный сервер и мир не изменялись и не перезапускались.
## Автоматические проверки
`npm test`: 16/16 успешны. Помимо 12 build-тестов проверены:
- точное отображение команд/аргументов packwiz, включая --all и --yes;
- отклонение неверных комбинаций до запуска tool;
- выбор source directory через config при вызове из другого cwd;
- literal search argv без shell execution;
- сохранение stdout/stderr и тестового exit code 17;
- отказ при изменённом hash packwiz.
Bash/Node syntax и `git diff --check` успешны. Реальные GUI/server проверки
новых модов не входят в Phase 3; wrappers меняют source, не deployment.
Пользовательские команды и контракт — [mods.md](mods.md).
+43
View File
@@ -0,0 +1,43 @@
# Phase 4: server runtime
Phase 4 завершена для NeoForge 21.1.252 / Minecraft 1.21.1. Full release
0.0.4 реально запущен с пустой директорией данных через `scripts/server.sh`.
## Реальная Docker-проверка
- Server root: `.tools/phase4-server`, project: `minecraft-lab-phase4`.
- Настройки: EULA=TRUE по ранее полученному принятию, UID/GID=1000:1000,
memory=2G, IPv4 loopback port=25566.
- Первый `up -d --wait --wait-timeout 600` завершился healthy. Лог подтвердил
создание нового мира, Minecraft 1.21.1, NeoForge 21.1.252, Backpacks 3.26.7
и Core 1.5.5. Mouse Tweaks отсутствовал в серверных JAR.
- Docker inspect подтвердил обычные абсолютные bind mounts: host runtime
→ `/data` rw, release → `/modpacks` ro, server/config → `/config` ro.
- `world/level.dat` принадлежит 1000:1000. Pack загружен из `.mrpack`,
server override marker=`server-override-v1`, `online-mode=false` и
`enforce-secure-profile=false` подтверждены.
- Restart того же контейнера завершился healthy.
- После остановки и `create --force-recreate` все 23 файла мира совпали
по путям/SHA256 до нового запуска. Неуправляемый config marker сохранился.
- Вновь запущенный контейнер healthy; mc-health отвечает Minecraft 1.21.1.
- Финальная версия wrapper дополнительно проверена реальным up/exec/down
после добавления проверки принадлежности Compose project.
- Тестовый контейнер/network удалены через down, runtime и binding сохранены.
Основной сервер на 25565 не пересоздавался и остался healthy.
Свидетельства: `.tools/phase4-evidence/first-start.log`, `mounts.json`,
`before-recreate-world.json`, `recreate-verification.txt`,
`recreated-start.log`. Test root остаётся пригодным для последующих проверок.
## Автоматические проверки
`npm test`: 25 тестов. Новые runtime tests используют controlled Compose/Docker
substitutes и проверяют integrity/coverage, EULA/UID/GID, immutable binding,
смену release/project, защиту существующего мира, failed startup/exit code,
operation lock, offline/bind contract, paths и конфликт project другого root.
Build/mod regression tests также проходят. Реальная Docker-проверка выше
подтверждает integration, которую substitutes сами по себе не доказывают.
Bash/Node syntax и `git diff --check` пройдены. Backup, миграция существующего
мира, remote deployment и расширенная диагностика healthcheck не реализуются
этим этапом. Контракт и команды — [runtime.md](runtime.md).
+74
View File
@@ -0,0 +1,74 @@
# Phase 5: backups и восстановление
Phase 5 завершена для NeoForge 21.1.252 / Minecraft 1.21.1. Реализованы
`./mcpack backup`, `./mcpack restore` и обязательный gate `backupBeforeApply`
для будущего deployment. Контракт и проверенная инструкция: [backups.md](backups.md).
## Проверка с реальным Docker
Проверка 2026-10-03 выполнялась на отдельном root `.tools/phase4-server`,
project `minecraft-lab-phase4`, port 25566, full release 0.0.4. Основной
Phase 0 сервер на 25565 не останавливался и остался healthy.
- Запущенный сервер был корректно остановлен; backup всего runtime
опубликован после tar/zstd listing и пробной распаковки.
- Snapshot содержит runtime archive, metadata, полную копию release 0.0.4
и checksums всех файлов. Исключений нет.
- После backup сервер автоматически вернулся на исходный release в healthy;
`mc-health` подтвердил Minecraft 1.21.1.
- Затем намеренно подставлена команда tar с exit 42. Backup вернул non-zero,
оставил сервер остановленным, не создал дополнительный окончательный
snapshot и удалил временный staging. Metadata операции фиксирует failed.
- Старый тестовый контейнер удалён через down. Snapshot восстановлен в новый
`.tools/phase5-restored-server` без распаковки поверх существующего root.
- До запуска совпали все 416 inventory entries (160 файлов, включая 23 файла
world): пути, bytes/SHA256, mode, UID и GID. В наборе есть владельцы
1000:1000 и 0:0; обе группы сохранились.
- Подтверждены `mod-state/phase5.json` и unmanaged config вне world,
server.properties, ops.json, whitelist.json. Локальный host `.env` не
копировался: при запуске использован уже настроенный файл прежнего test root.
- Восстановленная копия запущена с собственным сохранённым `release/`:
NeoForge 21.1.252, Minecraft 1.21.1, статус healthy и успешный `mc-health`.
- После проверки восстановленный контейнер/network удалены через down.
Оба тестовых runtime и snapshot сохранены для дальнейших проверок.
Проверенный snapshot:
```text
.tools/phase4-server/backups/20261003T134409150Z-edd387d7-f947-4c3d-8ffc-bc02649cdfdc
archive bytes: 162695375
archive SHA256: e52c82adb379942ed2b63fb0cd361dd7d2cfd153314b26b026bafd942c87c9ce
release manifest SHA256: 5933ed34f35473026a32f0c3eab9a8b86b6fb484461bac2f670e0a74930aaa0c
```
Свидетельства: `.tools/phase5-evidence/backup.log`, `failed-backup.log`,
`failure-verification.txt`, `restore-verification.txt`, `resumed-health.txt`,
`restored-health.txt`, `restored-start.log`, `primary-server-status.txt`,
`integration.log`. Сценарий — `scripts/verify-phase5.sh`; он требует специально
подготовленного test root Phase 4 и ещё не существующего restore destination.
Его нельзя запускать как универсальную команду на primary сервере.
## Автоматические проверки
`npm test`: 37 tests pass. Backup/restore tests проверяют общий lifecycle lock,
stop/archive/verify/resume, stopped/absent state, разные snapshot ID,
восстановление bytes/modes/ownership и связанного release, запрет существующей
цели, повреждённый archive, неполное checksum coverage, ошибку tar/stop/resume,
unsupported links, проверку target до остановки и отказ применения до backup.
Gate test доказывает, что callback будущего apply не вызывается при ошибке
архива и вызывается только после verified snapshot под той же блокировкой.
При ошибке callback status учитывает текущее состояние контейнера и не
запускает старую сборку автоматически. SSH apply сам пока не реализован.
Build, mod commands и runtime regression tests также проходят. Node/Bash
syntax и `git diff --check` пройдены.
## Оставшиеся границы
Автоматическое удаление snapshot, off-host копии, retention, rollback и
переключение active release — последующие фазы. Пока runtime не поддерживает
links/special files в backup. Atomic publication защищает от обычной ошибки
процесса, но fsync для гарантии при отключении питания не реализован.
Primary старого spike wrapper не мигрирован на full-release binding.
Следующая Phase 6 — расширенная диагностика healthcheck, crash/restart-loop
и timeout с полезными logs.
+61
View File
@@ -0,0 +1,61 @@
# Phase 6: healthcheck и диагностика startup
Phase 6 завершена: ожидание готовности проверяет pinned image healthcheck,
Minecraft status query и стабильность контейнера; отказ возвращает non-zero
и пути к сохранённым logs/report. Контракт — [health.md](health.md).
## Реальная Docker-проверка 2026-10-03
Исходный snapshot Phase 5 восстановлен в отдельный `.tools/phase6-server`.
Использован сохранённый полный NeoForge release 0.0.4, Minecraft 1.21.1,
NeoForge 21.1.252, pinned image, host UID/GID 1000:1000 и port 25566.
Project — свободный после Phase 5 `minecraft-lab-phase4`.
- `up --wait --wait-timeout 2 --stable-seconds 1` на холодном JVM старте
вернул timeout/non-zero через 2000 ms ожидания. Container оставлен для
диагностики; runtime/binding сохранились.
- `health --wait-timeout 120 --stable-seconds 3` затем вернул 0 после
Docker healthy и стабильных свежих Minecraft queries. TCP не использован
как достаточное свидетельство готовности.
- Только тестовый контейнер аварийно завершён `docker kill --signal KILL`.
Следующий health вернул crash/non-zero, report сохранил exit code 137
и OOMKilled=false; elapsed около 230 ms.
- Повторный `up --wait --wait-timeout 120 --stable-seconds 3` поднял тот же
NeoForge release и подтвердил готовность. Исходный модпак не заменялся.
- Для restart loop создан отдельный `.tools/phase6-loop-server` и намеренно
повреждённая Compose fixture: pinned image, entrypoint с exit 42 и
restart=on-failure:5. Это тестовая копия полного релиза с отдельной версией
`phase6-failure-fixture` в manifest и пересчитанными checksums;
опубликованный immutable release 0.0.4 не менялся. Fixture не доказывает
загрузку NeoForge: её назначение — реальный Docker restart loop.
- Docker inspect подтвердил RestartCount=1 до проверки. Health затем
вернул restart-loop/non-zero за около 1476 ms с RestartCount=2 и новым
StartedAt, сохранив logs с intentional failure marker.
- Для всех ошибок есть JSON report и файл последних logs, binding сохранился,
lifecycle lock освободилась, automatic rollback не выполнялся.
- Test containers/network удалены через down. Их runtime/release/diagnostics
оставлены для просмотра. Primary Phase 0 сервер на 25565 не останавливался,
не пересоздавался и остался healthy.
Сценарий: `scripts/verify-phase6.sh`. Он требует Phase 5 snapshot и новых
fixture roots, использует только копии test runtime. Свидетельства:
`.tools/phase6-evidence/{integration.log,timeout.txt,healthy.txt,healthy-start.log,
crash.txt,recovered.txt,restart-loop.txt,loop-docker-state.txt,assertions.txt,
primary-server-status.txt}`. Подробные reports/logs находятся в diagnostics
соответствующих test roots.
## Автоматические проверки
`npm test`: 46 тестов. Deterministic health tests проверяют успешные
последовательные queries, провал протокола при running/healthy state,
starting/unhealthy, сброс окна после transient failure, crash/OOM/paused,
RestartCount/StartedAt/container identity, missing container/healthcheck,
inspection error, deadlines включая launch и слишком поздний query, строгие
флаги и диагностику Compose failure с сохранением binding/lock cleanup.
Regression tests Phase 2–5 проходят; backup resume теперь действительно
ожидает стабильную готовность. Bash/Node syntax и `git diff --check` пройдены.
SSH transport, switching active-release, automatic recovery/rollback и
continuous watchdog не добавляются в эту фазу. Следующая Phase 7 — SSH deployment
с уже готовыми backup gate и healthcheck.
+94
View File
@@ -0,0 +1,94 @@
# Phase 7: SSH deployment
Phase 7 завершена для подтверждённой NeoForge 1.21.1 / 21.1.252 комбинации.
Добавлены `mcpack deploy`, SSH worker и host deployment state. Подробный
контракт и подготовка/recovery — [deploy.md](deploy.md).
## Реальный host пользователя
2026-10-03 выполнен deployment на `crab@192.168.1.61:2228` (minipc):
- Отдельный root: `/home/crab/workspace/minecraft-lab/minecraft-lab-neoforge-spike`.
Базовый каталог для других сборок не используется как runtime.
- Подтверждены Docker 27.4.1, Node 26.10.0 через nvm, rsync, GNU tar и zstd.
Новые системные зависимости на remote host не устанавливались.
- Создан host `.env` с ранее принятой EULA, UID/GID 1000:1000, memory 2G,
port 25565, project minecraft-lab-neoforge-spike. Mode env=600, root=700.
- SSH key host закреплён в локальном known_hosts. Password authentication
использован для ControlMaster; пароль нигде не записан. Worker/rsync
используют BatchMode и StrictHostKeyChecking=yes.
- Clean release **0.0.4** построен в отдельном Git checkout, поскольку root
development release 0.0.4 имеет dirty=true и намеренно не допускается deploy.
Source repository пользователя не коммитился и immutable старый dist не менялся.
- Release manifest SHA256:
`4b9a060ecb74329c0c10ca8c24954f87f4b137dfb5240f844bb93208780a694f`.
- Первый deploy создал runtime без backup (first deploy), загрузил pinned
image/dependencies, запустил NeoForge и подтвердил status query + 10 s stability.
- Повторный deploy завершился healthy noop. Deployment state complete,
applied=false, noop=true; container StartedAt остаётся временем первого
запуска `2026-10-03T15:40:01.458777215Z`.
- Remote status query возвращает Minecraft 1.21.1. `online-mode=false` и
`enforce-secure-profile=false`; world принадлежит 1000:1000, Backpacks/Core
присутствуют, client-only Mouse Tweaks в server JAR нет.
- Сервер оставлен запущенным. Game port остаётся на remote IPv4 loopback;
инструкция SSH tunnel для клиента находится в deploy.md.
- Локальный primary Phase 0 сервер на 25565 не останавливался и остался healthy.
Свидетельства: `.tools/phase7-target-evidence/deploy-first.log`,
`deploy-noop.log`, `verification.txt`, `primary-server-status.txt`.
SSH config — `.tools/phase7-ssh-target/config`, только параметры соединения.
## Реальный изолированный SSH/rsync стенд
`scripts/verify-phase7.sh` создал loopback SSH server на 22227 с временными
ключами и новым Docker project minecraft-lab-phase7, port 25566. Полный
source snapshot помещён в отдельный Git clone, dependencies скопированы как
обычный ignored каталог (symlink node_modules делал worktree dirty).
Проверены реальные операции:
1. Clean build `0.0.4`, первая передача rsync и запуск healthy.
2. Повторная передача команды deploy той же версии: ID и StartedAt контейнера
совпали до/после, release не передавался повторно и restart не было.
3. Общий host lock заблокировал параллельный deploy до остановки сервера.
4. Clean build `0.0.5` с новым server override marker; update создал проверенный
backup исходного runtime/release, запустил target и обновил active/previous.
5. World/level.dat и persistent mod-state вне world сохранились; новый marker
применился после backup, unknown файлы не удалялись.
6. Ручное изменение managed config затем вызвало conflict/non-zero до stop/apply.
Изменённый файл сохранён, state failed/applied=false.
7. Test container/network и временный SSH daemon остановлены; snapshots,
оба releases/runtime/state/evidence сохранены.
Стенд: `.tools/phase7-check.6dbBak`, свидетельства в его `evidence/`:
`build-first.log`, `build-second.log`, `deploy-first.log`, `deploy-noop.log`,
`noop-container.txt`, `locked.log`, `deploy-second.log`, `update-assertions.txt`,
`manual-conflict.log`, `conflict-assertions.txt`, `primary-server-status.txt`.
## Автоматические проверки
`npm test`: 57 tests pass. Deployment tests используют controlled Compose/Docker
и проверяют first deploy, no-op, immutable version collision, backup-gated
update/world/mod-state, manual conflicts/diff, точный перенос ручной правки
в clean target, failure передачи/backup/startup, unresolved state, async lock
во время upload, dirty provenance, unsafe SSH arguments и CLI option scope.
При failure передачи source не останавливается. При failure backup target
не запускается. Failed startup оставляет target binding, source pointers,
verified snapshot и stopped state; старый pack не запускается автоматически.
Это unit fault injection, а не имитация сбоев на пользовательском live host.
Health failures с живым Docker проверены отдельно в Phase 6; tar failure и
restore — в Phase 5. Обычный SSH update выше подтверждает интеграцию gate.
## Границы
Первый реальный remote deploy и no-op проверены на host пользователя;
обновление и ручной conflict — на loopback стенде. Forge остаётся непроверенным.
Аккаунты отключены; LAN game port не открывался, используется tunnel.
Новый unknown существующий мир не принимается автоматически.
Failed/incomplete state блокирует следующий deploy до ручного recovery.
Указатели каждый atomic, общая пара не transaction; state содержит этап
committing и прежние значения. fsync, retention, off-host backup и автоматический
recovery не реализованы. Remote agents автоматически не удаляются.
Следующая Phase 8 — rollback с восстановлением snapshot и исходного release.
+32
View File
@@ -0,0 +1,32 @@
# Phase 8 — завершена
Проверено 4 октября 2026 (Asia/Novosibirsk): rollback по SSH без повторного build.
Оба режима автоматизированы; команды и условия описаны в [rollback.md](rollback.md).
На изолированном SSH/Docker-стенде восстановлен snapshot Phase 7, применён
чистый release 0.0.5, затем выполнен pack-only rollback на retained 0.0.4.
Маркеры мира и состояния модов сохранили текущий прогресс. Следующий snapshot
rollback восстановил исходные маркеры, убрал появившийся позже файл из активного
runtime и сохранил его вместе с текущим прогрессом в retired runtime.
Для обеих операций проверены backup текущего состояния, успешный health,
сохранение `.env` и публикации `0.0.0.0:25566`, правильные pointers и освобождение
общей блокировки. Просмотр планов сохранил список файлов root, ID контейнера и
время его запуска. Финальный Minecraft status query прошёл. Контейнер стенда удалён.
68 unit tests прошли: CLI подтверждения, readonly plan, corrupt snapshot до stop,
ошибка current backup, startup failure без автоматического возврата, snapshot
recovery после failed deploy по фактическому binding, lock и безопасные ID.
Старый immutable Compose проверен настоящим pinned Compose без зависимости от dist.
Обнаруженные права `700` у опубликованного release исправлены на `755`, чтобы
контейнер UID 1000 мог читать bind-mounted `.mrpack`; добавлена регрессионная проверка.
Доказательства: `.tools/phase8-check.Hgc1oL/evidence/`,
`.tools/phase8-unit-tests.log`, `.tools/phase8-integration.log`.
Рабочий host не откатывался: прямой query `192.168.1.61:25565` прошёл;
результат `.tools/phase8-target-evidence/direct-status.json`.
Ограничения: world compatibility подтверждает оператор; аварийное завершение
между обновлениями двух pointer files требует сверки state/binding. Незавершённый
rollback требует документированного ручного recovery. Retired runtime и backups
сохраняются без автоматической очистки. Forge остаётся непроверенной веткой.
+29
View File
@@ -0,0 +1,29 @@
# Phase 9 — завершена
Проверено 4 октября 2026: `mcpack release VERSION` реализует workflow из §22 плана.
Требует clean Git worktree, проверяет SemVer без build metadata, отсутствие
release/tag и Git identity; обновляет только версию pack, выполняет candidate build,
коммитит версию, собирает полный release из чистого commit, проверяет SHA256SUMS
и provenance, создаёт annotated tag. Push, публикация и deploy не выполняются.
76 тестов прошли. Новые проверки покрывают успешный выпуск, грязное дерево,
занятые release/tag, некорректные аргументы/версии, lock, candidate export failure,
final build failure, отклонённый Git commit hook и сохранение concurrent source edit.
Общий build fixture выделен без изменения прежних проверок build.
`scripts/verify-phase9.sh` проверил настоящий pinned packwiz/Compose в отдельном
Git clone: обе сборки 0.0.6, чистый финальный manifest, полные checksums, отдельный
version commit только для `pack.toml`, annotated `v0.0.6` на manifest commit,
отказ повторного выпуска и удаление временного output/lock. HEAD и status
основного workspace до/после совпали.
Артефакт:
`.tools/phase9-check.FX8NZe/clone/dist/minecraft-lab-neoforge-spike-0.0.6/`.
Доказательства: `.tools/phase9-check.FX8NZe/evidence/`,
`.tools/phase9-unit-tests.log`, `.tools/phase9-integration.log`.
Основной workspace по-прежнему содержит uncommitted исходники; его выпуск
корректно заблокирован до обычного коммита проекта. Версия исходного pack остаётся
0.0.5, основной Git не получил release commit/tag, рабочий сервер не изменён.
Recovery и ограничения описаны в [releases.md](releases.md). Синхронизация
удалённых tags остаётся обязанностью оператора; автоматического push нет.
+42
View File
@@ -0,0 +1,42 @@
# Выпуск релиза — Phase 9
```bash
./mcpack release 0.0.6
# Дополнительная конфигурация:
./mcpack release 0.0.6 --config platform/config.yaml
```
Команда требует чистый Git worktree, включая untracked files, активную ветку,
полный локальный clone и настроенные Git author/committer. Подготовьте исходники
обычным Git-коммитом заранее. Команда не коммитит остальные изменения проекта.
Версия — `MAJOR.MINOR.PATCH`, допускается prerelease (`0.1.0-rc.1`). Build metadata
с `+` пока не поддерживается форматом имён deployment. Версия должна отличаться
от текущей и не быть занята каталогом релиза или локальным tag `vVERSION`.
Удалённые tags автоматически не загружаются: синхронизация refs остаётся задачей оператора.
Workflow:
1. Проверяет Git, конфигурацию, версию и наличие release/tag; берёт локальный lock.
2. Меняет только top-level `version` в `pack.toml`, сохраняя остальной текст.
3. Собирает пробный артефакт в отдельном временном output с pinned packwiz/Compose.
4. При успехе коммитит только `pack.toml` с сообщением `Release VERSION`.
5. Собирает финальный полный release из чистого коммита, проверяет checksums и provenance.
6. Создаёт annotated tag `vVERSION` на тот же commit и выводит каталог/commit/tag.
Пробный output удаляется. Ошибка до коммита восстанавливает точный исходный
`pack.toml`; параллельные изменения сохраняются для разбора. Если финальная сборка
или создание tag завершается ошибкой, version commit остаётся, tag не создаётся
этой операцией. Финальный артефакт, если уже создан, остаётся для проверки.
Автоматического Git reset коммита, перезаписи существующего release/tag, push,
публикации или deployment нет.
После прерывания изучите HEAD, status, refs, `dist/` и `.tools/release.lock`.
Убедитесь, что процесс закончился, прежде чем убрать stale lock. После ошибки
финальной сборки исправьте причину и осознанно завершите выпуск из чистого коммита:
`./mcpack build --release`, затем проверьте manifest/SHA256SUMS и создайте annotated
`vVERSION` на manifest commit. Если артефакт уже существует, проверьте его целиком,
не запускайте повторный build поверх него. Tag создавайте только после проверки.
В текущем workspace пока незакоммиченные исходники: `release` корректно откажет.
Phase 9 проверена на отдельном source snapshot, с настоящими packwiz/Compose,
version commit и tag; основной Git, версия pack и рабочий сервер не менялись.
+47
View File
@@ -0,0 +1,47 @@
# Rollback — Phase 8
Команды работают по SSH с root из `platform/config.yaml`. При необходимости
передайте `--ssh-config FILE` и `--remote-node /absolute/path/to/node`.
```bash
./mcpack rollback
./mcpack rollback-pack 0.0.4
```
Без подтверждения это только чтение: active/previous, actual binding, состояния
операций и список snapshots с временем и исходным релизом. Нет загрузки agent,
блокировки, остановки сервера или сборки. Список не заменяет проверку snapshot.
```bash
./mcpack rollback-pack 0.0.4 --confirm-compatible
./mcpack rollback --backup SNAPSHOT_ID --accept-progress-loss
```
Первый режим запускает сохранённый полный релиз с текущим миром. Оператор
подтверждает совместимость модов и мира; SemVer её не доказывает. Ручные изменения
managed configs блокируют перезапись, если target не содержит ровно эти изменения.
Второй режим восстанавливает весь runtime и исходный релиз snapshot. Прогресс
после указанного времени исчезает из активного runtime, включая данные модов.
Архив, inventory и release проверяются до остановки; распаковка происходит в
отдельном каталоге. Текущий runtime переносится в `retired-runtimes/UUID/runtime`,
а не удаляется. Дополнительно перед обоими режимами создаётся проверенный backup
текущего состояния при остановленном сервере под общей блокировкой.
`.env` и секреты сохраняются. Явный `MC_BIND_ADDRESS` действует и для старого
Compose через временный port overlay; immutable release не переписывается.
Pointers обновляются только после успешного startup/health. Состояние записывается
в `rollback-state.json`, предыдущий deployment — в `operation-history/`.
После неудачного deploy snapshot recovery определяет применённый релиз по binding,
даже если active ещё указывает на предыдущий. Pack-only в таком состоянии запрещён.
При ошибке rollback сервер останавливается; автоматического запуска старой сборки
на изменённом мире нет. Незавершённый rollback блокирует следующие операции.
Для ручного восстановления после ошибки сначала изучите state, binding,
diagnostics и оба pointers. Сохраните current backup, retired runtime и staged
restore. Восстановите проверенный snapshot в отдельный root по [backups.md](backups.md),
с host settings этого сервера; удалите остановленный старый контейнер и проверьте
source release и health восстановленной копии. Завершать failed state можно лишь
после сверки фактического binding и pointers; удаление state само по себе не recovery.
Backups и retired runtime автоматически не очищаются.
+107
View File
@@ -0,0 +1,107 @@
# Server runtime — Phase 4
Полный релиз содержит готовый Docker Compose. `scripts/server.sh` управляет
им локально, проверяя artifact checksums, manifest, pinned Compose и
подтверждённую Minecraft/loader/image/Java комбинацию. Wrapper требует
Node/npm dependencies, Docker daemon и локальный Compose из bootstrap.
Сам payload можно запускать обычным Docker Compose на подготовленном host.
## Первоначальная настройка
Выбрать отдельный server root и готовый full release. Пример для второго
локального сервера, пока основной работает на 25565:
```bash
mkdir -p .tools/local-server
cp server/.env.example .tools/local-server/.env
chmod 600 .tools/local-server/.env
```
В локальном `.env` явно задать:
```dotenv
MC_EULA=TRUE
MC_MEMORY=2G
MC_PORT=25566
MC_BIND_ADDRESS=127.0.0.1
MC_UID=1000
MC_GID=1000
MC_PROJECT_NAME=minecraft-lab-local
```
`MC_EULA=TRUE` означает ваше принятие https://www.minecraft.net/en-us/eula.
Для текущего эксперимента оно уже получено. На новом host EULA принять
самостоятельно. UID/GID выбрать для владельца runtime на host; image
инициализирует права данных с этими значениями. Оператор должен иметь
доступ к Docker, server root и локальному `.env`.
Файл использует простые `KEY=value` без кавычек/interpolation. Пока разрешены
только перечисленные ключи. Его значения имеют приоритет над случайными
ambient MC_EULA/MEMORY/PORT/UID/GID из shell. Project можно выбрать также
через `MC_PROJECT_NAME` в shell; он должен оставаться стабильным для этого root.
```bash
export MC_ROOT="$PWD/.tools/local-server"
export MC_RELEASE_DIR="$PWD/dist/minecraft-lab-neoforge-spike-0.0.4"
scripts/server.sh config --quiet
scripts/server.sh up -d --wait --wait-timeout 600
scripts/server.sh ps
scripts/server.sh logs --tail 100
scripts/server.sh exec -T minecraft mc-health
```
Пути должны быть абсолютными; cwd не влияет на mounts. `MC_ENV_FILE` может
выбрать другой абсолютный файл настроек вне immutable release; по умолчанию
используется `MC_ROOT/.env`. Для второго runtime нужен отдельный стабильный
project и свободный порт. Wrapper отклоняет project, чьи существующие
контейнеры подключают другой `/data`.
## Lifecycle и данные
```bash
scripts/server.sh restart --timeout 120
scripts/server.sh up -d --wait --wait-timeout 120
scripts/server.sh stop
scripts/server.sh create --force-recreate
scripts/server.sh up -d --wait --wait-timeout 120
scripts/server.sh down
```
Restart/create/stop/down относятся к тому же выбранному релизу. `down`
удаляет контейнер и network, но сохраняет host runtime. Данные не находятся
в named volume: `MC_ROOT/runtime` монтируется в `/data` read-write.
Release и его `server/config` монтируются read-only. Онлайн-проверка аккаунтов
выключена. `MC_BIND_ADDRESS` в host `.env` выбирает IPv4 интерфейс:
по умолчанию 127.0.0.1; для remote LAN задать 0.0.0.0.
Первая инициализация разрешена только с пустым runtime (или `.gitkeep`).
В root создаётся `.mcpack-runtime.json`: project и SHA256 manifest выбранного
релиза. Binding записывается перед запуском и сохраняется даже при его
неудаче, чтобы повторный up продолжал ту же попытку. Это не active-release
и не отметка успешного deployment.
Смена релиза/project на связанном runtime и неявное принятие существующего
мира без binding запрещены. Backup/restore реализованы в Phase 5;
смена релиза требует будущего deploy workflow. Текущий старый Phase 0 мир управляется `scripts/spike-server.sh`;
миграция на новый wrapper в Phase 4 не выполняется.
Lifecycle-операции и весь цикл backup используют общую `.mcpack-runtime.lock`
в server root. Snapshot/recovery procedure — [backups.md](backups.md). После
принудительного убийства процесса lock может остаться; удалять только когда
соответствующий процесс точно завершён. `config` проверяет контракт без
запуска Docker и без создания runtime/binding. `up --wait` теперь проверяет
Minecraft status query и окно стабильной работы, включая restart loops.
Отдельная проверка — `scripts/server.sh health`; timeout/report/logs описаны
в [health.md](health.md).
Обычный Compose также работает с release payload:
```bash
MC_ROOT=/absolute/server-root MC_RELEASE_DIR=/absolute/full-release \
docker compose --project-name minecraft-lab-local \
--env-file /absolute/server-root/.env \
-f /absolute/full-release/server/compose.yaml up -d --wait
```
При прямом вызове Compose проверки/binding/lock wrapper не выполняются;
для локального lifecycle используйте `scripts/server.sh`.
+118
View File
@@ -0,0 +1,118 @@
# Phase 0: NeoForge, Minecraft 1.21.1
Целевая сборка работает без онлайн-проверки аккаунта. Пользователь выбрал
NeoForge на Minecraft 1.21.1; EULA принята для локального эксперимента.
Phase 0 завершена для NeoForge 21.1.252 на Minecraft 1.21.1. Импорт и вход
клиента пользователь подтвердил; оставшиеся серверные сценарии проверены
на том же loader. Это не подтверждает поддержку Forge.
## Закреплённая сборка
- Minecraft 1.21.1, NeoForge 21.1.252.
- Sophisticated Backpacks 3.26.7.2182 и Sophisticated Core 1.5.5.2363: обе стороны.
- Mouse Tweaks 2.26.1: только клиент.
- Source: `pack/`; версии и download hashes находятся в packwiz metadata.
- Инструменты и digest Docker image закреплены в `toolchain.lock.env`.
- Артефакт: `dist/minecraft-lab-neoforge-spike-0.0.1/minecraft-lab-neoforge-spike-0.0.1.mrpack`.
- SHA256: `67d6802ae30a1d42413dc44d76f3cc7f0a77885dbbea5329ab9c2e288bda2d4e`.
## Подтверждённые результаты
`scripts/spike-build.sh` экспортировал `.mrpack`. В `modrinth.index.json`
указаны зависимости Minecraft 1.21.1 и NeoForge 21.1.252; для Mouse Tweaks
записано `server: unsupported`.
`scripts/spike-server.sh up -d --wait --wait-timeout 600` успешно запустил
сервер. Первый запуск закончился таймаутом загрузки Maven-зависимостей;
повторный запуск того же артефакта завершился успешно. Это внешняя ошибка
загрузки, воспроизводимость установки без сети пока не подтверждена.
Сервер стал healthy; `mc-health` ответил с версией Minecraft 1.21.1.
Лог подтвердил NeoForge 21.1.252 и загрузку Backpacks/Core. В `runtime/mods/`
ровно два соответствующих JAR: клиентский Mouse Tweaks исключён.
В `runtime/server.properties` подтверждены `online-mode=false` и
`enforce-secure-profile=false`. Порт привязан к `127.0.0.1:25565`.
Windows TCP-проверка `127.0.0.1:25565` успешна. При первой попытке клиент
разрешил `localhost` в IPv6 `::1` и получил Connection refused; для текущей
публикации порта подключаться к `127.0.0.1:25565`.
После смены адреса пользователь подтвердил успешный вход в мир. Импорт в
Prism, запуск клиента с NeoForge и подключение без проверки аккаунта пройдены.
Мир, моды и сгенерированные конфиги доступны как обычные файлы в bind-mounted
`runtime/`.
Сгенерированные серверные конфиги NeoForge и Backpacks находятся в
`runtime/config/`, включая `neoforge-server.toml` и
`sophisticatedbackpacks-server.toml`. Нельзя предполагать, что серверный
конфиг обязательно создаётся в `world/serverconfig/`; политика конфигов
должна учитывать выбранную версию loader.
Локальные свидетельства (не коммитятся):
- `.tools/neoforge-evidence/mrpack-index.json`;
- `.tools/neoforge-evidence/first-start.log`;
- `.tools/neoforge-evidence/healthy-start.log`.
## Оставшиеся серверные сценарии: пройдены
Перед тестами сервер остановлен. Полный `runtime/` сохранён в
`backups/phase0-neoforge-baseline.tar.zst`; архив проверен чтением и SHA256.
Backup содержит мир после подтверждённого пользовательского входа.
Часть файлов мира принадлежит UID контейнера и недоступна в sandbox;
backup и чтение этих файлов выполнены с разрешённым повышением доступа.
Ошибка чтения файла должна прерывать backup, а не оставлять частичный архив
как успешный результат.
| Проверка | Результат |
| --- | --- |
| Recreate | `create --force-recreate` не изменил SHA256 `world/level.dat` до запуска; затем сервер healthy. |
| Смена версии | Релиз 0.0.2 заменил Backpacks 3.26.7.2182 на 3.26.6.2174; старый JAR отсутствовал, новая версия загружена. Это проверка замены версии, включая downgrade. |
| Удаление модов | Релиз 0.0.3 без модов запустился healthy; `runtime/mods/` пуст. |
| Pack config | Ручной `manual-runtime-edit` в управляемом `config/spike-marker.txt` заменён на `neoforge-pack-v2` при смене релиза. |
| Server override | С дополнительным Compose marker стал `server-override-v1`, несмотря на pack default и ручную правку. |
| Неуправляемые файлы | `config/unmanaged-probe.txt` сохранил `preserve-me` после смены релиза и удаления модов. |
| Defaultconfigs | Шаблон `removeErroringEntities=true` не заменил существующий `config/neoforge-server.toml` со значением false. После переноса текущего конфига вне runtime и запуска 0.0.2 на том же мире новый конфиг получил true. |
| Datapack | Добавленный в 0.0.2 `world/datapacks/phase0` выполнил load-функцию на существующем мире: в логе есть `[Phase0] datapack-v2-loaded`. В 0.0.3 управляемые файлы datapack удалены. |
| Startup failure | Отсутствующий `missing-neoforge-phase0.mrpack` привёл к exit 1; SHA256 остановленного `level.dat` до/после совпал. |
| Restore | Backup извлечён в отдельную директорию. Все 28 файлов мира совпали по путям и SHA256 с архивом до запуска; восстановленный `level.dat` совпал с исходным. Возвращён релиз 0.0.1, сервер healthy. |
Тестовые варианты воспроизводятся `bash scripts/spike-neoforge-variants.sh`
из корня на чистом checkout после bootstrap. Скрипт не меняет canonical
`pack/`, не запускает сервер и отказывается перезаписывать готовые релизы.
Артефакты проверки:
- 0.0.2 SHA256: `4f26f91f7b6e2db80caf522d370db37607ab46de5951e9cd8178f709b9e12ffd`.
- 0.0.3 SHA256: `ba0e2476ac7f095f13f5feed73584f38042f93457563dd00920c0211422494a2`.
- `.tools/neoforge-evidence/v2-start.log`, `v2-override-start.log`, `v3-start.log`.
- `.tools/neoforge-evidence/config-results.txt`, `controlled-failure.log`.
- `.tools/neoforge-evidence/baseline-level.sha256`, `before-failure-level.sha256`.
- `.tools/neoforge-evidence/restored-world-manifest.json`, `restore-verification.txt`, `restored-start.log`.
После тестов сохранённое исходное состояние вновь находится в `runtime/`,
промежуточное — в `.tools/neoforge-runtime-after-tests`. Активен 0.0.1 с
Backpacks 3.26.7.2182 и Core 1.5.5.2363, marker `neoforge-pack-v1`,
`online-mode=false`, `enforce-secure-profile=false`. Подключение:
`127.0.0.1:25565`; повторный импорт instance не требуется.
## Выводы для следующих фаз
Управляемые pack-конфиги заменяются при смене релиза, поэтому ручные правки
не являются устойчивым источником настроек. Серверные overrides должны быть
явными. Defaultconfigs в проверенной версии служат seed, а не механизмом
обновления существующего конфига. Runtime-generated конфиги сохраняются,
пока явно не включены в управляемые пути.
Политики replace/seed-only/preserve и ownership manifest пока не реализованы
в CLI: spike подтвердил поведение закреплённых компонентов. Контролируемая
ошибка проверена до старта игры; сохранность мира при произвольном сбое
мода или миграции формата мира этим тестом не подтверждается.
## Предварительный Fabric-эксперимент
Результаты предыдущего эксперимента сохранены в `fabric-spike-results.md`,
source — в `examples/fabric-spike/`. Они не заменяют проверки NeoForge/Forge.
Перед сменой loader Fabric-сервер остановлен, создан и проверен архив
`backups/phase0-fabric-before-neoforge.tar.zst`; старый runtime сохранён в
`.tools/fabric-runtime`. NeoForge запущен с новым миром.
Forge требует отдельного integration spike.