diff --git a/docs/DEFAULT-RANK-COMMANDS-2026-05-07.md b/docs/DEFAULT-RANK-COMMANDS-2026-05-07.md new file mode 100644 index 0000000..1dba0f8 --- /dev/null +++ b/docs/DEFAULT-RANK-COMMANDS-2026-05-07.md @@ -0,0 +1,265 @@ +# Default-rank command audit — 2026-05-07 + +**Test rig:** YOU500 (LuckPerms group `default`, weight 10). +**Reporter:** Operator says `/help` and "homestead/land claiming" don't work for him. + +This is a diagnostic document written before any fix. Section 6 ("Applied") +records what was changed live after operator sign-off. + +--- + +## 1. What's actually loaded on the box + +Plugins enabled at boot (from `Enabling X` lines in `docker logs`): + +``` +LuckPerms, Vault, ProtocolLib, FAWE, VoidWorldGenerator, SkinsRestorer, +PlaceholderAPI, Essentials, MiniPlaceholders, AuthMe, AuthLimbo, EZShop, +ProAntiTab, Homestead, CarbonChat, voicechat, CoreProtect, TAB, MiniMOTD, +HelpCommand, EpicGuard (errored), AuctionHouse, Chunky +``` + +**The `Lands` plugin is NOT loaded.** The directory `/data/plugins/Lands/` +exists with config + data files (probably leftover from a prior install) but +there is **no `Lands*.jar`** in `/data/plugins/`. Land-claiming is handled +exclusively by **Homestead** v5.2.0.0 (free GPL-style alternative; main class +`tfagaming.projects.minecraft.homestead.Homestead`). + +This means every reference to `/lands` in our configs is dead text: +- `HelpCommand/config.yml` page 2 advertises `/lands` +- `ProAntiTab/storage.yml` has `lands` in the default whitelist +- `Lands/config.yml` and friends are sitting unused + +--- + +## 2. Default-group LuckPerms snapshot + +From `lp export pat-debug` → `/data/plugins/LuckPerms/pat-debug.json.gz`, +the `default` group has only these nodes: + +| Node | Value | Notes | +|------|-------|-------| +| `essentials.motd` | **false** | suppress MOTD spam (intentional) | +| `prefix.10.&8[&2Adventurer&8]&r ` | false | prefix off (operator-managed elsewhere) | +| `skinsrestorer.command` | true | base SR command | +| `skinsrestorer.command.gui` | true | open SR GUI | +| `skinsrestorer.command.set` | true | `/skin set ` | +| `skinsrestorer.command.set.url` | true | `/skin url ` | +| `skinsrestorer.ownskin` | true | apply own MC skin | +| `skinsrestorer.player` | true | be a SR-recognised player | +| various `skinsrestorer.*` | false | restrict admin/edit/clear | +| `weight.10` | true | rank ordering | + +The `default` group has **no inheritance** — it's a flat group. Nothing +explicitly grants or denies `bukkit.command.help`, `essentials.help`, +`homestead.commands.region`, `homestead.commands.claim`, etc. + +For commands declared `default: true` in their plugin.yml, Bukkit grants +the perm to all players automatically — so the LP node table not listing +them is **expected and fine**. + +--- + +## 3. Why `/help` looks broken + +Both EssentialsX and HelpCommand register a `/help` command. Plugin enable +order (from logs): **Essentials** at 13:07:35, **HelpCommand** at 13:07:40. +Bukkit gives the original name to whoever registered first — Essentials +wins `/help`; HelpCommand's command becomes `helpcommand:help`. + +EssentialsX's `/help` runs fine for default players (no perm gate on the +command itself), BUT in `Essentials/config.yml`: + +```yaml +non-ess-in-help: true +hide-permissionless-help: true +``` + +`hide-permissionless-help: true` means Essentials hides every plugin's help +entries unless the player has `essentials.help.`. The default group +has none of those nodes, so YOU500 sees a near-empty Essentials help page — +indistinguishable from "/help is broken". + +Meanwhile our pretty branded help screen (with the racked.ru header, +`&e/lands`, `&e/claim`, etc) is in **HelpCommand's** config and reachable +only via `/helpcommand:help` or the `/hc` admin command. The help-text +that the welcome message and AuthMe login-hint advertise is therefore +unreachable from the bare `/help` token. + +**Root cause for `/help`:** plugin-conflict; Essentials shadows HelpCommand +on the bare `/help` token, and Essentials has a hide-by-default policy. + +Two viable fixes (pick one): + +- **(A) Re-route `/help` to HelpCommand via `commands.yml` alias.** Add: + ```yaml + aliases: + help: + - helpcommand:help $1- + ``` + This forces every `/help` invocation to hit HelpCommand's branded screen. + No LP changes needed. PAT whitelist already allows `help`. Cleanest + option — matches the rest of our alias style in `commands.yml`. + +- **(B) Grant `essentials.help` (and per-plugin children) to default.** + Keeps Essentials's help. Requires a long perm-list and won't show the + racked-styled page. Not recommended. + +We applied **option A** (see §6). + +--- + +## 4. Why "homestead/land claiming" doesn't work + +Homestead claim flow (from its plugin.yml + language file +`/data/plugins/Homestead/languages/en-US.yml`): + +1. `/hs create ` — create a region. Aliases: `/region`, `/rg`, + `/homestead`. The Homestead commands `claim` and `unclaim` operate + against the **target region** stored per-player. +2. `/hs set target ` — set which region the next `/claim` will + add the chunk to. Friendly hover-link is sent right after `/hs create`. +3. `/claim` — adds the chunk you stand in to the target region. +4. `/hs menu` — GUI for trust, flags, etc. + +`ProAntiTab/storage.yml` group `default` whitelist contains: +``` +help, rules, sethome, home, deletehome, claim, lands, tpaccept, tp, pay, +pm, bal, skin, skin url, skin set, shop, ah, auctionhouse, balance, baltop, +msg, reply, r, back, spawn, delhome, homes, warp, warps, list, login, register +``` + +It does **NOT** contain `region`, `rg`, `hs`, `homestead`, or `unclaim`. + +Because PAT runs with `turn-blacklist-to-whitelist: true`, **every command +not in this list is blocked for default players** — including all four +entry-points to Homestead. So a default player can run `/claim` (and PAT +allows it), but `/claim` returns "set a target region first" because they +were never able to run `/hs create`. The land-claiming workflow is +unreachable. + +The dead `/lands` entry in the whitelist does no harm but advertises a +command that doesn't exist on this server. + +**Root cause for homestead claiming:** PAT whitelist gap. Operator added +`claim` and `lands` in a previous session but never added the Homestead +master commands. Three of the four entry-points are blocked. There is no +LuckPerms denial — Homestead's plugin.yml declares `homestead.commands.region.*` +and `homestead.actions.regions.*` as `default: true`, so a default player +already has all the perms; only PAT is in the way. + +--- + +## 5. Fix proposed + +### 5a. PAT — add Homestead entry-points to default whitelist + +Append to `/data/plugins/ProAntiTab/storage.yml` under +`groups.default.commands` (do NOT remove existing entries): + +```yaml + - region + - rg + - hs + - homestead + - unclaim +``` + +Reload via RCON: `pat reload`. + +Safe because: +- These are the bona-fide Homestead user commands. All four resolve to the + same Homestead `region` command (one base + three aliases) plus `unclaim`. +- All five commands are declared `default: true` in Homestead's plugin.yml + — no privilege uplift. +- We are only ADDING, never REMOVING, so existing whitelist semantics are + preserved. + +Optional cleanup (not applied): the dead `lands` entry in the whitelist is +harmless and we leave it for now in case the Lands jar is restored. + +### 5b. `/help` routing — alias `/help` → `/helpcommand:help` + +Append to `/data/commands.yml` under `aliases:`: + +```yaml + # /help = our branded HelpCommand page (Essentials shadows /help otherwise) + help: + - helpcommand:help $1- +``` + +Apply via RCON: `minecraft:reload` is unsafe; instead let it take effect +on next restart. Since this is a small text-only change and operator is +running active live tests, we apply via a more surgical path: send +`/reload confirm` only if operator wants immediate effect; otherwise +queue for next deploy. + +(Decision in §6: we wrote the alias and asked the operator to verify +without forcing a reload.) + +Safe because: +- HelpCommand's `/help` has no permission requirement (its plugin.yml + declares `commands.help` with no `permission:` field). +- PAT whitelist already allows `help` → still allowed after alias. +- Does not change behaviour for ops/admins meaningfully — they get the + branded page like everyone else. + +--- + +## 6. Applied (live, after operator sign-off) + +### 6a. PAT whitelist (applied) + +Added five lines under `groups.default.commands` in +`/data/plugins/ProAntiTab/storage.yml`: + +``` +- region +- rg +- hs +- homestead +- unclaim +``` + +Reload: `echo 'pat reload' | docker exec -i minecraft-mc rcon-cli` + +Verification: `pat reload` returned no errors; YOU500 to confirm +`/hs create test1` succeeds. + +### 6b. `/help` alias (applied) + +Added to `/data/commands.yml` under `aliases:`: + +```yaml + help: + - helpcommand:help $1- +``` + +Effect requires server restart or `/reload confirm` (Bukkit aliases are +loaded once at startup). Operator decides when to bounce. + +### 6c. LuckPerms changes + +**None needed.** The Homestead and HelpCommand commands are all open by +plugin.yml defaults. The earlier hypothesis of a `false` override on +`bukkit.command.help` or `lands.command.claim` was disproved by the LP +export — the only `false` overrides on `default` are `essentials.motd` +(intentional) and SkinsRestorer admin-action denies (intentional). + +Per workspace policy in `feedback_lp_prefixes_locked.md`, prefixes/suffixes +were not touched. + +--- + +## 7. Open items + +- The empty `/data/plugins/Lands/` config tree is dead weight. Either + reinstall the Lands jar (if operator wants the premium plugin) or + archive the directory and remove the dead `lands` line from PAT + whitelist. Not blocking. +- HelpCommand page 2 still says `&e/lands` — should be updated to + `&e/hs create ` or similar to match reality. Not blocking, but + filed in `ROADMAP.md`. +- Consider granting default group some `essentials.help.` nodes + if we ever want fallback `/help` to be useful. Low priority while alias + is in place. diff --git a/docs/PLAYER-SMOKE-TEST.md b/docs/PLAYER-SMOKE-TEST.md new file mode 100644 index 0000000..aebd8f2 --- /dev/null +++ b/docs/PLAYER-SMOKE-TEST.md @@ -0,0 +1,140 @@ +# Player smoke-test — racked.ru + +A regression checklist for "things a default-rank player must be able to do". +Pair with `scripts/test-default-perms.sh` to spot LP/PAT regressions before +asking the test rig to log in. + +**Test rig:** YOU500 (LuckPerms group `default`, weight 10). He stays on +`default` permanently — never elevate him; he is the canary. Operator +gear-loads him via RCON `give` commands; that's fine and doesn't change +his perms. + +**When to run:** before any of these: +- plugin update (jar swap) +- LuckPerms group/perm change (anything affecting `default`, `settler`, or inheritance) +- ProAntiTab `storage.yml` edit (whitelist add/remove) +- `commands.yml` or `help.yml` edit +- AuthMe / AuthLimbo config change +- After server restart following a config-only change + +--- + +## A — Authentication path (AuthLimbo + AuthMe) + +| # | Step | Expected | Pass? | +|---|------|----------|-------| +| A1 | Log in cold (server restart, fresh session) | Lands at `auth_limbo` (0.5 / 128 / 0.5) facing void | | +| A2 | Type `/login ` | `[AuthLimbo] Restoring YOU500 to world(...)` in logs; teleported back to last location | | +| A3 | Movement keys before `/login` | No movement; chat-prompt visible | | +| A4 | Re-login within session timeout | Auto-login, no prompt | | + +## B — Help and discovery + +| # | Step | Expected | Pass? | +|---|------|----------|-------| +| B1 | `/help` | Branded HelpCommand page 1 (racked.ru header, list of basic commands) | | +| B2 | `/help 2` (or arrow `>>`) | Page 2 with /bal, /pay, /hs, /shop entries | | +| B3 | `/rules` | Server rules from RULES.md displayed | | +| B4 | `/list` | Player count + groups (default/Admins) | | + +## C — Skin (SkinsRestorer) + +| # | Step | Expected | Pass? | +|---|------|----------|-------| +| C1 | `/skin set ` (e.g. `/skin set Notch`) | Skin updates within ~3s | | +| C2 | `/skin url ` | Custom URL skin applied | | +| C3 | `/skins` (GUI) | Skin browser opens | | +| C4 | `/skin clear` | **Should be denied** (admin-only) | | + +## D — Land claiming (Homestead) + +| # | Step | Expected | Pass? | +|---|------|----------|-------| +| D1 | `/hs create test1` | "Successfully created a new region: test1" + clickable hover-link | | +| D2 | `/hs set target test1` | "test1 is now set as the target region" | | +| D3 | Stand in unclaimed chunk → `/claim` | "This chunk is now part of the region test1" | | +| D4 | `/unclaim` (in same chunk) | "This chunk has been unclaimed" | | +| D5 | `/hs menu` | Region GUI opens | | +| D6 | `/region info` (or `/rg info`) | Region data printed | | + +If D1 returns "Unknown command" or the silent-PAT-block, the PAT whitelist +regressed — see `docs/DEFAULT-RANK-COMMANDS-2026-05-07.md` §5a. + +## E — Homes & teleport (Essentials) + +| # | Step | Expected | Pass? | +|---|------|----------|-------| +| E1 | `/sethome base` | "Home set" | | +| E2 | `/home base` | TP to base | | +| E3 | `/homes` | List of saved homes | | +| E4 | `/deletehome base` | Home removed | | +| E5 | `/spawn` | TP to spawn | | +| E6 | `/back` | TP to prior location | | +| E7 | `/tp ` | Sends TPA request (aliased) | | +| E8 | `/tpaccept` | Accept incoming TPA | | + +## F — Economy (Vault + EZShop + AuctionHouse) + +| # | Step | Expected | Pass? | +|---|------|----------|-------| +| F1 | `/bal` | Balance shown | | +| F2 | `/baltop` | Top-balance leaderboard | | +| F3 | `/pay ` | Transfer succeeds (or "insufficient funds") | | +| F4 | `/shop` | EZShop GUI opens | | +| F5 | `/ah` (or `/auctionhouse`) | AuctionHouse GUI opens | | + +## G — Chat (CarbonChat) + +| # | Step | Expected | Pass? | +|---|------|----------|-------| +| G1 | Plain chat message | Visible to others, prefix per LP rank (Adventurer for default, but operator manages prefix display elsewhere) | | +| G2 | `/msg hi` | DM delivered, both sides see it | | +| G3 | `/r hi` | Reply works after a DM | | +| G4 | `/pm hi` (alias) | Same as `/msg` | | + +## H — Negative tests (must NOT work for default) + +| # | Step | Expected | Pass? | +|---|------|----------|-------| +| H1 | `/op` | "Unknown command" or PAT-block | | +| H2 | `/gamemode creative` | Denied | | +| H3 | `/give @s diamond 64` | Denied | | +| H4 | `/lp ...` | PAT-blocks at execution (lp is global-whitelisted but LP itself denies non-ops) | | +| H5 | `/hsadmin reload` | Denied (op-only by Homestead plugin.yml) | | +| H6 | `/skin clear` | Denied (LP `false` override) | | + +## I — Voicechat + +| # | Step | Expected | Pass? | +|---|------|----------|-------| +| I1 | Connect with simple-voice-chat client | Mic + speaker work | | +| I2 | Group chat | Spatial audio normal | | + +--- + +## Operator workflow + +1. Run `bash scripts/test-default-perms.sh > /tmp/perms-before.txt` (snapshot). +2. Make config / plugin / LP change. +3. Run `bash scripts/test-default-perms.sh > /tmp/perms-after.txt`. +4. `diff /tmp/perms-before.txt /tmp/perms-after.txt` — anything unexpected + means the change had collateral effects on the default group. +5. Have YOU500 walk through sections A → I above. Mark Pass/Fail. +6. If anything regressed, see `docs/DEFAULT-RANK-COMMANDS-2026-05-07.md` + for last-known-good state and fix recipes. + +The diff in step 4 is the cheap pre-flight; the YOU500 walk-through is the +authoritative regression test. + +--- + +## What we explicitly are NOT testing here + +- **Rank-prefix display** — operator manages LP prefixes/suffixes manually + (locked behaviour per workspace memory). YOU500 will show "Adventurer" + with `false` weight; that's intentional and not a regression. +- **Premium Lands plugin** — the `/data/plugins/Lands/` config tree exists + but the jar is not loaded. If/when re-installed, this checklist needs a + Lands section. +- **AuthMe registration of new accounts** — separate flow, not part of the + default-rank smoke test. diff --git a/scripts/test-default-perms.sh b/scripts/test-default-perms.sh new file mode 100755 index 0000000..c449338 --- /dev/null +++ b/scripts/test-default-perms.sh @@ -0,0 +1,144 @@ +#!/usr/bin/env bash +# scripts/test-default-perms.sh +# +# Snapshot the things that influence what a default-rank player on +# racked.ru can do. Designed for diffing across config changes. +# +# Output: a deterministic dump (sorted where possible) of: +# - LuckPerms `default` group nodes +# - ProAntiTab `default` group whitelist +# - PAT global command whitelist +# - Plugin enable order from the most recent boot +# - Aliases defined in commands.yml +# +# Runs from the host (laptop or nullstone). Reads files out of the +# minecraft-mc container; parses YAML / JSON on the host (PyYAML and +# python3 are not present in the Paper container image). +# +# Usage (from any host with SSH access to nullstone): +# bash scripts/test-default-perms.sh > snapshot.txt +# bash scripts/test-default-perms.sh --remote > snapshot.txt # explicit ssh +# +# Then `diff` two snapshots taken before/after a change. +# +# Pairs with docs/PLAYER-SMOKE-TEST.md. + +set -euo pipefail + +CONTAINER="${MC_CONTAINER:-minecraft-mc}" +DATA="${MC_DATA:-/data}" +SSH_TARGET="${MC_SSH:-user@192.168.0.100}" + +# If we're running on the box itself (docker is local), skip SSH. +mode="local" +if ! command -v docker >/dev/null 2>&1 || ! docker ps --format '{{.Names}}' 2>/dev/null | grep -qx "$CONTAINER"; then + mode="ssh" +fi +if [[ "${1:-}" == "--remote" ]]; then + mode="ssh" +fi + +# read_file -> stdout = file contents +read_file() { + local path="$1" + if [[ "$mode" == "ssh" ]]; then + ssh -o BatchMode=yes "$SSH_TARGET" "docker exec $CONTAINER cat $path" + else + docker exec "$CONTAINER" cat "$path" + fi +} + +# rcon -> stdout = response (LP exports go to file, not stdout) +rcon() { + local cmd="$1" + if [[ "$mode" == "ssh" ]]; then + ssh -o BatchMode=yes "$SSH_TARGET" "echo '$cmd' | docker exec -i $CONTAINER rcon-cli" + else + echo "$cmd" | docker exec -i "$CONTAINER" rcon-cli + fi +} + +# docker_logs -> stdout = full container log +docker_logs() { + if [[ "$mode" == "ssh" ]]; then + ssh -o BatchMode=yes "$SSH_TARGET" "docker logs $CONTAINER 2>&1" + else + docker logs "$CONTAINER" 2>&1 + fi +} + +# read_lp_export -> stdout = JSON (decompressed) of latest snapshot +read_lp_export() { + rcon 'lp export default-perms-snapshot' >/dev/null + sleep 1 + local path="${DATA}/plugins/LuckPerms/default-perms-snapshot.json.gz" + if [[ "$mode" == "ssh" ]]; then + ssh -o BatchMode=yes "$SSH_TARGET" "docker exec $CONTAINER bash -c 'gunzip -c $path'" + else + docker exec "$CONTAINER" bash -c "gunzip -c $path" + fi +} + +heading() { + printf '\n========== %s ==========\n' "$1" +} + +heading "Mode" +echo "$mode (target: ${SSH_TARGET}, container: ${CONTAINER})" + +# ---------- LuckPerms default-group export ---------- +heading "LuckPerms default-group nodes" +read_lp_export | python3 -c ' +import json, sys +try: + data = json.load(sys.stdin) +except Exception as e: + print(f"(failed to parse LP export: {e})") + sys.exit(0) +default = data.get("groups", {}).get("default", {}).get("nodes", []) +for node in sorted(default, key=lambda n: (n.get("type",""), n.get("key",""))): + t = node.get("type","?") + k = node.get("key","?") + v = node.get("value","?") + print(f"{t:12s} {k} = {v}") +' + +# ---------- ProAntiTab whitelist ---------- +heading "ProAntiTab — global commands" +read_file "${DATA}/plugins/ProAntiTab/storage.yml" | python3 -c ' +import sys, yaml +data = yaml.safe_load(sys.stdin) or {} +for cmd in sorted(data.get("global", {}).get("commands", []) or []): + print(cmd) +' + +heading "ProAntiTab — default-group commands" +read_file "${DATA}/plugins/ProAntiTab/storage.yml" | python3 -c ' +import sys, yaml +data = yaml.safe_load(sys.stdin) or {} +default = (data.get("groups", {}) or {}).get("default", {}) or {} +priority = default.get("priority", "?") +print("# priority: " + str(priority)) +for cmd in sorted(default.get("commands", []) or []): + print(cmd) +' + +# ---------- commands.yml aliases ---------- +heading "commands.yml aliases" +read_file "${DATA}/commands.yml" | python3 -c ' +import sys, yaml +data = yaml.safe_load(sys.stdin) or {} +aliases = data.get("aliases", {}) or {} +for name in sorted(aliases.keys()): + target = aliases[name] + print(f"{name}: {target}") +' + +# ---------- Plugin enable order (latest boot) ---------- +heading "Plugin enable order — latest boot" +docker_logs \ + | grep -E "Enabling [A-Z]" \ + | tail -25 \ + | sed -E 's/^\[[0-9:]+\] \[Server thread\/INFO\]: //' + +heading "Snapshot complete: $(date -Is)"