minecraft-server/docs/ROADMAP.md
s8n-ru 0dad38e02e Initial commit: racked.ru Minecraft server config snapshot
Captures live config state of nullstone Purpur 1.21.11 server:
- docker-compose.yml (itzg/minecraft-server image, MODRINTH_PROJECTS + PLUGINS lists)
- All plugin configs under live-server/plugins/ (no DBs, no jars, no world data)
- Server core: bukkit.yml, spigot.yml, purpur.yml, paper-global.yml, paper-world-defaults.yml, server.properties

Excluded via .gitignore:
- World data (world/, world_nether/, world_the_end/, auth_limbo/)
- Sensitive: AuthMe DB (password hashes), Lands DB, CoreProtect DB, Essentials userdata
- Jars (auto-fetched), logs, caches, .paper-remapped
2026-04-30 18:33:38 +01:00

6.9 KiB

Roadmap — plugin acquisition overhaul

Goal: replace runtime env-driven plugin downloads with a reproducible, source-of-truth-first acquisition pipeline. Make the server fully open source, fully auditable, fully reproducible.

Problem (current state)

Plugins are pulled at every container boot via MODRINTH_PROJECTS + SPIGET_RESOURCES env vars in docker-compose.yml. Pain points hit during 2026-04-27 deploy:

  • Slug ≠ namevault, protocollib not on Modrinth at expected slug. Three boot loops to discover.
  • Channel mismatch — GrimAC alpha-only, WorldEdit beta-only on bleeding-edge MC versions. Default release filter silently rejected. Two more boot loops.
  • Wrong env varMODRINTH_DEFAULT_VERSION_TYPE is for modpack flow; MODRINTH_PROJECTS flow needs MODRINTH_PROJECTS_DEFAULT_VERSION_TYPE. One more boot loop.
  • VERSION=LATEST + Purpur — Purpur 26.x versioning scheme confused itzg, sent "26.1.2" as MC version to Modrinth API; EssentialsX query returned no files.
  • No lockfilelatest drifts daily. No checksum, no audit trail.
  • No pre-flight — every typo is a 30s container restart cycle.
  • License opacity — no automated check that plugins are FOSS-compatible before adding.
  • REMOVE_OLD_MODS=*.jar wipes manually-placed jars on every boot, hostile to manual-only plugins (LoginSecurity, MarriageMaster, etc).

Acquisition order — proposed

  1. GitHub Releases (primary)
  2. Hangar (PaperMC official)
  3. Modrinth
  4. Spiget / SpigotMC
  5. Manual jar (last resort, premium/dead)

Why GitHub first

  • Source-truth: jar built from tag, signed commit, reproducible.
  • License visible — repo LICENSE file. FOSS audit trivial.
  • Stable URL pattern: github.com/<owner>/<repo>/releases/download/<tag>/<asset>.jar.
  • API: api.github.com/repos/<owner>/<repo>/releases/latest — JSON, version + asset URLs + checksums.
  • No platform lock-in (Modrinth/Hangar can delist; GH source survives).
  • Most Bukkit plugins ARE on GitHub — Modrinth/Hangar often just mirror.

Design

plugins.yml (manifest, committed)

plugins:
  - name: LuckPerms
    sources:
      - github: { owner: LuckPerms, repo: LuckPerms, asset_pattern: "LuckPerms-Bukkit-*.jar" }
      - modrinth: luckperms
    pin: latest                     # or "5.5.20" or sha256:abc...

  - name: ProtocolLib
    sources:
      - github: { owner: dmulloy2, repo: ProtocolLib, asset_pattern: "ProtocolLib.jar" }
      - spiget: 1997

  - name: Vault
    sources:
      - github: { owner: MilkBowl, repo: Vault, asset_pattern: "Vault.jar" }

  - name: WorldEdit
    sources:
      - github: { owner: EngineHub, repo: WorldEdit, asset_pattern: "worldedit-bukkit-*.jar" }
      - hangar: { author: EngineHub, project: WorldEdit }
      - modrinth: worldedit
    channel: beta

  - name: LandClaimPlugin
    sources:
      - modrinth: landclaimplugin

  - name: LoginSecurity
    sources:
      - manual: ./manual-jars/LoginSecurity-3.3.1.jar
    license: GPL-3.0
    upstream_url: https://www.spigotmc.org/resources/loginsecurity.19362/

plugins.lock (generated, committed)

LuckPerms-Bukkit-5.5.20.jar  sha256:abc...  github:LuckPerms/LuckPerms@v5.5.20
ProtocolLib.jar              sha256:def...  github:dmulloy2/ProtocolLib@5.4.0
...

scripts/fetch-plugins.sh (resolver)

Runs before docker compose up. Pseudo:

for plugin in plugins.yml; do
  for src in plugin.sources; do          # fallback chain — first hit wins
    case src.type in
      github)   asset=$(gh-api releases/latest); curl -L -o $asset ;;
      hangar)   curl hangar.papermc.io/api/v1/projects/...     ;;
      modrinth) curl api.modrinth.com/v2/project/$slug/version ;;
      spiget)   curl api.spiget.org/v2/resources/$id/download  ;;
      manual)   cp $path                                       ;;
    esac
    [ $? -eq 0 ] && break
  done
  sha256sum $jar >> plugins.lock
done

Output: plugins/*.jar directory ready to bind-mount, plus plugins.lock for diff/audit.

Compose changes

volumes:
  - /opt/docker/minecraft:/data
environment:
  REMOVE_OLD_MODS: "false"        # plugins/ pre-populated, don't wipe
  # delete: MODRINTH_PROJECTS, SPIGET_RESOURCES, MODRINTH_PROJECTS_DEFAULT_VERSION_TYPE

itzg image becomes pure runtime. Plugin acquisition is a separate, testable, reproducible build step.

Phased rollout

Phase 1 — pin everything (1 hour)

Keep itzg env-driven. Replace slug with slug:VERSION_ID in MODRINTH_PROJECTS. Use id:VERSION in SPIGET_RESOURCES. No more latest drift.

Acceptance: docker compose up -d produces identical plugin set on any host, any day.

Phase 2 — fetch script + manifest (1 day)

  • Write plugins.yml w/ all current plugins + sources.
  • Write scripts/fetch-plugins.sh (bash, jq, curl, gh CLI).
  • Write plugins.lock first run, commit it.
  • Strip MODRINTH_PROJECTS/SPIGET_RESOURCES from compose; REMOVE_OLD_MODS: false.
  • Document new deploy: ./scripts/fetch-plugins.sh && docker compose up -d.

Acceptance: plugins/ populated from GH-first, lock committed, deploy reproducible.

Phase 3 — CI automation (1 day)

  • GH Action daily: fetch-plugins.sh --check-updates → open PR per update, body has changelog link.
  • GH Action per-PR: license audit (/repos/{owner}/{repo}/license → SPDX id → LICENSES.md).
  • Renovate-style auto-merge for patch updates (config-gated).

Acceptance: plugin updates land via PR, license audit in CI, no manual fetches.

Tooling to evaluate first

Tool Status Verdict
mcpkg exists, immature reuse if active
packwiz mod-focused, Modrinth/Curse adapt?
paper-plugin-manager Hangar client use as Hangar source
Custom bash + jq + gh + curl trivial to build likely fastest

Probably 200 lines of bash beats adopting an unmaintained tool.

Side benefits

  • License audit — generated LICENSES.md proves the stack is FOSS.
  • Pre-flightfetch-plugins.sh --check validates manifest in CI before merge, no boot-time surprise.
  • Offline deploy — pre-baked plugins/ dir + tarball = air-gap deploy possible.
  • Forks — easy to swap LandClaimPlugin upstream → your own fork by changing one line in plugins.yml.

Open questions

  • License whitelist policy: GPL-3, MIT, Apache-2 OK? AGPL? Proprietary?
  • Update cadence: daily auto-PR, weekly, manual?
  • Pin granularity: per-plugin tag, sha256 hash, or commit SHA?
  • Failure mode if a source delists a pinned version: pin migration script?
  • Manual-jar storage: in-repo manual-jars/ (license risk) or separate private repo?

Status

  • 2026-04-27 — roadmap drafted post-deploy painshare. Not started.
  • 2026-04-28 — Phase 1 (pin versions) still pending. REMOVE_OLD_MODS bug discovered: itzg disables it when PLUGINS env set, so manual jars are safe. Phase 2 design finalized here. No code yet.