minecraft-server/docs/MIGRATION-PLAN-EXCELLENTSHOP.md
s8n 4116d67eaf feat(shop): stage ExcellentShop+CoinsEngine migration bundle
Replaces EZShop 1.0-SNAPSHOT (bespoke, sell-only, 27-slot) and Kiranhart
AuctionHouse 1.4.6 (ARR no-LICENSE, dupe history) with a single GPL-3
stack: ExcellentShop 5.0.1 + CoinsEngine 2.7.0 + nightcore 2.15.3.

Per SHOP-SYSTEM-DECISION.md (commit 9565f0b), Stack A wins on three
counts: GPL-3 source (vs ARR/proprietary), unified theme across shop
and AH, single-vendor support story under NightExpress.

Jars sourced from upstream Reposilite repo.nightexpressdev.com — same
artefacts a local mvn package would produce, just reproducible without
the alex9849 integration that breaks on TLS handshake. SHA256SUMS
committed for receipt; never re-fetched at swap time.

Bundle:
  build/    — three jars + SHA256SUMS (~2.1MB total)
  configs/  — post-first-run overrides (chest module off, /shop alias,
              AH 1% tax + BIN+bid + 10 listings matches Kiranhart)
  scripts/  — swap.sh, rollback.sh, lp-shop-migration.sh,
              lp-shop-rollback.sh, docker-compose.patch.yml

itzg integration: COPY_PLUGINS_SRC=/plugins-custom mount per
ITZG-CUSTOM-JAR-PERSISTENCE.md, plus REMOVE_OLD_MODS_EXCLUDE expanded
with the three new globs.

LP migration grants default-tier excellentshop.* + coinsengine.*
nodes; staff tier gets *.admin equivalents to legacy
auctionhouse.moderator. Group prefixes/suffixes untouched per
feedback_lp_prefixes_locked.md.

DOES NOT touch live /data/plugins/EZShop or /data/plugins/AuctionHouse
— staging only. Operator runs swap.sh in scheduled maintenance window.

Refs: SHOP-SYSTEM-DECISION.md, AUDIT-2026-05-07.md F-11,
ITZG-CUSTOM-JAR-PERSISTENCE.md, MIGRATION-PLAN-EXCELLENTSHOP.md.
2026-05-08 00:01:53 +01:00

137 lines
8 KiB
Markdown

# Migration Plan — EZShop + Kiranhart AH → ExcellentShop unified
**Date:** 2026-05-08
**Author:** s8n
**Supersedes:** AUDIT-2026-05-07 F-11; SHOP-SYSTEM-DECISION sec. 6 (now operational)
**Staging:** `staging/excellentshop/`
**Status:** Ready for operator-scheduled maintenance window.
---
## 1. Summary
Replace two plugins (EZShop 1.0-SNAPSHOT bespoke + Kiranhart AuctionHouse
1.4.6 ARR-no-LICENSE) with one GPL-3 stack:
ExcellentShop 5.0.1 + CoinsEngine 2.7.0, both built by NightExpress against
nightcore 2.15.3. All three jars are sourced from the upstream Reposilite
(`repo.nightexpressdev.com`) — source-of-truth identical to a local
`mvn package` from `github.com/nulli0n/ExcellentShop-spigot`, just reproducible
without the brittle `alex9849` integration that fails on TLS handshake.
Both modules share one theme, one perm tree, one update cadence. Chest Shop
module is disabled — not a future-of-server commit (QuickShop-Hikari path
already documented in SHOP-SYSTEM-DECISION).
## 2. Pre-flight (not in `swap.sh`, do these first)
1. **Heads-up window:** post in-game notice via `/pm news` minus 7d, 1d, 1h.
"Auction listings will be drained on YYYY-MM-DD HH:MM."
2. **Drain Kiranhart AH:** instruct players to claim/cancel listings.
Bid-in-progress items revert at the close of the bid window (max 2h,
per `bid-auction-duration: 7200`). Manual refund the residue to the
listing owner via `/eco give` after the window — record the audit
trail in `live-server/migration-2026-05-08-residue.log`.
3. **Snapshot live data:** invoke
`scripts/restic-backup-playerdata.sh` (already wired for nullstone).
Verify the snapshot via `restic snapshots --tag pre-shop-migration`.
4. **Verify staged bundle:**
```
cd staging/excellentshop/build
sha256sum *.jar # match the values committed alongside this doc
```
5. **Confirm operator window:** compose stop → up takes ~3 minutes;
ExcellentShop generates ~11 default shops + 400 items on first start
(~30s extra). Total expected downtime: 4-6 minutes best case,
12 minutes worst case (if itzg downloads stall).
## 3. Swap procedure
`scripts/swap.sh` runs as `user@nullstone` and performs:
| Step | Action |
|------|--------|
| 1 | `docker compose stop mc` |
| 2 | tar legacy `EZShop` + `AuctionHouse` plugin trees + jars to `/opt/docker/minecraft/plugins.tar.bak-<ts>` |
| 3 | privileged `rm -rf` of legacy trees and jars (uses `docker run --privileged --userns=host` per `feedback_docker_sudo_bypass`) |
| 4 | populate `/opt/docker/minecraft-custom-plugins/` with the three staged jars, chown 1000:1000 |
| 5 | replace `docker-compose.yml` with `docker-compose.patch.yml` (adds `COPY_PLUGINS_SRC=/plugins-custom`, the bind mount, and the new `REMOVE_OLD_MODS_EXCLUDE` glob list) — keeps a `.bak-<ts>` |
| 6 | `docker compose up -d mc` (first-start config generation runs here) |
| 7 | poll `mc-health` for up to 4 minutes |
| 8 | `docker cp` the staged config overrides into the now-populated plugin tree, run `excellentshop reload` + `coinsengine reload`, then run `lp-shop-migration.sh` over RCON to grant default-tier perms |
The compose patch is identical to the existing live compose **except** for
the three lines: `PLUGINS` (drops EZShop+AH URLs), `REMOVE_OLD_MODS_EXCLUDE`
(adds three new globs), and `COPY_PLUGINS_SRC` + matching volume mount.
## 4. Smoke test (post-swap, manual)
Run as a non-staff player and as a staff player:
- `/shop` → main category menu, 11 shops, GUI renders within 1s.
- `/sellall` → sells inventory, `+N coins` chat feedback, `/balance` updates.
- `/sellhand` → sells hand stack, hand cleared.
- `/ah` → AH GUI opens, list a test item via `/ah list 100`, see it in browse.
- `/ah` admin: `/ah admin remove <listing>` works, `/ah admin expire` works.
- `/balance`, `/pay`, `/eco` resolve through CoinsEngine via Vault bridge.
- Tail logs `docker logs -f minecraft-mc | grep -E 'nightcore|ExcellentShop|CoinsEngine|ERROR|WARN'` for 5 minutes after first player joins.
If any of the above fail, run `scripts/rollback.sh`.
## 5. Rollback
`scripts/rollback.sh` reverses everything: stops the container, removes
ExcellentShop+CoinsEngine+nightcore plugin trees and jars, clears the
custom-plugins bind dir, restores legacy `EZShop`+`AuctionHouse` from the
tar snapshot, restores the previous `docker-compose.yml`, restarts, and
runs `lp-shop-rollback.sh` over RCON to restore the legacy perm nodes.
Rollback total time: ~5 minutes.
## 6. Risk register
| # | Risk | Likelihood | Impact | Mitigation |
|---|------|-----------|--------|-----------|
| R1 | nightcore 2.15.3 incompatible with Purpur 1.21.11 (api-version 1.21 should match) | Low | High | Smoke-test on a throwaway container first; rollback path < 5 min if breakage |
| R2 | CoinsEngine 2.7.0 fails Vault bridge (EssentialsX expects Vault) | Medium | High | `Vault_Compatibility.Default: true` set in staged currency YAML; verify `/balance` in smoke step 4 |
| R3 | NightExpress reposilite goes dark before next maintenance | Low | Low | Jars are committed to the repo at `staging/excellentshop/build/` with sha256 receipts never re-fetched at swap time |
| R4 | Players keep typing `/auctionhouse` (legacy alias) | Certain | Low | `commands.yml` aliases `[ah, auctionhouse, market]` keep muscle memory |
| R5 | EZShop balance migration loss (if EZShop tracked any) | Very Low | Low | EZShop was sell-only with Vault balances live in EssentialsX, untouched by this swap |
| R6 | Live AH listings lost (residual stuck items) | Medium | Medium | Pre-flight step 2 drains, residue refund is documented |
| R7 | First-start delay > 4 min healthcheck window (large default catalog generation) | Low | Medium | `start_period: 240s` already set; if exceeded, manual `docker exec mc mc-health` poll |
| R8 | Compose typo locks server out of plugins | Low | High | `swap.sh` keeps `docker-compose.yml.bak-<ts>` for trivial revert; rollback restores it |
| R9 | `excellentshop.shop.use` not granted to default group → no players can `/shop` | Medium | High | `lp-shop-migration.sh` step 8 grants the full perm tree; verify with `/lp user <name> permission check excellentshop.shop.use` |
| R10 | Compose `cap_drop: ALL` strips a cap CoinsEngine needs | Low | Low | If `/data/plugins/CoinsEngine/` write fails, `cap_add: DAC_OVERRIDE` rescue documented in swap.sh comments |
## 7. Top 3 swap-window risks (operator brief)
1. **R9 (perm regression)** — if the LP migration RCON call drops mid-script,
players see `/shop` work but `/sellall` fail. Mitigation: re-run
`lp-shop-migration.sh` idempotently — every line is `permission set <node> true`.
2. **R7 (slow first-start)** — first start of a fresh ExcellentShop with the
bundled catalog generation takes ~30s extra and might trip the 240s
healthcheck on a cold pool. Mitigation: `swap.sh` step 7 polls manually
before declaring success.
3. **R2 (Vault bridge)** — if `/balance` returns 0 instead of the player's
actual balance, the Vault bridge from CoinsEngine is mis-wired. Mitigation:
`Vault_Compatibility.Default: true` is in the staged YAML; if still broken,
`/eco give <player> <prev-balance>` from the EssentialsX-side dump that
`restic-backup-playerdata.sh` already takes.
## 8. Estimated downtime
- **Best case:** 4 minutes (compose stop → up + health check, no log churn).
- **Typical:** 6 minutes (one extra stall on jar copy or LP RCON timeout).
- **Worst case:** 12 minutes (first-start catalog generation slow + manual smoke).
If the worst case is exceeded, abort and run `rollback.sh` — the data
snapshot from pre-flight step 3 is the source of truth.
## 9. Post-migration
- Update `AUDIT-2026-05-07.md` F-11: status → Resolved (date 2026-05-08).
- Update `docs/COMMANDS.md`: `/shop`, `/ah`, `/sellall`, `/sellhand`,
`/balance` all now route through ExcellentShop+CoinsEngine.
- Update `SYSTEM.md` plugin table: drop EZShop, drop AuctionHouse 1.4.6,
add ExcellentShop 5.0.1, CoinsEngine 2.7.0, nightcore 2.15.3.
- Schedule a follow-up audit in 7d to confirm no log spam, no economy
drift, no listing exploits.