A Java annotation-based API for controlling bytecode obfuscation behavior at the class, method, and field level.
The Nebula Obfuscator API gives you control over how your code is obfuscated. By annotating classes, methods, and fields, you can instruct the obfuscator to preserve names, keep signatures, inline methods, encrypt resources, or compile down to native code.
Prevents the obfuscator from renaming the annotated element.
Targets: Class, Method, Field
import dev.nebulaobf.api.annotation.KeepName;
@KeepName
public class MyPublicApi {
@KeepName
public String getUserName() { /*...*/ }
@KeepName
public String userEmail;
}Use this on any class, method, or field whose name must remain stable - for example, public APIs, reflection targets, or serialized fields.
Prevents the obfuscator from altering the method's signature (parameter and return types).
Targets: Method
import dev.nebulaobf.api.annotation.KeepSignature;
@KeepSignature
public List<String> fetchItems(Map<String, Integer> params) { /*...*/ }Use this when a method's generic type information needs to survive obfuscation, such as for frameworks that rely on reflection to read signatures at runtime.
Marks a method as a candidate for inlining during obfuscation - the method body is inserted directly at call sites, eliminating the method call.
Targets: Method
import dev.nebulaobf.api.annotation.InlineMethod;
@InlineMethod
private int add(int a, int b) {
return a + b;
}Best used on small, frequently-called utility methods where eliminating the call overhead and the method's existence improves both performance and obfuscation strength.
Marks a class or method to be compiled to native code by the native obfuscator.
Targets: Type, Method
Retention: CLASS
import by.radioegor146.nativeobfuscator.Native;
@Native
public class CriticalLogic {
public static int compute(int x) { /*...*/ }
}Applying @Native to a class marks all of its methods for native compilation. You can also apply it to individual
methods for selective native conversion.
Excludes a specific method from native compilation, even if its containing class is annotated with @Native.
Targets: Method
Retention: CLASS
import by.radioegor146.nativeobfuscator.Native;
import by.radioegor146.nativeobfuscator.NotNative;
@Native
public class CriticalLogic {
public static int compute(int x) { /*...*/ } // compiled to native
@NotNative
public static String debugInfo() { /*...*/ } // kept as JVM bytecode
}NebulaResourceEncryption is a stub class used to integrate resource encryption into your code. During obfuscation,
the obfuscator replaces the stub's implementation with a decryption resource loader.
Replace direct getResourceAsStream calls with the stub:
import dev.nebulaobf.api.resource.NebulaResourceEncryption;
// Before
InputStream is = MyClass.class.getResourceAsStream("config.json");
// After
InputStream is = NebulaResourceEncryption.getResourceAsStream(MyClass.class, "config.json");The obfuscator detects these calls and replaces them with decryption logic at build time. The encryption itself is handled entirely by the obfuscator - no manual key management is needed. After obfuscation, the annotations are removed from classes and their members.
| Annotation | Target | Effect |
|---|---|---|
@KeepName |
Class, Method, Field | Preserves the element's name after obfuscation |
@KeepSignature |
Method | Preserves the method's generic signature |
@InlineMethod |
Method | Inlines the method at call sites |
@Native |
Type, Method | Compiles the element to native code |
@NotNative |
Method | Excludes a method from native compilation |
Add the full NebulaObfuscator API jar to your project so all annotations are available at compile time. However, only
the resource package needs to be shaded into your output jar - this ensures NebulaResourceEncryption works
correctly when running your jar pre-obfuscation during development. The annotation classes themselves are only needed
at compile/obfuscation time and don't need to be bundled.
plugins {
id "java"
id "com.github.johnrengelman.shadow" version "8.1.1"
}
repositories {
mavenCentral()
ivy {
url = uri("https://github.com/SpigotRCE/NebulaObfuscator-Api/releases/download")
patternLayout {
artifact("[revision]/[artifact]-[revision].[ext]")
}
metadataSources {
artifact()
}
}
}
dependencies {
shadow(implementation("dev.nebulaobf.api:NebulaObfuscator-Api:1.0"))
}
shadowJar {
configurations = [project.configurations.shadow]
archiveClassifier.set("")
exclude "dev/nebulaobf/api/annotation/**"
exclude "by/radioegor146/**"
exclude "META-INF/**"
}
build.dependsOn shadowJar| Package | Needed at compile time | Needs to be shaded |
|---|---|---|
dev.nebulaobf.api.annotation |
✅ (for @KeepName, etc.) |
❌ (consumed by the obfuscator, not at runtime) |
dev.nebulaobf.api.resource |
✅ | ✅ (stub must exist pre-obfuscation so your jar runs) |
by.radioegor146.nativeobfuscator |
✅ (for @Native, etc.) |
❌ (compile/obfuscation time only) |
After obfuscation runs, the obfuscator replaces the NebulaResourceEncryption stub with its own decryption
implementation and all the resources defined are encrypted, so the shaded stub is no longer present in the final
distributed jar.
- ASM - used by
AnnotationProcessorto inspect bytecode nodes - native-obfuscator - provides
@Nativeand@NotNative