# The Protection Map: what a build protected

The HTML report that shows what AfterPack did to your code: every token coloured by how hard it is to reverse.

Source: https://www.afterpack.dev/docs/protection-map

The Protection Map is how you verify your protection: your original source, with every token coloured by how obfuscated it became and what it would take to reverse.

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

You get this map:

*(live Protection Map demo embed — see https://www.afterpack.dev/protection-map-demo.html)*

## How to read it

Tokens are coloured by complexity score, a measure of how obfuscated each region is, targeted by [`complexity`](https://www.afterpack.dev/docs/config#complexity). The strongest transform that touched a token sets its [reversal-class](https://www.afterpack.dev/docs/reversal-classes) label. These are [static-attacker](https://www.afterpack.dev/docs/threat-model) classes: they describe what someone reading the bytes faces. Four labels:

| Colour | Label | Reversal class | What it means |
| --- | --- | --- | --- |
| Dim | **Preserved** | None | Untouched: [third-party imports](https://www.afterpack.dev/docs/config#paths-exclude), public exports, anything [excluded](https://www.afterpack.dev/docs/config#paths-exclude) or marked [`skip`](https://www.afterpack.dev/docs/config#skip). Left readable so callers still resolve. |
| Green | **Renamed & encoded** | [polynomial-injective](https://www.afterpack.dev/docs/reversal-classes) | Identifiers renamed, [strings encoded](https://www.afterpack.dev/docs/config#strings-encode). Structure recoverable with real effort; defeats AI explainers and stock deobfuscators. |
| Yellow | **Flattened & fused** | polynomial-non-injective | Control flow flattened, identities fused. An attacker recovers an equivalent computation, not your original names and values. |
| Red | **Destroyed & fused** | destructive fusion | Decomposed and fused across the bundle. A static reader recovers an equivalent program, not which original you authored. Not available; see below. |

Hover a token to see the transforms applied to it, grouped by category (for example control-flow flattening), alongside the class it lands in. [Try it in the live demo](https://www.afterpack.dev/protection-map-demo.html).

The number behind the colour is the complexity score. It flags a region that's weaker than expected, and prompts you to raise its [`preset`](https://www.afterpack.dev/docs/config#preset) or mark it [`preset=hard`](https://www.afterpack.dev/docs/config#preset).

> **A colour is not a security guarantee**
>
> A protected region resists a static read: grep, AST tooling, a blind LLM. A value the running code uses is still observable to an attacker who [runs it under an instrumented runtime](https://www.afterpack.dev/docs/threat-model#the-boundary). Keep genuinely high-value secrets [server-side](https://www.afterpack.dev/docs/threat-model#not-a-substitute-for-server-authoritative-validation). The Map shows what a static reader faces, nothing more.

## Mark high-value code

The Map shows stronger protection where you ask for it. Two [directive forms](https://www.afterpack.dev/docs/directives#writing-one) cover almost every case. An inline marker scopes the literal or expression on the rest of its line. A block marker alone on its line opens a region that the matching `/* @afterpack end */` closes; everything between is covered, whole function bodies included.

```ts
// A single literal — API keys, license secrets, sensitive constants:
const SECRET = /* @afterpack preset=extreme */ "EXPECTED_LICENSE_SECRET";

// A whole function — validators, anti-cheat, payment flow.
// The opener is alone on its line; the matching end closes the block:
/* @afterpack preset=hard */
export function checkLicense(token: string): LicenseClaims | null {
  // ...everything in here is at complexity target 25...
}
/* @afterpack end */

// A span of several statements:
/* @afterpack preset=extreme */
const claims = JSON.parse(atob(payload));
if (claims.secret !== SECRET) return null;
/* @afterpack end */
```

If a region you marked shows no more protection than the code around it, the directive didn't take effect. Usually a compiler stripped the comment before AfterPack saw it. AfterPack [reports this](https://www.afterpack.dev/docs/directives#checking-what-was-applied). The full directive grammar is in [Directives](https://www.afterpack.dev/docs/directives#writing-one); the markers used above are [`preset=hard`](https://www.afterpack.dev/docs/config#preset) and [`preset=extreme`](https://www.afterpack.dev/docs/config#preset).

## Free vs Pro

The Free baseline already renames, encodes, flattens, and fuses within a file. It spans both the green (polynomial-injective) and yellow (polynomial-non-injective) [regions](https://www.afterpack.dev/docs/reversal-classes) on its own.

[Pro](https://www.afterpack.dev/docs/pro) adds the [surgical per-region targeting](https://www.afterpack.dev/docs/directives#what-you-can-set) that lets you paint exactly the dozen functions where being wrong is expensive, plus the two hardening transforms ([`selfIntegrity`](https://www.afterpack.dev/docs/config#transforms-kind-enabled), [`comparisonHardening`](https://www.afterpack.dev/docs/config#transforms-kind-enabled)). Free covers the bulk of a real codebase well; Pro is what you reach for around license checks and secrets. See them side by side in [Tiers](https://www.afterpack.dev/docs/tiers).

The red class is not available. See [Reversal classes](https://www.afterpack.dev/docs/reversal-classes).

## Generate yours

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

You get the same interactive view rendered against your own source: a static HTML file, no login or [telemetry](https://www.afterpack.dev/docs/config#telemetry-enabled), that you can attach to a PR. The Map renders your original source, so it is written only when your build emitted a source map for the engine to chain. It is opt-in otherwise: pass [`protectionMap: { enabled: true }`](https://www.afterpack.dev/docs/config#protectionMap-enabled) in your plugin options and the plugin turns the bundler's map on for you. Pass [`--protectionMap.enabled=false`](https://www.afterpack.dev/docs/config#protectionMap-enabled) to skip it. Point at your build directory: [Next.js](https://www.afterpack.dev/docs/frameworks/nextjs) writes to `.next/`, [Nuxt](https://www.afterpack.dev/docs/frameworks/nuxt) to `.output/`, most bundlers to `dist/`.

## Where your map lives

A [local run](https://www.afterpack.dev/docs/deployment-modes) writes the map into [`.afterpack/`](https://www.afterpack.dev/docs/cli#files) on your machine and sends nothing anywhere. Add that directory to your `.gitignore`. The CLI refuses to write the map into a served path. A published Protection Map contains your full source. Never publish it.

A [Pro cloud build](https://www.afterpack.dev/docs/deployment-modes) works differently. The map is produced by the cloud engine that did the obfuscation, the only engine that honours your [`preset=hard`](https://www.afterpack.dev/docs/config#preset), [`preset=extreme`](https://www.afterpack.dev/docs/config#preset), and [`skip`](https://www.afterpack.dev/docs/config#skip) directives, so it is the only map that can show their effect. AfterPack keeps it, so you open any recent build's map from the [dashboard](https://www.afterpack.dev/docs/dashboard/builds) instead of regenerating it.

> **A stored map contains your original source**
>
> A Protection Map is your code, coloured. On a Pro cloud build it is stored on AfterPack's infrastructure and readable by **every member of the [workspace](https://www.afterpack.dev/docs/dashboard/workspaces) that owns the project**. If your source shouldn't be readable workspace-wide, turn it off. See below.

What is stored, exactly:

- **The map data for each build**: your original source plus the per-token metrics behind the colours, in Cloudflare R2, encrypted at rest with AES-256, alongside a row of summary numbers (coverage, inflation, weak-spot counts) that the dashboard charts. Your obfuscated output is not stored.
- **Scoped to one project**, the one the build's [key](https://www.afterpack.dev/docs/dashboard/api-keys) is bound to.
- **Readable only by members of that workspace.** Not public, not indexed, no shareable link.
- **Kept for one year, and for the last 500 builds per project, whichever runs out first.** An hourly job deletes whatever is past either bound. The one-year window supports comparing a release against the one before it. The 500-build cap keeps a project that builds many times a day from accumulating a year of source copies; for such a project, that cap is the bound that bites first.
- **A build that could not store a map leaves a note instead**, recording the reason and the sizes involved, never your code and never a file name. It is kept and deleted on the same schedule, and the dashboard shows that build as *not stored* rather than as one that never ran.
- **Past that window, the dashboard says so.** Notes and maps age out together; your build history does not. A build older than the retention above reads as *no record*.

### Turning it off

Storing maps is on by default, per project. A [workspace](https://www.afterpack.dev/docs/dashboard/workspaces) admin can switch it off in the project's settings. After that, new cloud builds for that project generate and store nothing; maps already stored age out under the retention above rather than vanishing immediately. Local Protection Maps are unaffected either way: that path never touches the network.

**Your [dashboard charts](https://www.afterpack.dev/docs/dashboard/builds) keep working.** Turning this off stops retaining your source. It does not stop recording what a build measured. Coverage, inflation, complexity, and build time contain no code, and they continue to be recorded and charted for a project with capture switched off. The weak-spot count is the one exception, because it is counted inside a stored map. The obfuscated output is unchanged.

A stored map is the only thing AfterPack keeps at rest that contains your source. Everything else about a [cloud build](https://www.afterpack.dev/docs/cloud-api) stays in memory for the length of one request. See [Privacy & data handling](https://www.afterpack.dev/docs/privacy).

## Next

- [Reversal classes](https://www.afterpack.dev/docs/reversal-classes): the three classes behind these colors, per-transform.
- [Best practices](https://www.afterpack.dev/docs/best-practices): patterns for marking the right regions.
- [Configuration](https://www.afterpack.dev/docs/config): directive grammar, per-file rules, presets.
- [How AfterPack works](https://www.afterpack.dev/docs/concepts): the three reversal classes in depth.
- [Threat model](https://www.afterpack.dev/docs/threat-model): the static-vs-dynamic axis these labels assume.
- [Privacy & data handling](https://www.afterpack.dev/docs/privacy): what a cloud build stores, for how long, and who can read it.
- [Directives](https://www.afterpack.dev/docs/directives): every marker you can put in front of a region.
- [Pro](https://www.afterpack.dev/docs/pro): what unlocks per-region marking and stored maps.
- [Live demo](https://www.afterpack.dev/protection-map-demo.html): the same view, on sample source, with nothing to install.
