How to add license keys to a Fabric mod
Short answer: add the SunLicense Java SDK (included in your jar), read the buyer's key from a file in config/, check it on a background thread from your DedicatedServerModInitializer, and switch your features off when SunLicense says no. A network error is not a no: keep running and check again later.
// build.gradle: implementation + include "com.sunlicense:sunlicense:0.0.1"
public final class MyMod implements DedicatedServerModInitializer {
private static final SunLicense CLIENT = new SunLicense.Builder()
.baseUrl("https://api.sunlicense.com").token("slk_…").build(); // API key from your build, never git
public static volatile boolean licensed;
@Override
public void onInitializeServer() {
String key = readKey(FabricLoader.getInstance().getConfigDir().resolve("mymod.properties"));
Thread.ofVirtual().start(() -> {
try {
SunLicense.Result r = CLIENT.validate(key, Map.of("host", machineId()));
licensed = r.isValid() || r.getStatus() != 400; // only a 400 is SunLicense saying no
if (r.getStatus() == 400) LOGGER.error("MyMod license rejected: {}", r.getBody());
} catch (SunLicense.SunLicenseException e) {
licensed = true; // unreachable: keep running, check again later
}
});
}
}Every feature starts with if (!MyMod.licensed) return;. readKey reads license-key from the properties file and machineId() is the one from the Paper guide.
Start from the template instead: example-fabric-mod (Fabric 26.3, Java 25, Loom) has this plus a stable machine id, masked keys, re-checks, a signed offline license fallback, BuiltByBit placeholders, a sealed core, tests and CI. All templates.
Or let your AI agent do it: paste the setup prompt into your coding agent in the mod's folder.
Free plan: 3 products, 100 licenses, no credit card. Create a free account.
Step by step
In the dashboard: a product for your mod, a policy (for example 2 machines: a machine is a server) and an API key, which can only validate licenses.
Add the SDK to the jar. Fabric has no shading by default; Loom's
includenests the SDK jar in yours:groovydependencies { implementation "com.sunlicense:sunlicense:0.0.1" include "com.sunlicense:sunlicense:0.0.1" }Keep the API key out of git: put it in
~/.gradle/gradle.propertiesand expand it into a resource at build time (processResources), like the template'ssunlicense.properties.The buyer's key goes in
config/mymod.properties(license-key=), or comes from a%%__SUNLICENSE__%%placeholder that BuiltByBit and Voxel Shop fill in on download.Server side only. Use
DedicatedServerModInitializer(theserverentrypoint infabric.mod.json) so the check runs on the server that holds the license, not on every player's client.Never block the server thread. Check on a background thread; act on the result on the server thread (
server.execute(...)) if you need to touch the game.Machine id: a hash of OS, architecture and host name, sent as
host(the Paper guide has the snippet; why it matters).Results: 200 starts your features, 400 stops them with the reason (keys masked in logs), anything else keeps running and retries in about 10 minutes, or uses an offline license. Re-check every few hours (
ServerTickEventsor a scheduled executor) so a revoked key reaches running servers.
Selling it
Mods sold on BuiltByBit or Voxel Shop get the buyer's key written into the jar by the placeholder. Anywhere else (Tebex, Gumroad, Payhip, other stores), SunLicense issues a key per purchase and the buyer pastes it into the config file.
A license check belongs in mods you sell for servers. Free mods on Modrinth or CurseForge are free downloads: don't lock them; if you want install numbers for those, use product analytics without a license.
Going further
- Protection: a sealed core and tamper reports. Obfuscation for the jar.
- Paper and Velocity versions of this guide. More on licensing Minecraft mods.
