# Obfuscation presets

The five protection levels from minify to extreme, the complexity target each one sets, and what the zero-config default (light) already guarantees.

Source: https://www.afterpack.dev/docs/presets

A preset is a named set of transforms with a known size and runtime cost. A heavier preset applies more transformation; it is not a measured step up in protection. One name sets the [complexity target](https://www.afterpack.dev/docs/config#complexity); output size follows from that target. Setting [`complexity`](https://www.afterpack.dev/docs/config#complexity) explicitly overrides the target, and a value set directly always applies to every token, even on `light`.

*(preset ladder diagram — see https://www.afterpack.dev/docs/presets)*

## The ladder

| Preset | [`complexity`](https://www.afterpack.dev/docs/config#complexity) | Typical gzipped size | [String floor](https://www.afterpack.dev/docs/config#strings-encode) | Reach for it when |
|---|---:|---:|:---:|---|
| `minify` | `0` | `1.09x` to `1.17x` | off | No obfuscation. Identifiers are renamed and the code is compressed; string literals stay readable. |
| `light` | `5` | `1.29x` to `1.60x` | on | **Default.** Adds string encoding to `minify` and rewrites syntax, with no structural layers. The right baseline for most projects. |
| `medium` | `7` | `6.04x` to `8.33x` | on | Production code you want meaningfully harder to follow. |
| `hard` | `12` | `6.62x` to `9.23x` | on | Code that matters: pricing, gating, license checks, client-side business logic. |
| `extreme` | `25` | `8.25x` to `11.19x` | on | The highest applied complexity. Aim it at a file or [region](https://www.afterpack.dev/docs/directives#what-you-can-set) rather than a whole bundle. |

Gzipped output measured against the same file already minified, on five real minified production bundles, with AfterPack 0.2.6 on 2026-10-16; [Performance](https://www.afterpack.dev/docs/performance#output-size) has the method.

The string floor is on for any positive target.

## The default is `light`

Run [`npx afterpack@latest dist/`](https://www.afterpack.dev/docs/cli#usage) with no [`--preset`](https://www.afterpack.dev/docs/config#preset), or install a [plugin](https://www.afterpack.dev/docs/frameworks) and configure nothing, and you get `light`.

```bash
npx afterpack@latest dist/
```

```bash
npx afterpack@latest dist/ --preset=hard    # step up
```

[Flags](https://www.afterpack.dev/docs/config) join their value with `=`. A space-separated value is rejected as an unknown flag.

`light` is the zero-config floor. Every string literal it can rewrite goes through a runtime decoder. It renames identifiers and rewrites syntax, and it adds no structural layers. From engine 0.2.3, loops are no longer rewritten into callback helpers at `minify` and `light`, which removes the 2-5x slowdown those helpers caused in hot loops. At `light`, property and global names still go through encoded constants, which has some cost in very hot code. Some strings stay readable at every preset: directive prologues, `typeof` comparands, property names the runtime needs, and export names. The [Protection Map](https://www.afterpack.dev/docs/protection-map) lists them, and the engine reports the count and byte total on every build. Raising the preset to `medium` or above adds structural work on every token: inflation, control-flow reshaping, and strings and integers keyed to a shared runtime value. It does not change string readability, which is already at its floor.

`minify` is the one rung that turns the floor off. From engine 0.2.3, it only renames identifiers and compresses the code, with no obfuscation applied. Engine 0.2.2 also rewrote syntax at `minify`.

## Size budget

Every output carries the engine's fixed runtime overhead — the decoders and the integer-expression interpreter — so a file of a few hundred bytes comes out proportionally larger than the ranges above suggest. Read a ratio on a real bundle, not a small file.

[`inflation.max`](https://www.afterpack.dev/docs/config#inflation-max) is unset by default, so the complexity target alone decides the output size. Set it yourself when you have a real size budget you would rather fail than exceed. Hitting an explicit value is an error: [`DIAG_SIZE_CAP_REACHED`](https://www.afterpack.dev/docs/diagnostics#complete-reference), no output, [exit `3`](https://www.afterpack.dev/docs/diagnostics#exit-codes) from the CLI, and a failed build from a plugin. From engine 0.2.3, under-protected output is never written in its place: the finished file is checked against the limit, and string encoding always completes first. In engine 0.2.2, a tight limit could stop string encoding early and leave some strings readable.

## Setting the target directly

[`complexity`](https://www.afterpack.dev/docs/config#complexity) is an unbounded non-negative number. [`--complexity=200`](https://www.afterpack.dev/docs/config#complexity) is a valid [Free-tier](https://www.afterpack.dev/docs/tiers) override, well past what `extreme` sets.

```bash
npx afterpack@latest dist/ --preset=hard --complexity=40
```

That is `hard` with its complexity target overridden to 40; nothing else about the preset changes. Passing a preset name to [`--complexity`](https://www.afterpack.dev/docs/config#complexity) is an error.

## Applying a preset to one region

`--preset` sets the whole bundle uniformly; that is [Free](https://www.afterpack.dev/docs/tiers). Concentrating a heavier preset on one function or file is a [Pro](https://www.afterpack.dev/docs/pro#what-pro-buys) feature, through a [source directive](https://www.afterpack.dev/docs/directives):

```js
/* @afterpack preset=extreme */
function pricingEngine(plan, region) {
  return resolve(plan, region);
}
/* @afterpack end */
```

Directives are [block comments only](https://www.afterpack.dev/docs/directives#writing-one), read from your source by the plugin. See [Directives](https://www.afterpack.dev/docs/directives) for the grammar and the tier rules.

## Next

- [Configuration](https://www.afterpack.dev/docs/config): every key a preset touches.
- [CLI reference](https://www.afterpack.dev/docs/cli): the flags that select a preset.
- [Directives](https://www.afterpack.dev/docs/directives): [`skip`](https://www.afterpack.dev/docs/config#skip) or [`preset=<name>`](https://www.afterpack.dev/docs/config#preset) for one span.
- [How AfterPack works](https://www.afterpack.dev/docs/concepts): what a higher target actually buys against a static reader.
- [Best practices](https://www.afterpack.dev/docs/best-practices): what to push harder and what to leave alone.
