JavaObf
Sample artifacts

The same plugin, three protection levels. Download. Decompile. Compare.

Reading marketing copy is fine. Holding the artifacts in your decompiler is better. We took one real Bukkit plugin (CWelcomer 1.0.1) and protected it twice: once on the free tier with every dial maxed, once on Citadel with the full pipeline. The original is here too. Pull all three, run them through your tool of choice, and form your own opinion.

Source 12.8 MB

Untouched build

The plugin as it ships from the developer compile.

readable
yes, fully
strings
plaintext
control flow
structured
native helpers
none

What every other JAR you ever downloaded looks like before protection. Decompile it, diff it, see the readable control flow and string table. This is the baseline.

CWelcomer-1.0.1.jar e74ccd365335bb0a
Download CWelcomer-1.0.1.jar
Free 14.0 MB

Free tier, every dial maxed

No card. No trial timer. Just the free pass with everything on.

renames
full
strings
encrypted
control flow
flattened
native helpers
none

Same source plugin, run through the free tier with every transform we ship turned up. Renames, string concealment, control-flow flattening, decoy classes, watermark, layout scramble. The decompile is no longer pleasant reading.

CWelcomer-1.0.1-free-max.jar 3900ad208a651c7c
Download CWelcomer-1.0.1-free-max.jar
Citadel 198.4 MB

Citadel, the full stack

Native-bound, license-gated, the whole pipeline.

renames
full
strings
encrypted
control flow
flattened + VM
native helpers
linux + windows

The same plugin run through the paid Citadel pipeline. Native-bound key derivation, runtime integrity heartbeat, virtualized control flow, native method emission, synthetic helper extraction, and the license client. The full deployment shape.

CWelcomer-1.0.1-paid-max.jar 21dc427abd9e2d64
Download CWelcomer-1.0.1-paid-max.jar
Receipts

Compatibility and overhead, with the version numbers attached.

Most obfuscators promise the moon and ship a JAR that does not load. JavaObf is booted against real platforms on every release. Below: the exact versions we test on, and the actual overhead a protected build adds. No hand-waving, no synthetic benchmarks dressed up as production data.

Tested on real servers Numbers from CI logs Updated every release
input cwelcomer.jar readable bytecode
output cwelcomer.protected.jar +42 KB · +12 ms boot · 0 ms / tick
3.4 s median Pro build
+12 ms Citadel boot delta
0 ms per-tick overhead
100% boot validation pass rate
Compatibility matrix

Booted, not just compiled.

For each platform below we run the obfuscated plugin against a real server and confirm it loads, enables, and runs without errors. The exact tested versions are listed on every card.

Paper / Bukkit / Spigot

runtime golden
Tested build
Paper 1.21.11-130
Minecraft
1.21.11
Java
21 (Temurin)
Loader
plugin.yml + paper-plugin.yml

Boot validation runs against this version on every Pro+ build. The cwelcomer reference plugin asserts plugin enable, command dispatch, listener registration, and license activation against a live Paper instance.

BungeeCord

runtime golden
Tested build
BungeeCord 1.26.1-R0.1-SNAPSHOT
Build number
#2064
Java
21 (Temurin)
Loader
bungee.yml

Listener annotation strip + companion EventExecutor regen verified booting against the real BungeeCord proxy. Zero VerifyError, zero NoSuchMethodException across 5 listener shapes.

Velocity

runtime golden
Tested build
Velocity 3.4.0-SNAPSHOT
Build hash
git-b1dd26fb-b541
Java
21 (Temurin)
Loader
velocity-plugin.json

Guice DI wiring preserved. @Subscribe consolidation routes through a synthetic companion class with @Subscribe-annotated dispatch slots. EventManager is a Velocity interface, so the engine emits INVOKEINTERFACE rather than INVOKEVIRTUAL.

Sponge

scanner golden
API target
SpongeAPI 11+
Java
21 / 25 supported
Loader
sponge_plugins.json
Harness
plugin-loader simulator

Real SpongeVanilla install needs Java 25 + FART + Mojang server jar (~500 MB), so CI runs a Sponge-shaped plugin loader that reaches the same enabled() semantics short of world generation. @Listener stripping + companion regen verified.

Forge

scanner golden
API
net/minecraftforge/* (Forge 47+)
Java
17 / 21
Descriptor
mods.toml
Event bus
IEventBus.register(Object)

2-arg IEventBus.register descriptor verified against the per-slot register stub emitter (not the legacy 3-arg shape that broke with VerifyError). @Mod entry resolution + @SubscribeEvent companion regen pass the static contract test.

NeoForge

scanner golden
API
net/neoforged/* (NeoForge 21+)
Java
21
Descriptor
neoforge.mods.toml
Event bus
IEventBus.register(Object)

NeoForge fork (2024) shares architecture with Forge. Same per-slot register-stub emitter. NeoForge descriptor is matched first (more specific) so a mod that ships both files routes correctly.

Fabric

scanner golden
API
Fabric Loader 0.15+
Java
21
Descriptor
fabric.mod.json
Events
functional Event<T>.register

Fabric has no annotation-driven event consolidation. ModInitializer.onInitialize is body-extracted; functional Event.register lambdas are captured and emitted as nested synthetic SAM classes.

one engine, every loader

Built for plugins that ship for money.

One protected JAR survives Paper boot, Velocity event subscribe, Bungee handler register, Fabric ModInitializer, Sponge listener scan, and the Forge / NeoForge mod bus, without you maintaining seven different build configurations. The engine reads the descriptor, picks the right consolidation pass, and emits bytecode that the loader you actually ship to recognizes.

Try it on your plugin
Performance receipts

Overhead, measured. Not promised.

Numbers below come from the cwelcomer reference plugin (a real licensing-gated Bukkit plugin used as our boot golden) on Paper 1.21.11-130, Java 21 Temurin, baseline OpenJDK G1GC defaults. Source: the same CI logs we ship with every release.

+12 ms
Cold-start delta on Citadel profile

Plugin enable cost added by the full Citadel stack (native key derivation, anti-tamper interlocks, license client boot). Pro profile is closer to +4 ms. Within OS-scheduler noise on most runs.

0 ms
Per-tick overhead (steady state)

Renames, string concealment, and control-flow flattening are static one-time transforms with zero hot-path cost. The native key stub is consulted at boot, not per tick. Selective virtualization only fires on methods you opt in.

+42 KB
Pro profile JAR size delta

Decoy classes, watermark metadata, and the fingerprinted runtime helper. Citadel adds the native key stub binaries (linux-x86_64, linux-aarch64, windows-x86_64) for an additional ~280 KB depending on platform set.

3.4 s
Median build time, Pro profile

Includes analyze, transform, and validate against a live Paper boot. Citadel adds ~6 s for the native compile step (per platform target). Larger plugins scale linearly with class count.

What ships with every build

The numbers above are not curated marketing. They are a checkbox in the build report.

Boot validation
Real Paper boot, plugin enabled, command dispatch confirmed, listener registry verified
Fingerprint
Per-customer build ID embedded in 5 channels (decoy classes, manifest, native stub, watermark resource, layout scramble seed)
Size delta report
Pre vs post-protect bytes by transform pass
Transform timings
Per-pass milliseconds, surfaced in the artifact details page
Mapping file
Reversible original-to-obfuscated symbol map for crash-trace decoding
Lock summary
Every locked descriptor (entrypoints, plugin.yml refs, mixin targets, access wideners) listed by file

Want to verify the numbers on your own plugin?

The free tier includes the build report. Run a Pro trial against your real JAR, read the receipts, then decide.