# 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-` | | 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-` | | 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 ` 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-` 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 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 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 ` 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.