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

8 KiB

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.