online-mode=false made the server skip the online-auth handshake entirely, so authlib-injector never validated sessions against Drasl: players got offline-hash UUIDs and no skin textures (everyone Steve). authlib-injector's whole purpose is to redirect the *online* handshake from Mojang to Drasl, so online-mode must be true; only the 1.21+ secure profile stays off (ENFORCE_SECURE_PROFILE=false + Drasl SignPublicKeys=false). Correct the conflation in CLAUDE.md and plan/05-minecraft.md. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
80 lines
3.6 KiB
Markdown
80 lines
3.6 KiB
Markdown
# Minecraft server — itzg NeoForge
|
|
|
|
## Summary
|
|
|
|
`itzg/minecraft-server:java21`, NeoForge 1.21.1, offline-mode + authlib-injector
|
|
pointing at drasl. Mods come from a local `./server-mods` volume (mounted at
|
|
`/mods`), NOT packwiz — see `04-mods.md`. itzg installs MC + NeoForge + libraries
|
|
on first boot and auto-syncs `/mods` → `/data/mods` every boot; the stack assumes
|
|
the internet is present (for the NeoForge installer), so `TYPE=NEOFORGE` boots
|
|
normally every time.
|
|
|
|
## Server config
|
|
|
|
```
|
|
TYPE=NEOFORGE, VERSION=1.21.1, NEOFORGE_VERSION=21.1.x
|
|
REMOVE_OLD_MODS=TRUE # ./server-mods is the authoritative mod set
|
|
volume ./server-mods:/mods:ro
|
|
```
|
|
|
|
`./server-mods` is populated by `tooling/sync-server-mods.sh` (the `both`/`server`
|
|
subset of `mods-sides.json`, copied from `$DISTRIBUTION_WEB_ROOT`). First boot
|
|
installs MC + NeoForge + libraries into the `mc_data` volume; every boot itzg
|
|
mirrors `/mods` into `/data/mods` and prunes removed ones (`REMOVE_OLD_MODS`).
|
|
|
|
## Auth / config
|
|
|
|
```
|
|
ONLINE_MODE=TRUE
|
|
ENFORCE_SECURE_PROFILE=FALSE # pairs with drasl SignPublicKeys=false
|
|
JVM_OPTS=-javaagent:/extras/authlib-injector.jar=http://auth.${BASE_DOMAIN}/authlib-injector
|
|
```
|
|
authlib URL uses the `auth` subdomain; resolves internally via the caddy alias.
|
|
(Server still needs the agent even though Fjord clients have it built in.)
|
|
|
|
**`ONLINE_MODE` MUST be true.** authlib-injector works by redirecting the
|
|
server's normal online-auth handshake from Mojang to Drasl. With
|
|
`ONLINE_MODE=FALSE` the server skips that handshake entirely → players get
|
|
offline-hash UUIDs, no session validation, and **no skins** (the server never
|
|
fetches textures from Drasl). The thing that must stay *off* for 1.21+ is the
|
|
**secure profile** (`ENFORCE_SECURE_PROFILE=FALSE` + Drasl `SignPublicKeys=false`),
|
|
not online-mode. Symptom of getting this wrong: everyone can join but everyone is
|
|
Steve, and `usercache.json` shows offline-hash UUIDs.
|
|
|
|
Memory: `INIT_MEMORY 4G`, `MAX_MEMORY 10G`, `USE_AIKAR_FLAGS TRUE`.
|
|
RCON enabled for mc-backup.
|
|
|
|
## Server operators (OP)
|
|
|
|
**Do NOT use the itzg `OPS` env var.** It resolves usernames to UUIDs via a
|
|
Mojang lookup, but this server is `ONLINE_MODE=TRUE` validated against Drasl —
|
|
Drasl assigns its own UUIDs (not Mojang, not the offline-hash). A Mojang-resolved UUID
|
|
would not match the player's real session UUID, so the op entry would be inert
|
|
(and could clobber a correct `ops.json` on reboot).
|
|
|
|
**Op via RCON instead.** The player must have connected at least once so their
|
|
real Drasl UUID is cached in `/data/usercache.json`; then `op <name>` writes that
|
|
cached UUID to `ops.json`. On the host:
|
|
|
|
```
|
|
ssh cochi
|
|
docker exec minecraft rcon-cli list # confirm cache / who's on
|
|
docker exec minecraft sh -c 'cat /data/usercache.json' # verify name→UUID cached
|
|
docker exec minecraft rcon-cli op <name>
|
|
docker exec minecraft sh -c 'cat /data/ops.json' # verify level 4 + right UUID
|
|
```
|
|
|
|
`ops.json` lives in the `mc_data` volume → persists across restarts; no reboot
|
|
needed (takes effect live). Current op: `oier`
|
|
(`7fbc9adb-bcf4-3913-9a99-46d46acd1009`), level 4, set 2026-06-21.
|
|
|
|
## Tasks
|
|
|
|
- [ ] Compose: authlib URL `http://auth.${BASE_DOMAIN}/authlib-injector`
|
|
- [ ] Compose: mount `./server-mods:/mods:ro` + `REMOVE_OLD_MODS=TRUE`
|
|
- [ ] Run `tooling/sync-server-mods.sh` before bring-up (populates `./server-mods`)
|
|
- [ ] Mount `./runtime` for authlib-injector.jar at `/extras`
|
|
- [ ] `depends_on`: drasl (authlib must resolve at boot)
|
|
- [ ] Confirm `ENFORCE_SECURE_PROFILE=FALSE` ↔ drasl `SignPublicKeys=false`
|
|
- [ ] Verify a client joins with their drasl skin
|