Build1 publisher3 min readPublished
Obfuscating every class name in a Quarkus native build stops startup inside SmallRye Fault Tolerance
A developer turned on GraalVM 25's Advanced Obfuscation to ship closed Quarkus service implementations inside an open Community edition. Oracle's documented carve-out, -H:Preserve, then broke the build by pulling unreachable types in.
The Engineer · Build desk
What happened
- Backbone's Community edition ships runnable auth, actor, notification and document services for local development, with full source, CI/CD pipelines, AWS deployment and compliance features behind a commercial licence.
- GraalVM 25 adds Advanced Obfuscation, which replaces module, package, class, method, field and source-file names with opaque symbols across both application code and third-party dependencies.
- Pointed at a real Quarkus service on Oracle GraalVM 25 with -H:AdvancedObfuscation=export-mapping, the native build completed and startup then failed inside SmallRye Fault Tolerance with an NPE.
Compiled by The EngineerSomething wrong?How this is made
Why it matters
- decision A team that wants the rename has to build releases on Oracle GraalVM, which means its shipping artifact comes off a distribution the Quarkus native ecosystem does not default to.
- constraint Because Preserve also forces reachability, keeping a name means accepting code the analysis had already rejected, so the config work grows with how much optional dependency surface sits on the classpath.
- exposure The mapping JSON reverses the rename, so it has to be archived where support engineers can reach it and the people receiving the binary cannot.
- capability A runnable closed service inside an open distribution becomes shippable; enforcement of the commercial tier still rests on the signed licence and the platform's own entitlement checks.
Native image was already half an answer to the bytecode problem. You ship machine code instead of class files, and closed-world analysis strips what nothing reaches [2]. Names survived that, and Advanced Obfuscation takes them too, across third-party dependencies as well as application code [1].
A Quarkus service does not only call methods; it resolves them by string. CDI bean resolution, reflective lookup, `Class#getName()` and Fault Tolerance frames all read names at runtime, and renaming changes what they read [8]. The build completed. Startup then died inside SmallRye Fault Tolerance with an NPE [5]. Diagnosis cost more than the failure suggests, because the trace was partly obfuscated as well; the author wrote that it "made the evening more educational than it needed to be" [6].
The flag he used was `-H:AdvancedObfuscation=export-mapping`, which writes a JSON map from original names to obfuscated ones and supports deobfuscating stack traces afterwards [7]. That map is what turned the NPE into something readable [7].
Oracle's documentation points at `-H:Preserve` as the carve-out [9]. On a Quarkus classpath it does more than that. "Preserve is a reachability mechanism as well as a rename exclusion," he wrote [10]. He wanted something narrow: if a type is already reachable, keep its name. Preserve makes code available even when analysis did not find it, and then does not rename it [12]. Broad package preservation started dragging in logging bridges, Kotlin metadata, MicroProfile Metrics and deployment classes, and the build failed with missing classes; narrower Preserve lists still pulled optionals in [11]. "For Quarkus, that second behaviour felt like a bit of a sledgehammer," he wrote [13].
Your release build has to move to Oracle GraalVM, because AO is experimental and Oracle-only while the Quarkus native ecosystem is Mandrel-first [14]. And your reflection registrations have to be complete, because the AO rule he points at starts with classes registered for reflection in reachability metadata [20].
On the protection question he is careful. AO is the binary distribution boundary and nothing else: Community still requires a signed licence, and the platform runs its own integrity and entitlement checks [15]. He states plainly that this is not a security boundary and not DRM, that a skilled attacker with time and the right tools can still reverse-engineer a native binary, and that this is a different bar from unzipping a JAR and reading class files [16].
So the strongest reading the evidence supports is narrower than a general answer for closed-source Java. One developer, eighteen months into building the Backbone platform, got AO through a CDI-heavy service and published the reproduction; he says he could not find a public write-up of anyone else doing it [18][4].
What to watch
- Whether Oracle adds a rename-only exclusion that does not also force reachability, which is the flag this build needed.
- Whether AO leaves experimental status and becomes available in a build Quarkus users already run.
- A published list of which Quarkus extension reflection registrations AO treats as rename-exempt.