MSBuild Integration

The WiseOwl.Demeanor.MSBuild NuGet package runs obfuscation as a post-build step. It bundles its own obfuscator binary — the global CLI tool does not need to be installed on the build machine.

Every CLI option has a corresponding MSBuild property. Set them in your .csproj and they apply on every Release build.

Quick setup

dotnet add package WiseOwl.Demeanor.MSBuild
dotnet build -c Release

That’s it. Release builds auto-obfuscate. Debug builds and test projects (anything where IsTestProject=true, set automatically by Microsoft.NET.Test.Sdk) are auto-skipped — obfuscating an xunit / mstest / nunit assembly would break the runner’s reflection-based discovery.

Override the default

<PropertyGroup>
  <!-- Disable obfuscation entirely (any config): -->
  <Obfuscate>false</Obfuscate>

  <!-- Or obfuscate Debug builds too: -->
  <Obfuscate>true</Obfuscate>
</PropertyGroup>

Scope

PropertyCLIDefaultDescription
<Obfuscate>true on Release; false on Debug or when IsTestProject=trueMaster switch. Auto-enables for Release builds. Set to false to disable, or true to obfuscate every config.
<DemeanorIncludePublics>--include-publicsfalseAlso obfuscate public/protected symbols. Use for executables; omit for libraries.
<DemeanorIncludeDeps>--include-depsfalseAlso obfuscate co-located private dependencies.
<DemeanorOut>--out(the project’s output directory)Where the obfuscated assembly is written. By default it replaces the build output in place, so the rest of the build sees the obfuscated assembly.
<DemeanorProject>--project$(MSBuildProjectDirectory)Project root holding .demeanor/ — the committed rules and resolved decisions. Passed explicitly on every build so a CI machine reads the same catalog a developer does.
<DemeanorFailOnPendingDecisions>--fail-on-pending-decisionsfalseFail the build if a needs-decision rule matched a symbol nobody has resolved. Those symbols keep their names either way and are listed in the report; this makes the build stop instead.

Naming

PropertyCLIDefaultDescription
<DemeanorNamingMode>--namesAlphaAlpha (a, b, c) or Unicode (whitespace/invisible chars).
<DemeanorNamespacePrefix>--prefix(none)Namespace prefix for renamed types. Prevents cross-assembly name collisions.
<DemeanorRename>--renameaggressiveHow hard renaming is applied: off, on, or aggressive. At aggressive, renamed members share one name through privatescope and property and event metadata is stripped.
<DemeanorVirtualRename>--virtual-renameonRenaming of overrides of external methods through injected MethodImpl rows: off or on.
<DemeanorEnumDeletion>--enum-deletiononDeletion of enum member fields: off or on. Reaches only enums that were renamed.

Protection Toggles

PropertyCLIDefaultDescription
<DemeanorStringEncryption>--string-encryptiononEncryption of literal strings: off or on.
<DemeanorConstantEncryption>--constant-encryptiononEncryption of integer constants: off or on.
<DemeanorResourceEncryption>--resource-encryptiononEncryption of embedded resources: off or on. Applies to a raw resource this assembly reads by name.
<DemeanorCallHiding>--call-hidingonRelay methods that hide intra-assembly call targets: off or on.
<DemeanorProxyThreshold>--proxy-threshold16Minimum method body size (bytes) eligible for call hiding.
<DemeanorCfg>--cfgflattenControl flow obfuscation strength, weakest first: none, reorder, predicates, or flatten.
<DemeanorHinderReflection>--hinder-reflectiononMetadata constructs that hinder reflection-based tools, and suppression of ildasm: off or on.
<DemeanorAntiTamper>--anti-tamperonA module initializer that verifies the assembly file has not been modified: off or on. It verifies a file on disk, so under NativeAOT or single-file publishing the check passes without doing anything.
<DemeanorAntiDebug>--anti-debugonDebugger detection scattered across method entry points: off or on.
<DemeanorBaml>--bamlonPatching of compiled XAML so it tracks renamed types and properties: off or on. Turning it off freezes everything the BAML references from renaming instead.

Category Disabling

Disable renaming of entire symbol categories. Useful when a broad set of symbols must keep their names (e.g., all properties in a heavily serialized app).

PropertyCLIDescription
<DemeanorRenameTypes>--rename-typesType names: off or on.
<DemeanorRenameMethods>--rename-methodsMethod names: off or on.
<DemeanorRenameFields>--rename-fieldsField names: off or on.
<DemeanorRenameProperties>--rename-propertiesProperty names: off or on.
<DemeanorRenameEvents>--rename-eventsEvent names: off or on.
<DemeanorRenameParameters>--rename-parametersParameter names: off or on.
<DemeanorRenameEnums>--rename-enumsEnumeration type names: off or on.
<DemeanorRenameResourceNames>--rename-resource-namesManaged resource names: off or on.
<DemeanorNoSerializable>--no-serializablePreserve names of [Serializable] types and their fields.

Reporting & Incremental Builds

PropertyCLIDefaultDescription
<DemeanorReport>--reporttrueEmit a JSON name-mapping report for stack-trace decoding. Uses a default path when <DemeanorReportFile> is empty.
<DemeanorReportFile>--report <path>(auto)Optional explicit path that overrides the default report filename.
<DemeanorPriorReport>--prior-report(none)Path to a prior version’s report for incremental name stability.

Satellite Assemblies

PropertyCLIDefaultDescription
<DemeanorNoSatelliteAssemblies>--no-satellite-assembliesfalseLeave satellite assemblies untouched. Renaming without updating them makes a localized application fall back to its neutral culture at runtime.
<DemeanorSaCultures>--sa-cultures(all)Comma-separated list of cultures to process (e.g., fr,de).

Strong Name Re-Signing

PropertyCLIDescription
<DemeanorKeyFile>--keyfilePath to a .snk or .pfx file for strong-name re-signing.
<DemeanorKeyContainer>--keycontainerNamed key container for strong-name re-signing.

Output Control

PropertyCLIDefaultDescription
<DemeanorQuiet>--quiettrueSuppress non-error output. Set to false for verbose CI logs.
<DemeanorVerbose>--verbosefalsePer-phase stats and full stack traces on error.
<DemeanorDebug>--debugfalsePreserve PDB data in obfuscated output.

Exclusion Item Groups

To exclude types or assemblies from renaming without modifying source code. [Obfuscation] attributes in source are preferred for long-term maintenance. See the Exclusions Guide.

Item GroupCLIDescription
<DemeanorExclude>--excludeFully-qualified type name to exclude.
<DemeanorExcludeRegex>--xrRegex pattern for types to exclude.
<DemeanorExcludeAssembly>--xaAssembly name to skip entirely.
<DemeanorAddAssembly>--add-assemblyInclude a dynamically referenced assembly.

Example

<ItemGroup>
  <DemeanorExclude Include="MyNamespace.MyType" />
  <DemeanorExcludeRegex Include="MyNamespace\.Dtos\..*" />
  <DemeanorExcludeAssembly Include="ThirdParty.Library" />
</ItemGroup>

Complete Example

<PropertyGroup Condition="'$(Configuration)' == 'Release'">
  <!-- Obfuscation auto-enables on Release; no <Obfuscate>true</Obfuscate> needed. -->
  <DemeanorIncludeDeps>true</DemeanorIncludeDeps>

  <!-- Use incremental builds to preserve name stability -->
  <DemeanorPriorReport>$(MSBuildProjectDirectory)\prior.report.json</DemeanorPriorReport>

  <!-- Re-sign after obfuscation -->
  <DemeanorKeyFile>$(MSBuildProjectDirectory)\MyApp.snk</DemeanorKeyFile>
</PropertyGroup>

<ItemGroup>
  <!-- Exclude types that use custom reflection -->
  <DemeanorExcludeRegex Include="MyApp\.Plugins\..*" />
</ItemGroup>

Any [Obfuscation] attributes in source carry over automatically — the engine reads them from the assembly metadata.