Use presets for a quick start, then override individual HTTP API options from config or CI when a release needs tighter behavior.
Inspect presets from the CLI
jso-protector --list-presets
jso-protector --list-presets --json
standard Core string encoding, string movement, name replacement, and compression. |
balanced Adds short local names, deep obfuscation, code transposition, string encryption, and flat transform. |
maximum Adds member/global renaming, member movement, and low dead-code insertion. |
Inspect option names
jso-protector --list-options
jso-protector --list-options --json
The JSON output is useful for internal release tooling that wants to render approved option choices or validate CI overrides.
Inspect resolved config
Use --validate-config --json to catch malformed fields, missing credentials, invalid endpoint URLs, bad size budgets, and likely option-name typos before a release job runs.
jso-protector --config jso.config.json --validate-config --json
Use --print-config --json to see merged settings without leaking dashboard credentials.
jso-protector --config jso.config.json --print-config --json
Common option groups
Strings
EncodeStrings, MoveStrings, EncryptStrings, SplitStrings, SplitStringsChunkLength |
Names
ReplaceNames, RenameGlobals, RenameMembers, VariableExclusion, IdentityStyle |
Compression
SelfCompression, SelfCompressionMinSize, CompressionRatio |
Control flow
DeepObfuscate, ReorderCode, FlatTransform, AddDeadCode, DeadcodeLevel |
Locks
LockDomain, LockDomainList, LockDomainMsg, LockDate, LockDateValue, LockDateMsg, LockStartDate, LockStartDateValue, LockStartDateMsg, LockBrowser, LockBrowserList, LockOS, LockOSList |
Output and runtime
OptimizationMode, WriteFormats, WriteFormats_KeepIndent, WriteFormats_LineNumbers |
Runtime defense
DebugProtection, SelfDefending, RuntimeDefenseAction, RuntimeDefenseCallback, RuntimeDefenseBeaconUrl, RuntimeDefenseBeaconToken, DetectHeadlessBrowser |
Runtime locks
RuntimeSessionToken, RuntimeSessionVariable, RuntimeFingerprintAllow, RuntimeChallengeVariable, RuntimeSignedEnvelopeVariable |
See Runtime Defense for callback, beacon, fingerprint, challenge, and signed-envelope examples.
LockBrowserList accepts chrome, edge, firefox, safari, opera, and ie. LockOSList accepts windows, macos, linux, android, and ios. When available, the guards cross-check User-Agent Client Hints with legacy User-Agent/platform signals and route contradictions through the configured runtime-defense action. An attacker can still align all browser-visible signals; use these as licensing policy controls, not as a hardware trust boundary.
Choosing between the three presets
The presets are cumulative, so the question is where to stop rather than which to pick. A useful way to decide is by what your code would cost you if it were read:
- standard — the right default for an ordinary web app. It removes the free reading of your names, strings and structure. Nothing here changes an interface, so it is also the safest to adopt without a large exclusion list.
- balanced — for code where someone has a concrete motive to understand it: a paid front end, a licensed widget, an algorithm you sell. Control-flow work means beautifying the output no longer restores a readable program.
- maximum — for code that is the product. It adds member and global renaming, which change interfaces, so this is the tier where you must decide explicitly which names are public. Start from Protect Members before enabling it on a codebase you did not write.
Presets are a starting point, not a policy. The usual shape of a mature configuration is a lower preset applied broadly plus a higher option set scoped to the two or three modules that matter — inline directives and per-path config are how you express that.
Options that change an interface
Most options are contained: they change a file and the file still presents the same surface to the outside world. Four are different, and they are the ones worth reviewing before a release rather than after:
RenameMembers — renames object properties project-wide. Anything reading a property by its original name (a server response, stored data, a template binding) breaks. See Protect Members.
RenameGlobals — the same decision one scope up. See Replace Globals.
MixedServer — changes which files are eligible for processing at all, and mixed files are more fragile than plain JavaScript.
- The
Lock* family — adds runtime conditions under which your code deliberately refuses to run. Correct behaviour and a production outage look identical from a log line, so configure the message and test the failing path.
Pair any of these with --reserved-name patterns for the public surface, and validate before release with compatibility validation.
Making a configuration reproducible
Two options exist specifically to make a release auditable rather than to protect anything. Seed fixes the entropy so the same input and options produce byte-identical output — see reproducible builds. --print-config --json records exactly what the run resolved to, credentials excluded, which is the artifact to attach to a release ticket. Together they turn “this is the build we shipped” from an assertion into something you can re-derive.
Override from CI
jso-protector --config jso.config.json --option LockDomain=true --option LockDomainList=example.com
Repeat --reserved-name to preserve public entry points without editing shared config.
jso-protector --config jso.config.json --reserved-name "^PublicApi$" --reserved-name "^renderWidget$"
Frequently asked questions
How do I find out which options exist without reading the whole reference?
The command line exposes them. There are subcommands that list the available presets, list the option names, and print the fully resolved configuration for a given input. The resolved output is the most useful of the three when something is not behaving as expected, because it shows what the tool actually decided after presets, config file and command line overrides were merged.
How do the three presets differ?
They sit at increasing points on the trade between reversal cost and runtime cost. The lighter preset concentrates on renaming and is close to free at run time. The middle preset adds string handling and structural change. The heaviest adds the transforms that alter control flow and add the most startup work. Choose by measuring your own bundle rather than by name, because the right answer depends on how much of your code is on a hot path.
Which options can change a public interface?
The ones that rename things other code depends on: global renaming and member renaming in particular. If another script, a template, a test or a server response refers to a name by string, renaming it breaks the connection with no build error. Those options are opt-in for that reason, and the reserved name settings exist to pin the names that must survive.
How do I keep names stable across two builds?
Use a fixed seed, which makes generated names reproducible for the same input and configuration. This matters more than it first appears: delta updates, long-term caching and any workflow that diffs consecutive releases all degrade when every identifier changes on every build. It also trades against the churn that makes an attacker's patch break each release, so decide which of the two you need.
Can I override configuration from a continuous integration job?
Yes, and that is the intended arrangement. Keep the configuration file in the repository as the source of truth and pass overrides on the command line for the things that legitimately vary between environments. Print the resolved configuration in the job log so that a build which behaved unexpectedly can be explained afterwards without reproducing it.
What is the safest order for these options when adopting them?
Enable renaming first and confirm the test suite passes against the protected artifact, then add string handling, then anything structural, one change at a time. Adding several transforms at once and finding a failure means bisecting configuration under pressure, which is avoidable by construction.