Introduction
Submit a compiled Jar with your RR configuration file, then download the protected output from your build tools.
Before you start
You need an approved account, an API key and an RR configuration file.
API requests, RR configuration and protection options in one page.
Submit a compiled Jar with your RR configuration file, then download the protected output from your build tools.
You need an approved account, an API key and an RR configuration file.
Create a key to authenticate requests from your build tools
API keys authenticate requests to Hashira. Store each key in your server environment and keep it private.
Create a separate key for each integration. You can then disable one integration without interrupting the others. Your account must be approved before you can create keys.
Create your key from the API keys page in your Hashira account
Sign in and select API keys from the sidebar
The Add API Key dialog opens without leaving the page
Choose a name you will recognize later, such as Release builds.
Review the access permission and choose when the key expires.
The key appears once the request completes.
Copy the key before closing the dialog. You cannot retrieve the full key later.
Set the key in your server environment then include it as a Bearer token in the Authorization header
export hashirapikey="your-api-key"curl --fail-with-body "https://api.hashisoft.com/v1/account" \
--header "Authorization: Bearer $hashirapikey"Disable a key to stop requests from an integration. Delete it when you no longer need it. If a key is exposed, replace it and update the affected tools.
Create an API key and store it in your server environment or CI secret store.
export hashirapikey="your-api-key"Include the key in the Authorization header on every request, including status checks and downloads.
curl --fail-with-body "https://api.hashisoft.com/v1/account" \
--header "Authorization: Bearer $hashirapikey"Keep keys out of browser code and public repositories. New keys are shown once. Disable or delete a key when it should no longer be used.
Submit hi.jar with your RR file and download the protected build.
Have your API key and local RR file ready. You can use any filename such as release.rr.
Save your settings as release.rr or choose another filename. Update the config file path in the upload command.
Send the Jar and RR files together. Hashira returns HTTP 202 with the new build record.
curl --fail-with-body "https://api.hashisoft.com/v1/obfuscations" \
--header "Authorization: Bearer $hashirapikey" \
--form "file=@hi.jar" \
--form "config=@release.rr"Set jobId to the id returned by the upload request, not a configuration ID. Check the status until it is completed or failed.
curl --fail-with-body "https://api.hashisoft.com/v1/obfuscations/$jobId" \
--header "Authorization: Bearer $hashirapikey"When the build is completed, save the output as hi-obf.jar.
curl --fail-with-body "https://api.hashisoft.com/v1/obfuscations/$jobId/output" \
--header "Authorization: Bearer $hashirapikey" \
--output hi-obf.jarKeep your settings in an RR file and upload it with each build.
The API takes two files: your compiled Jar and a UTF8 RR configuration. There is no configuration ID to create or select. Name the configuration cat.rr, release.rr or any other filename.
Download this example and edit the engine block. Keep format 1 and the Lithium profile declaration. The API assigns input, output and workspace paths inside each job, so local Windows paths are unnecessary.
Download api.rrformat 1;
use "lithium";
engine ^ {
skipFlow false;
skipString false;
skipArithmetic false;
linkedIndy true;
chainedControlFlow true;
stringProtectionProfile "strong";
}Use false for skipFlow, skipString and skipArithmetic to enable those passes. Set a skip option to true to leave that part unchanged. Positive controls such as linkedIndy and chainedControlFlow use true to enable the feature.
The worker enables bytecode verification, disables application smoke runs and signing, and uses a 90 second engine budget. A separate validation block and a local buildWorkspace are not accepted. Native protection and unsupported advanced options return an error rather than being silently ignored.
Upload the Jar as file and the RR file as config. The returned id identifies that build only. The configHash identifies the uploaded file contents; it is not a configuration ID and is not sent in later uploads. To change protection, edit your RR file and submit both files again.
format 1; selects the RR syntax version. use "lithium"; loads the defaults bundled with your engine. Lithium is a base profile, not a separate configuration file you need to download.
Settings omitted from this file still inherit their values from Lithium. The bundled profile selects Java protection and disables native virtualization. Its defaults can differ between engine versions.
engine ^ overrides inherited engine settings. The caret is required when replacing a value supplied by the profile. Command line overrides take precedence over the file.
This extended example contains local engine settings. The API overrides its time budget and safety settings. Download the file or copy it below. For a local build, update input and output to your Jar paths. The seed is already supplied.
format 1;
use "lithium";
project {
input "hi.jar";
output "hi-obf.jar";
seed "96be4325b54ce68bb498693f97c42d82d78d7436e79501f5abd45ece9191befe";
}
engine ^ {
dictionary "MORVAY_UNICODE";
skipReflection false;
skipConstructorIndy false;
packageRoot "hashira";
expressionSteps 0;
expressionClasses 5;
compactAppearance true;
decimalAppearance true;
linkedIndy true;
chainedControlFlow true;
aggressiveFlow true;
bozarFlow true;
bozarConst true;
skipFlow false;
skipString false;
skipArithmetic false;
stringProtectionProfile "strong";
appearanceMethod "";
appearanceText "اللَّهُ لَا إِلَٰهَ إِلَّا هُوَ الْحَيُّ الْقَيُّومُ ۚ لَا تَأْخُذُهُ سِنَةٌ وَلَا نَوْمٌ ۚ لَهُ مَا فِي السَّمَاوَاتِ وَمَا فِي الْأَرْضِ ۗ مَنْ ذَا الَّذِي يَشْفَعُ عِنْدَهُ إِلَّا بِإِذْنِهِ ۚ يَعْلَمُ مَا بَيْنَ أَيْدِيهِمْ وَمَا خَلْفَهُمْ ۖ وَلَا يُحِيطُونَ بِشَيْءٍ مِنْ عِلْمِهِ إِلَّا بِمَا شَاءَ ۚ وَسِعَ كُرْسِيُّهُ السَّمَاوَاتِ وَالْأَرْضَ ۖ وَلَا يَئُودُهُ حِفْظُهُمَا ۚ وَهُوَ الْعَلِيُّ الْعَظِيمُ";
writeDiagnostics true;
signOutput false;
buildTimeBudgetSeconds 900;
}input is the source Jar and output is the protected Jar. seed supplies the build seed. dictionary selects MORVAY_UNICODE and packageRoot sets hashira as the renamed package root.
skipFlow false enables flow protection. chainedControlFlow, aggressiveFlow and bozarFlow enable the additional flow passes. linkedIndy true enables linked dynamic calls. skipReflection false and skipConstructorIndy false allow their respective reference and constructor call passes.
skipString false enables string encryption and stringProtectionProfile "strong" selects the Strong profile. skipArithmetic false enables arithmetic protection and bozarConst true enables constant transformation.
expressionSteps 0 sets expression depth to zero. It does not disable every arithmetic pass. expressionClasses 5 sets the expression family count independently.
compactAppearance, decimalAppearance, appearanceMethod and appearanceText configure generated helper appearance. The text in this preset is a literal appearance value, not a secret or an encryption key.
writeDiagnostics true enables build diagnostics. signOutput false disables output signing. buildTimeBudgetSeconds 900 sets the build time budget to 15 minutes.
For skip controls, false enables the pass and true skips it. Positive controls such as linkedIndy use true to enable the feature. Keep these two conventions separate.
Hardening Level ranges from 0 to 5. Expression depth ranges from 0 to 8. These settings control different transformations and are not a single protection score. More aggressive settings can increase output size, build time and runtime cost.
Preserve names used by reflection, frameworks and external callers through exclusions. Native protection requires a matching runtime and toolchain; enabling it changes the deployment requirements.
The protection guides below list individual option names and supported panel values. Their panel defaults are not the resolved Lithium defaults.
Save your protection settings in an RR file such as release.rr. The filename is your choice. The supplied catolog.rr is an example you can rename.
Send your Jar in the file field and your RR file in the config field. No saved configuration ID is required.
Use the returned job ID to check progress and download the protected Jar.
curl --fail-with-body "https://api.hashisoft.com/v1/obfuscations" \
--header "Authorization: Bearer $hashirapikey" \
--form "file=@hi.jar" \
--form "config=@release.rr"Each job keeps the uploaded settings. The configHash response field is a SHA256 digest of the uploaded configuration. Editing your local file does not change an existing job.
Look up a build with the job ID returned during submission. Only the account that created the build can access it.
/v1/obfuscations/:idcurl --fail-with-body "https://api.hashisoft.com/v1/obfuscations/$jobId" \
--header "Authorization: Bearer $hashirapikey"queuedprocessingcompletedfailedThe response includes id, configHash, status, createdAt, completedAt and error. Fields that are not yet available may be omitted.
Download the completed build using the same account credentials used to submit it
/v1/obfuscations/:id/outputcurl --fail-with-body "https://api.hashisoft.com/v1/obfuscations/$jobId/output" \
--header "Authorization: Bearer $hashirapikey" \
--output hi-obf.jarThe response contains the Jar file. An unfinished build returns HTTP 409. Completed jobs expire after about one hour, so download the output before it is removed.
Change how branches and loops connect
Eligible methods are rewritten around encoded dispatchers. Use the settings below to select the flow passes and control how much state they generate.
These entries belong in the engine block of your RR configuration.
engine ^ {
skipFlow false;
chainedControlFlow true;
aggressiveFlow true;
}Hardening levels range from 0 to 5. A higher level can increase bytecode size and processing cost. Dispatcher state cells control generated state and do not represent a protection score.
For options beginning with skip, false enables the pass.
Protect string constants
Select a decoder backend and a protection profile for strings in your application. String protection is independent of call routing and arithmetic mutation.
These entries belong in the engine block of your RR configuration.
engine ^ {
skipString false;
nativeStringVault false;
stringProtectionBackend "java-heterogeneous";
}Use java-heterogeneous for Java decoders or legacy for the older backend. Normal, Strong and Critical select different protection profiles. Higher settings can add work during startup and string decoding.
For options beginning with skip, false enables the pass.
Protect calls and calculations
Configure InvokeDynamic routing, reference protection and instruction mutation. Each pass has its own control so you can enable only the transformations your application needs.
These entries belong in the engine block of your RR configuration.
engine ^ {
linkedIndy true;
skipReflection false;
skipConstructorIndy false;
}The condy backend requires ConstantDynamic support on the target JVM. Expression depth and expression families control different parts of arithmetic generation. Increasing either can add bytecode and execution cost.
For options beginning with skip, false enables the pass.
Configure native protection
Select the native backend, runtime target and method coverage for a local build. Native protection requires a matching runtime for the platform that runs your application.
These entries belong in the engine block of your RR configuration.
engine ^ {
nativeVm.enabled false;
nativeVm.backend "legacy";
nativeTarget "windows-x86_64";
}Native options take effect when Native VM is enabled. Prebuilt mode uses an existing runtime. Per-build compilation requires a compatible native compiler toolchain.
Control names and retained metadata
Choose which identifiers and metadata Hashira changes. Preserve names that external code, reflection or framework configuration relies on.
These entries belong in the engine block of your RR configuration.
engine ^ {
skipNaming false;
dictionary "MORVAY_UNICODE";
packageRoot "hashira";
}Annotations, generic signatures and META-INF resources can be used at runtime. Keep the metadata your application needs and use exclusions for names that must remain stable.
For options beginning with skip, false enables the pass.
Add runtime checks
Configure debugger guards, integrity checks and class protection. These controls affect the protected application at runtime and remain separate from build-time bytecode verification.
These entries belong in the engine block of your RR configuration.
engine ^ {
antiDebug false;
selfVerify true;
runtimeIntegrity false;
}Debugger guards can affect diagnostics and development tools. Integrity checks can reject changes made after protection. Enable the checks that match how you distribute and run your application.
Control build output
Configure diagnostics, verification and optional post-build tasks. The build report records the transformations applied and any methods the engine skips.
These entries belong in the engine block of your RR configuration.
engine ^ {
writeDiagnostics true;
skipVerify false;
smokeRun false;
}The remote Java worker enables bytecode verification and disables smoke runs and output signing. Local builds use the settings supported by your engine distribution.
For options beginning with skip, false enables the pass.
These additional identifiers are recognized by the bundled RR schema. Availability depends on the selected backend.
accessTransformeradversarialReleaseGatearenaDecoyBudgetBytesarenaDecoySurfacearenaPackagearenaPoisonarenaPoisonBudgetBytesartifactSignatureautoRepairOnSmokeFailautoRepairOnVerifyFailbogusVmNamebootstrapDiskExtractionbootstrapModebootstrapStageAeadbootstrapVisibleStagesbozarCrasherbrandDecoyDensitybrandDecoysbuildChannelbuildTimeBudgetbuildWorkspacecarrierKeyPolicycarrierModecarrierOnlineUnsealEnabledcarrierTempExtractionchangeLogclassFolderclosedDomainconstPoolClassNameconstPoolResourcecontrolFlowcrashAllClassescrashRelocatePackagecustomTransformerJardecompileHardeningdecoyClassdecoyPackagedestroyCrashedBytecodedistributionOutputdynamicConstantsfabricEntrypointNamesOnlyfieldReferenceEncryptionflowLevelhardCrashAllClasseshelperlessJavahybridMaxMethodsPerClasshybridMinInstructionshybridProtectExplicitOnlyhybridProtectionhybridSelectioninnerClassRemoverintegrityVerifierNamejavaHardeningjavaHardeningArithmeticjavaHardeningFlowLeveljavashroudEnginelayerslegacyShellBozarlineNumberTransformerliveOperandMbalocalVariableTransformerlockHwidlookalikeIlWeightlookalikeO0WeightmanualPeMappingmbaIntensitynativeCompilernativeDomainBackendnativeExportPolicynativeLiftOnlynativeMaxBinaryBytesnativeOpcodeDomainnativePayloadMaxRationativeProgramStoragenativeRecordLayoutnativeSignerThumbprintnativeStringVaultOnlynativeSuperOpsnativeTimestampUrlnativeVmBackendPolicynativeVmRequireNativeResolvernativeVmRequireProgramRecordsnativeVmResourcehashiraHelperNamenopRemovernumberObfuscationopaquePredicatesoracleSensitiveparserPoisonBudgetBytespayloadSealpreferredClassNamespreserveClasspreserveFieldNamesprimaryVmPackageprivateArtifactIdprivateContextStringsprivateLicensePublicKeyprivateNativeGuardprivateVmFactoryprivateVmFactorySeedramWorkspaceRootreferenceEncryptionreferenceImportanceClassifierrenameClassMaxLengthrenameClassMinLengthrenameDictionaryFilerenameMemberMaxLengthrenameMemberMinLengthrenamerresourceEncryptionruntimeAutoRepairruntimeDecoyPackageruntimePayloadStage2runtimeSilentSabotageruntimeWritePolicyseedDictionaryLayoutshuffleMemberssidecarBootstrapClassNameskipStringObfuscationskipToolTargetedCrashersmokeArgssmokeExpectExitsmokeInputsmokeJavaArgssmokePassOutputContainssmokeTimeoutSecondssourceTransformerstrengthModestringCallOptimizerstringEncryptionstringFamilystringPolicystringProtection.backendstringProtection.profilesurfaceModevisualAsciiJunkvmBlockInfoNamevmChunkReaderNamevmInterpreterNamevmProgramResourcevmExtensionvmProtectionworkspacePolicyappearanceMethodappearanceTextUpload a compiled Jar using multipart form data. A successful request returns HTTP 202 with the new build record.
/v1/obfuscationscurl --fail-with-body "https://api.hashisoft.com/v1/obfuscations" \
--header "Authorization: Bearer $hashirapikey" \
--form "file=@hi.jar" \
--form "config=@release.rr"fileconfigInclude each field once. Additional fields are rejected. Let your multipart client set the Content-Type header and boundary.
Look up a build with the job ID returned during submission. Only the account that created the build can access it.
/v1/obfuscations/:idcurl --fail-with-body "https://api.hashisoft.com/v1/obfuscations/$jobId" \
--header "Authorization: Bearer $hashirapikey"queuedprocessingcompletedfailedThe response includes id, configHash, status, createdAt, completedAt and error. Fields that are not yet available may be omitted.
Download the completed build using the same account credentials used to submit it
/v1/obfuscations/:id/outputcurl --fail-with-body "https://api.hashisoft.com/v1/obfuscations/$jobId/output" \
--header "Authorization: Bearer $hashirapikey" \
--output hi-obf.jarThe response contains the Jar file. An unfinished build returns HTTP 409. Completed jobs expire after about one hour, so download the output before it is removed.
Check the HTTP status and error field before retrying a request.
Jar uploads are limited to 32 MiB and RR files to 64 KiB. Output files are limited to 512 MiB. Each account can have one queued or processing build at a time.
400404409413429503