Obfuscation
Protect the code you ship. SunLicense obfuscates it on our servers, so it is far harder to read or reuse:
- Java — upload a built
.jarand we run ProGuard, yGuard or Skidfuscator over it (pick the engine per profile): it renames your classes, fields and methods, and can shrink, repackage or — with Skidfuscator — add control-flow obfuscation and string encryption. You get the obfuscated jar, a mapping file (so you can de-obfuscate your own stack traces) and a report. - JavaScript and TypeScript — upload a
.js,.mjs,.cjsor.tsfile, or a project.zip, and we run javascript-obfuscator over it. You get the obfuscated file (or a zip of your project) and a report. (See below.) - Python — upload a
.pyfile or a project.zip, and we run python-minifier over every.pyin it. The output is plain Python 3 that still runs on your buyers' machines; protection is lighter than Java's. (See below.) - Lua / FiveM — upload a
.luafile or a zipped FiveM resource, and we run Prometheus over your client, server and shared scripts while leavingfxmanifest.lua,html/,stream/and data files alone. (See below.)
Obfuscation is on the Lite and Standard plans, with a monthly limit shared across languages. It runs as a task, so you can close the page and come back — the result waits for you, and files are kept for 7 days.
Java
Upload a built Java .jar and SunLicense runs ProGuard over it: it renames your classes, fields and methods, and can shrink unused code and repackage everything. You get back the obfuscated jar, the ProGuard mapping file (so you can de-obfuscate your own stack traces) and a short report.
Obfuscate a jar
- Open Obfuscation in the sidebar.
- Pick your language (Java) and an obfuscator — ProGuard is the recommended, safe default.
- Drop in your built
.jar(or a.zipof several jars). SunLicense looks at it and tells you what it found — a Paper plugin, a Fabric mod, a library — names the run for you and applies safe settings, so there's usually nothing else to do. - Optional: open Advanced options to change the settings (below), start from a saved profile, or add library jars you compile against (the Paper, Velocity or JDA API) — only needed if the obfuscator reports missing classes. Without them it runs with warnings ignored, and the report says so.
- Choose Obfuscate and watch the live log. When it finishes, download the obfuscated jar, the mapping file and the report.
Your past runs are on the page's History tab — open one any time to download its results again (links last a few minutes).
Your plugin still loads
SunLicense detects your jar's entry points and keeps their names, so you never have to write keep rules by hand:
- the manifest
Main-Class; plugin.yml,paper-plugin.ymlandbungee.ymlmain, andvelocity-plugin.json;- Fabric
fabric.mod.jsonentrypoints and mixin classes, and Forge/NeoForge@Modclasses; META-INF/servicesproviders, event handlers (@EventHandler,@Subscribe), enums and serialisable types.
The SunLicense SDK's license check is renamed along with the rest of your code — there is no fixed name for an attacker to find — but it still runs, and the per-buyer download placeholders (product downloads) are kept intact. Classes that hold a placeholder are never string-encrypted, so per-buyer key injection keeps working; a run that would lose a placeholder is refused.
Engines
Pick the engine in your profile — one engine runs per job. They trade strength against compatibility:
| Engine | Licence | What it does | Good for |
|---|---|---|---|
| ProGuard | GPL-2.0 w/ classpath exception | Renames, and can shrink and optimise bytecode. Mature and widely compatible. | The default; plugins, mods, bots and libraries. |
| yGuard | MIT | Renames classes, fields and methods only (no string encryption), configured with its own XML. | A lightweight, highly compatible rename, or if you already have a yGuard config. |
| Skidfuscator | MIT | Adds control-flow obfuscation and string encryption on top — much harder to read, at some cost in size and compatibility. | The strongest protection, when you can test the result loads. |
TIP
Stronger obfuscation is more likely to break reflection-heavy plugins. Try Skidfuscator with just string encryption first, test that your plugin still loads, then add control-flow obfuscation.
Settings
The options depend on the engine (the form is generated from each engine's own schema). For ProGuard:
| Option | Default | What it does |
|---|---|---|
| Rename symbols | on | Rename classes, fields and methods. |
| Remove unused code | off | Drop unused classes and members. Off by default, because plugins and mods are loaded by reflection. |
| Optimise | off | Run ProGuard's optimisation passes. |
| Keep line numbers | on | Keep line-number tables so your stack traces still map to your source. |
| Keep public API | off | Keep public and protected members unrenamed — for libraries other code compiles against. |
| Repackage classes | off | Move all renamed classes into a single package. |
| Keep unrenamed | — | Extra packages, classes or members to leave untouched, such as com.acme.api.**. |
yGuard offers Keep public API, Keep line numbers and Keep unrenamed. Skidfuscator offers Encrypt strings and Obfuscate control flow (both off by default) and Leave untouched.
Advanced
Choose Advanced to edit the engine's configuration directly, starting from the one your options generate. SunLicense manages the input, output and library jars for you, so anything that names a file is refused with a clear message: ProGuard directives (-injars, -libraryjars, -printmapping <file>, @file, dictionaries…), yGuard XML that adds inoutpair/externalclasses/logfile or a DTD/DOCTYPE, and Skidfuscator libs/renaming keys, HOCON include or file paths.
JavaScript and TypeScript
Upload a single .js, .mjs, .cjs or .ts file, or a .zip of your project. For a project, SunLicense obfuscates every .js, .mjs, .cjs and .ts file outside node_modules and your build folders (build/, dist/, …); everything else, node_modules included, is copied into the result unchanged. TypeScript is transpiled to JavaScript first (a plain transpile — no type checking — so the output is .js).
Your node_modules doesn't need to be in the zip (your buyers run npm ci); if it is, keep it under 150 MB or the run is refused.
Your SunLicense SDK calls and the per-buyer download placeholders (product downloads) are always kept readable, and your import/require statements are left intact, so licensing and injection keep working.
| Option | Default | What it does |
|---|---|---|
| Strength | medium | How hard the transforms you turn on push: low, medium or high. |
| Runs on | node | node for bots, servers and CLIs; browser for front-end bundles. |
| Encode strings | off | Encode the extracted string literals (base64, or RC4 on high). |
| Flatten control flow | off | Rewrite control flow so it's hard to follow. Can slow hot code down. |
| Inject dead code | off | Add believable unreachable code. Grows the file. |
| Self-defending | off | Break the output if it's reformatted or beautified — don't post-process the file. |
| Keep exported names | on | Leave top-level (exported) names so other code can still import them. |
| Skip files | — | In a project zip, paths to leave untouched, such as src/vendor/**. |
Every obfuscated file is checked with node --check before you download it, so a broken transform never ships.
Choose Advanced to edit the javascript-obfuscator options directly as JSON, starting from the one your options generate. SunLicense manages the input, output and source maps, so options that name files (inputFileName, sourceMap…, and so on) are refused.
Python
Upload a single .py file or a .zip of your whole project (a Discord bot, a tool) and SunLicense runs python-minifier over every .py file in it: it shortens the names used inside your functions and removes docstrings, so the code is harder to read. The output is plain Python 3 — no compiled binaries, no C extensions — so it still runs on your buyers' Windows, macOS and Linux machines on any Python 3 they have.
In a project .zip, SunLicense obfuscates only your .py files. Anything inside a virtualenv, site-packages, __pycache__ or a package's metadata folder is copied untouched, and so are your non-Python files (requirements.txt, assets, data). You get back a .zip with the same layout, plus a short report.
Python protection is lighter than Java's
Python ships as source, so obfuscation can only go so far: names are shortened and docstrings removed, but the program's structure stays. It raises the effort to read or copy your code; it does not make it unrecoverable. If you need stronger protection, Java (compiled bytecode + ProGuard) is the better target.
Settings
| Option | Default | What it does |
|---|---|---|
| Rename local names | on | Rename arguments and variables used inside a function. This is the main protection. |
| Rename module-level names | off | Also rename classes, functions and variables at module level. Off by default: it breaks imports between your own modules, and names your framework looks up by string (discord.py cogs, Flask routes). Turn it on only for a single self-contained file. |
| Remove docstrings | on | Drop docstrings and other bare literal text. |
| Remove type annotations | off | Strip type hints. Off by default: discord.py commands and libraries like pydantic read annotations at runtime, so removing them can break your bot. |
| Keep names | — | Names to never rename, one per line (for example setup, or a command your framework finds by name). |
Because module-level names and annotations are kept by default, a typical discord.py bot obfuscates and still imports and runs unchanged.
Advanced
Choose Advanced to edit the engine options directly as a JSON object of python-minifier keyword arguments, starting from the one your options generate (for example {"rename_locals": true, "rename_globals": false}). python-minifier reads and writes no files, so there is nothing to point at the filesystem; any option that isn't a known python-minifier flag is refused with a clear message.
Lua and FiveM resources
Choose the Prometheus (Lua / FiveM) engine to obfuscate Lua. Upload either a single .lua file or a zipped FiveM resource (up to 50 MB).
For a resource zip, SunLicense reads your fxmanifest.lua (or the legacy __resource.lua) and obfuscates only the scripts it lists under client_scripts, server_scripts and shared_scripts — globs like server/*.lua are expanded. Everything else is passed through untouched:
- the manifest itself, and
files/ui_page/data_fileentries; html/,stream/and other assets, and any non-Lua file;- scripts that belong to another resource (
@other-resource/lib.lua).
Because Prometheus keeps string literals, your exports, event names and NUI callbacks keep working — nothing that other resources call by name changes. A single .lua upload is obfuscated as-is.
Settings (Lua)
| Option | What it does |
|---|---|
| Protection preset | Minify, Weak, Medium (recommended) or Strong — Prometheus' built-in presets. Stronger presets grow and slow the output. |
| Lua version | Lua 5.1 (FiveM and most Lua) or LuaU (Roblox; experimental upstream). |
| Exclusions | Resource-relative files to leave alone, e.g. config.lua or locales/*.lua. |
FiveM and Lua 5.4
FiveM's CfxLua is based on Lua 5.4, but Prometheus parses and emits Lua 5.1 (or LuaU). Most resources are written in 5.1-compatible syntax and work fine. Scripts that use 5.4-only syntax — integer division //, bitwise operators & | ~ >> <<, or <close>/<const> attributes — won't parse; list those files under Exclusions so they're shipped unchanged.
Each output file is syntax-checked (compiled without being run) before you get it back, so a broken obfuscation never ships silently.
Advanced (Lua)
A Prometheus configuration is itself a Lua table — i.e. code — so, for safety, the Advanced editor takes a JSON form of it, not Lua. You can set the preset, luaVersion, prettyPrint, exclusions and an explicit steps list (each an allow-listed step name with primitive settings); SunLicense builds the Lua config from your JSON and never runs any Lua you write. Steps or settings outside the allow-list are refused with a clear message.
Obfuscation for Lua is powered by Prometheus — Based on Prometheus by Elias Oelschner, https://github.com/prometheus-lua/Prometheus, used under the Prometheus License.
Profiles
Save a configuration as a profile to reuse it on later runs. A profile can be linked to a product, and starting a run copies its settings into the task, so editing the profile later never changes a past result.
TIP
Keep the mapping file from each release. If a customer sends you an obfuscated stack trace, ProGuard's retrace tool turns it back into your original class and method names using that file.
Beyond the dashboard
Obfuscation always runs on SunLicense's servers — never on your machine — so you can reach it the same way everywhere.
Publish an obfuscated build to Downloads
When you upload a release file under product downloads, turn on Obfuscate before publishing and (optionally) pick a profile — the default is the product's linked profile. SunLicense obfuscates the upload before it goes live: the release stays processing until it succeeds, then the obfuscated file is published with each buyer's license written in, and your original is removed. Buyers only ever get the obfuscated file. If a run fails, the release shows the task log and stays unpublished.
From the CLI (and CI)
Use the sunlicense obfuscate command to obfuscate a build and download the result, with a non-zero exit code on failure so it fits a release step:
sunlicense obfuscate build/libs/app.jar --profile release -o dist/The language is detected from the file; pass --engine or --profile to be explicit, --config <file> for a raw engine config, and --json for scripts. sunlicense obfuscate profiles lists your saved profiles.
From an AI coding agent (MCP)
The CLI's MCP server exposes obfuscation_profiles_list, obfuscate_file (it uploads a local file — the file is never shown to the model) and obfuscation_status. Connected apps and SunLicense AI get the read-only tools only.
From the API
A run uses the standard tasks API: create it with POST /api/v1/obfuscation/runs (with a profileId, or engine + options/rawConfig), upload the input with the task's presigned URL, submit it, follow the live log over the task's SSE stream, and download the outputs. GET /api/v1/obfuscation/engines lists the languages, engines and their options; GET /api/v1/obfuscation/profiles lists your profiles.
