Compatibility
Minecraft: Java Edition
Platforms
Tags
Creators
Details
ClientPolicy
ClientPolicy identifies the client mods that announce themselves to your server, compares them against a policy profile you define, and applies a LOG → WARN → KICK ladder. Players can read the rules with /clientpolicy rules, so nobody gets kicked by a rule they were never shown.
No client-side mod is required. Vanilla, Fabric, Forge and NeoForge clients all work.
⚠️ Read this first: what ClientPolicy is not
ClientPolicy is not an anti-cheat.
Detection works by looking at three things the client volunteers:
- the plugin messaging channels it registers,
- its
minecraft:brandstring, - the presence of a mod loader handshake.
A mod that never talks to the server is invisible to this method. X-ray, killaura, reach, freecam, auto-clickers and similar cheats register no channels and cannot be detected here.
What ClientPolicy does see reliably: Litematica, Xaero's Minimap/World Map, JourneyMap, VoxelMap, WorldEditCUI, Baritone, ReplayMod, Distant Horizons, Simple Voice Chat, Vivecraft, LabyMod, Lunar Client, and anything else you teach it.
This limit is repeated in config.yml, in /clientpolicy check and in /clientpolicy rules, on purpose. A policy tool that lets admins believe they have cheat detection is worse than no tool at all.
✨ Features
- 🔎 Fingerprint engine — channel registrations are buffered from the login moment, so records arriving before
PlayerJoinEventare never lost. Brand and mod loader handshake are read alongside them. - 📋 Signature database —
signatures.ymlmaps a mod id to channel patterns (wildcards supported, e.g.litematica:*), a brand regex and a category (MINIMAP, SCHEMATIC, AUTOMATION, PERFORMANCE, UTILITY). - 🛡️ Policy profiles —
BLOCKLIST(only ids indenyare violations) orALLOWLIST(anything not inallowis a violation). Players resolve to a profile throughclientpolicy.profile.<name>, falling back todefault-profile. - 📶 Tiered enforcement — a per-profile ladder: first hits
WARN, later hitsKICK, with a configurable escalation threshold and counter reset window.clientpolicy.bypassexempts a player while still recording the detection. - ⏱️ Delayed and repeated scanning — the first evaluation runs
scan-delay-ticksafter join, then every newly registered channel triggers another evaluation forrescan-window-seconds. Registering late does not dodge enforcement. - 👀 Player transparency —
/clientpolicy rulesshows a player the allowed and denied list of their own profile, grouped by category. Kick messages name the offending mod and point back at the rules. - 🧪 Discovery mode —
/clientpolicy inspect <player>prints raw channels, brand, matched signatures and the channels that matched nothing, so you can write your own signatures. - 🗒️ Audit trail — every detection and action is written to SQLite and read back with
/clientpolicy history <player>, including offline players in the server's local cache. - 🧯 Unknown-channel policy — channels matching no signature get their own action (default: log only), which keeps
ALLOWLISTprofiles from kicking people over harmless plumbing.
Deliberately out of scope: behavioural cheat detection, a client companion mod, resource pack enforcement, mod file hash verification, a web panel, MySQL.
✅ Step one: verify your signatures
Channel names change between Minecraft versions and mod releases. The shipped signatures.yml is a starting set, not ground truth. Confirm every entry you care about:
- Join with the mod installed on your own client.
- Run
/clientpolicy inspect <yourname>and read the Unrecognised channels section. - Copy those channels into a
signatures.ymlentry. /clientpolicy reload.
Do this before switching any profile to ALLOWLIST. In allowlist mode, an unverified signature list means kicking players for channels you simply never taught the plugin about.
/clientpolicy check is the same view limited to a player's own client, and is safe to leave available to everyone — it is what makes the policy auditable by the people it applies to.
🎮 Commands
Root command /clientpolicy, alias /cp.
| Command | Description | Permission |
|---|---|---|
/clientpolicy rules |
The allowed/denied mod list of your profile, grouped by category | clientpolicy.rules (everyone) |
/clientpolicy check |
What the server currently sees on your own client | clientpolicy.check (everyone) |
/clientpolicy inspect <player> |
Raw channels, brand, matched signatures, scan state | clientpolicy.admin (op) |
/clientpolicy history <player> [limit] |
Past detections and actions (default 10, max 100) | clientpolicy.admin |
/clientpolicy profile <player> |
Which profile a player resolved to, and why | clientpolicy.admin |
/clientpolicy rescan <player> |
Re-apply the policy immediately, ladder included | clientpolicy.admin |
/clientpolicy reload |
Reload config.yml and signatures.yml |
clientpolicy.admin |
Permissions
| Node | Default | Meaning |
|---|---|---|
clientpolicy.rules |
everyone | Use /clientpolicy rules |
clientpolicy.check |
everyone | Use /clientpolicy check |
clientpolicy.admin |
op | inspect, history, profile, rescan, reload |
clientpolicy.bypass |
nobody | Exempt from every action. Detections are still recorded and staff are still notified. |
clientpolicy.notify |
op | Receive a chat notification on every violation |
clientpolicy.profile.<name> |
nobody | Bind a player to the profile <name> |
Assign clientpolicy.profile.<name> through a permissions plugin, and give exactly one per player or group — a player holding two profile permissions gets whichever is found first.
⚙️ Configuration
detection:
scan-delay-ticks: 60 # first evaluation, 60 ticks = 3s after join
rescan-window-seconds: 120 # new channels re-trigger evaluation for this long
track-brand: true
brand-unknown-action: LOG # ALLOW | LOG | WARN | KICK, applied at window end
policy:
default-profile: default
unknown-channel-action: LOG # keep this soft; it is the allowlist safety valve
ignore-channels: # never evaluated, supports * and ?
- "minecraft:*"
- "bungeecord:*"
- "fabric:registry_sync"
enforcement:
kick-message: |
<red>A mod not permitted on this server was detected: <white><mod>
<gray>Allowed mods: <aqua>/clientpolicy rules
warn-message: "<gold>[!] <yellow><mod> <gray>is not permitted here. Next time you will be kicked."
notify-staff: true
notify-format: "<gray>[CP] <white><player> <gray>-> <red><mod> <dark_gray>(<action>)"
profiles:
default:
mode: BLOCKLIST # ALLOWLIST | BLOCKLIST
allow: [litematica, sodium, iris, worldeditcui, replaymod]
deny: [xaeros-minimap, journeymap, voxelmap, baritone]
ladder:
first: WARN
after: KICK
escalate-after: 2 # hits 1-2 WARN, hit 3 onwards KICK
counter-reset-hours: 24
builder: # clientpolicy.profile.builder
mode: BLOCKLIST
allow: [litematica, worldeditcui, sodium, iris, xaeros-minimap]
deny: [baritone]
ladder:
first: LOG
after: WARN
escalate-after: 3
counter-reset-hours: 24
storage:
retention-days: 30 # older records are pruned at startup
messages:
prefix: "<dark_gray>[<aqua>ClientPolicy<dark_gray>] "
All messages use MiniMessage. Placeholders in kick-message, warn-message and notify-format: <player>, <mod>, <mod_id>, <channels>, <category>, <profile>, <reason>, <action>, <count> — where <reason> is one of DENIED, NOT_ALLOWED, UNKNOWN_CHANNEL, UNKNOWN_BRAND.
Only WARN and KICK are broadcast to clientpolicy.notify. LOG detections go to the console and to /clientpolicy history only — a single modded client easily brings a dozen uncatalogued channels, and broadcasting those would make the permission unusable.
Channels are never forgotten once registered: a client that registers and immediately unregisters has still revealed the mod.
🚀 Installation
- Drop
ClientPolicy-<version>.jarintoplugins/. - Start the server.
config.yml,signatures.ymlanddata.dbare created inplugins/ClientPolicy/. - Verify the signatures before you trust them — see the section above.
- Edit your profiles in
config.yml, then run/clientpolicy reload.
| Server | Paper 1.21 – 1.21.11 (one jar for the whole line; no NMS, no reflection) |
| Java | 21 |
| Dependencies | org.xerial:sqlite-jdbc, downloaded at runtime by Paper's library loader |
If your server cannot reach Maven Central, the SQLite driver will not load. Detection and enforcement still work normally; only /clientpolicy history and the cross-session violation counter are disabled, and a warning is printed at startup.


