How it protects your plugin
- A buyer completes checkout on NMCrate.
- NMCrate mints a unique
NMK-…key bound to one live server at a time. - The download bakes the buyer’s key into their
plugin.jaras an embeddednmkey.txt— there is nothing for buyers to copy or configure. - On boot, your plugin validates the key via the NMKey library (asynchronously), which verifies the Ed25519-signed response.
- The first server claims the license seat; any second server is rejected with
max_servers. A leaked key simply refuses to boot anywhere else.
The NMKey library
The officially endorsed client is an ultra-lightweight Kotlin/JVM library (GuavaDealer/NMKey, Apache 2.0):- No networking bloat — no Ktor, no coroutine dispatchers; just the native Java 17
HttpClientand the Bukkit API, withkotlinx.serializationfor reflection-free JSON. - Dynamic fingerprinting — identifies the server with in-memory JVM heuristics (processors, OS arch, server port) to prevent seat spoofing in containerized environments.
- Offline grace period — a 256-bit AES-GCM encrypted cache keyed to the server’s fingerprint lets valid buyers survive API outages for up to 48 hours.
- Self-Cleaning architecture — hooks Bukkit’s
PluginDisableEventto automatically release the license seat and destroy HTTP threads on shutdown or/reload; no manual cleanup needed.
Requirements
| Java | 17+ |
| Paper API | 1.17+ |
| Kotlin | 2.4+ (stdlib + serialization, shaded into your jar) |
Enabling NMKey for a product
In your studio dashboard, open Licensing → Add license, pick the plugin, and choose NMKey Plugin Safe. You’ll get an 8-character plugin id — that’s thePLUGIN_ID your code passes to the library. Default policy (servers per key, expiration) is configurable per product, and individual keys can be revoked or adjusted from the same tab.
Not on the JVM? The raw HTTP endpoints are public — see the NMKey HTTP API.