Tracing & retrace
Two post-build tools that both rest on the same idea: the information needed to read a protected build is yours, and only yours. rymga hands you the files and keeps nothing — so nobody, us included, can undo your obfuscation or forge your watermarks.
Retrace
Obfuscation renames everything, so a crash report from a user comes back unreadable —
at a.a.b(...) instead of at app.EventDispatcher.foo(...).
Retrace turns it back, offline, using the name mapping your build produced. No upload, no
key, no server — just a text lookup.
The mapping file
Every build that renames emits a mapping file — a small text file that ties your original
names to the obfuscated ones. The CLI downloads it next to your output (app-obf.map
beside app-obf.jar), or wherever --mapping-out points.
.map like a build artifact:
commit it or archive it with each version you ship. Lose it and you can't retrace crash reports from that
build — and because rymga never stores it, we can't hand you another copy. (It's the same discipline as
ProGuard's mapping.txt.)We don't keep it for a reason: the mapping is the key to reading your obfuscated code. Holding it would make us a single place to breach and undo everyone's protection — so we deliver it and forget it.
Stable names across releases (incremental)
Point the CLI at the same file with --mapping rymga.map and it does both at once: it
uploads your previous mapping so unchanged classes and methods keep their obfuscated names,
and writes the updated mapping back to that file for next time. Commit rymga.map with
each release and a renamed public API stays stable version to version — anything that compiled against it keeps
linking. (This is the "incremental obfuscation" a seed alone can't give you.)
Running retrace
Point it at the build's mapping and feed it the stack trace — from a file or a pipe:
# from a file
java -jar rymga-cli.jar retrace --mapping app-obf.map --trace crash.txt
# or straight from a pipe
cat crash.txt | java -jar rymga-cli.jar retrace --mapping app-obf.map
The obfuscated trace on the left becomes the readable one on the right:
Exception in thread "main" java.lang.IllegalStateException: boom
at a.a.b(EventDispatcher.java:4)
at a.a.a(EventDispatcher.java:3)
at app.Main.main(Main.java:2)Exception in thread "main" java.lang.IllegalStateException: boom
at app.EventDispatcher.foo(EventDispatcher.java:4)
at app.EventDispatcher.launchSomething(EventDispatcher.java:3)
at app.Main.main(Main.java:2)Per-buyer watermark
If you sell your plugin, you can stamp each copy you hand out with the buyer's id, invisibly. When a copy leaks, you recover that id and know exactly whose it was. It's redundant across the jar, so it survives re-jarring and even deleting some classes.
It's the same CLI — one tool, nothing extra to install or manage. The stamping and tracing run in our cloud; each license has its own secret that never leaves our servers and never ships in any jar. You don't handle a key file — you just point the CLI at a jar.
How it works
- Obfuscate once. A normal build produces your protected
app-obf.jar. - Stamp per sale. For each buyer,
watermark stampthe obfuscated jar with their id. No re-obfuscation — it's a light edit, and costs 1 token. - Trace on a leak. Run
watermark traceon the leaked copy to recover the id. Free.
Commands
# per sale, stamp the obfuscated jar with that buyer's id (1 token, no re-obfuscation)
java -jar rymga-cli.jar watermark stamp \
--in app-obf.jar --id "order-8842-jane" --out app-jane.jar
# a copy leaks → recover the id (free)
java -jar rymga-cli.jar watermark trace --in leaked.jar
# → order-8842-jane
Same auth as everything else — your seat key (--key or OBF_KEY).
The CLI only makes the call; none of the watermark logic or the secret is ever in the client.