Nebula.NET
Protecting different project types
Recipes for every kind of .NET project — libraries & NuGet packages, WPF/WinForms, ASP.NET Core & Blazor, MAUI, console apps & services, Unity, single-file, F#/VB.
Nebula protects any managed .NET assembly, but the right settings depend on how your code is loaded and called. Find your project type below. The recurring rule: anything resolved by name at runtime (reflection, serialization, XAML/Razor binding) must be preserved — Nebula auto-detects most of it, and you can preserve the rest explicitly.
If your exact case isn’t listed, the closest one plus Keeping your app working and Troubleshooting will cover it — and you can always ask us.
Class library / SDK (consumed by others)
Your public API is your contract — keep it, obfuscate the internals.
{ "inputs": ["bin/Release/net8.0/MyLib.dll"], "outputDirectory": "protected",
"preservePublicApi": true, "controlFlowObfuscation": true, "encryptStrings": true }
preservePublicApi: truekeeps public/protected types and members named; everything internal/private is renamed.- Shipping several libraries that call each other? Obfuscate them together with
crossAssemblyRename— see Multiple assemblies.
NuGet package (.nupkg)
Point Nebula straight at the package — it obfuscates the assemblies inside and repacks a protected .nupkg:
{ "inputs": ["bin/Release/MyLib.1.2.3.nupkg"], "outputDirectory": "protected", "preservePublicApi": true }
Console app / Windows service / daemon
No public surface to preserve, so protect everything for maximum strength:
{ "inputs": ["bin/Release/net8.0/MyApp.dll"], "outputDirectory": "protected",
"preservePublicApi": false, "controlFlowObfuscation": true, "controlFlowIntensity": "aggressive",
"encryptStrings": true }
The entry point is renamed safely — the runtime locates Main by metadata token, not by name.
WPF / WinForms (desktop, XAML)
XAML binds to view-model properties, commands, and event handlers by name at runtime, and the WinForms designer serializes control fields by name. Preserve what the UI layer touches:
- Keep
preservePublicApi: true(bound members are usually public). - Exclude the types that XAML binds to if any bound member isn’t public — e.g. view-models and their bound properties. See exclusions.
- Don’t rename types referenced by name in XAML (
x:Class, converters,{x:Type}); Nebula’s auto-detect catches most, but test the running app.
ASP.NET Core / Blazor / Web API
Model binding, routing, Razor/Blazor markup, and JSON serialization all resolve by name:
- Controllers / minimal-API handlers / Razor Pages / Blazor components referenced in routing or markup — preserve (usually public;
preservePublicApi: true). - DTOs / request & response models — their property names become JSON/XML fields, so preserve them (auto-detected when they carry
System.Text.Json/Newtonsoft/DataContractattributes; otherwise exclude them). - Dependency injection is type-based (
AddScoped<IFoo, Foo>()) and safe; convention/assembly-scanning by name needs the scanned types preserved.
Internals (business logic, algorithms) rename and control-flow freely — that’s where the value is.
.NET MAUI / Xamarin
Treat like WPF (XAML binding) plus platform interop: preserve XAML-bound view-models and any [DllImport]/platform-service names. [DllImport] entry points are preserved automatically.
Unity
Unity serializes public/[SerializeField] fields by name and can call methods via SendMessage("Name"). Preserve serialized fields and any message-target methods (annotate them — see below), and preserve MonoBehaviour subclasses referenced by name in scenes.
F# and VB.NET
Fully supported — Nebula works at the IL level, so language doesn’t matter. F# emits extra compiler-generated members; keep autoDetect: true (the default) and run your tests against the protected build.
Single-file & self-contained apps
Obfuscate the managed assemblies, then let the single-file publish bundle the protected copies (build → obfuscate → dotnet publish --no-build). Two notes:
- Anti-tamper’s self-hash no-ops inside a single-file bundle (the assembly has no on-disk path), so rely on control-flow + string encryption there. For multi-file/self-contained-folder layouts, anti-tamper works normally.
- Self-contained just adds the runtime; it doesn’t change how you obfuscate your assemblies.
Strong-named or Authenticode-signed output
Obfuscation rewrites the assembly, so sign after obfuscating — Nebula does it for you:
{ "strongNameKeyFile": "keys/mykey.snk",
"authenticodeCertFile": "keys/cert.pfx", "authenticodeTimestampUrl": "http://timestamp.digicert.com" }
Strong naming is re-applied to the protected assembly; Authenticode signs the final output.
Licensing / anti-piracy code (like a license check)
This is a prime target — flatten the decision logic so it can’t be read or patched:
{ "controlFlowObfuscation": true, "controlFlowIntensity": "aggressive",
"controlFlowInclude": ["MyApp.Licensing.LicenseManager.Validate", "MyApp.Licensing.LicenseManager.CheckEdition"],
"encryptStrings": true }
Target the specific methods with controlFlowInclude, encrypt the strings that reveal branch conditions, and keep any type-identity self-checks ([Guid], Assembly.GetExecutingAssembly()) working — Nebula preserves [Guid] and assembly identity through obfuscation.