# AfterPack CLI reference

Invoking npx afterpack@latest: an optional path, every config key as a flag, the verify and audit subcommands, and the full exit-code contract.

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

`npx afterpack@latest` obfuscates a build directory. Use it when there is no plugin for your bundler, or in [CI](https://www.afterpack.dev/docs/builds). If a plugin exists for your framework, install `@afterpack/<fw>` instead: your normal build then emits obfuscated output with no separate step. See [Frameworks](https://www.afterpack.dev/docs/frameworks).

This page covers the [argument](#usage), [examples](#examples), [what a run does](#what-a-run-does), [`verify`](#afterpack-verify-dir), [`audit`](#afterpack-audit-url), [machine-readable output](#machine-readable-output), and the [exit codes](#exit-codes). Every option is defined once, on [Configuration](https://www.afterpack.dev/docs/config).

## Usage

```
afterpack [path] [--<key>=<value> ...] [-h | --help] [-v | --version]
afterpack verify [dir]
afterpack audit <url>
```

```bash
npx afterpack@latest
```

The path takes either:

- a directory, walked recursively for every `.js`, `.mjs` and `.cjs`, or
- a single `.js`, `.mjs` or `.cjs` file, obfuscated on its own.

Run without a path and `afterpack` detects your build output: the directory a bundler named in your `package.json` conventionally writes (Next → `.next/`, Nuxt → `.output/`, everything else → `dist/`) when that directory exists, else the newest of `dist/`, `build/`, `out/`, `.output/` and `.next/`. It never reads your bundler's own config file, so a custom output directory is not detected — pass the path yourself when you have one. It prints what it found and what it will do, then runs; in CI it never prompts. When nothing is found it prints the quickstart help and exits `1`. The command also reads your bundler from `package.json` and prints a one-line nudge toward the [matching plugin](https://www.afterpack.dev/docs/frameworks) at the end of a run.

Point it at your bundler's output, not your source. Most bundlers ([Vite](https://www.afterpack.dev/docs/frameworks/vite), [Rollup](https://www.afterpack.dev/docs/frameworks/rollup), [esbuild](https://www.afterpack.dev/docs/frameworks/esbuild), [webpack](https://www.afterpack.dev/docs/frameworks/webpack), [Astro](https://www.afterpack.dev/docs/frameworks/astro), [Parcel](https://www.afterpack.dev/docs/frameworks/parcel)) write to `dist/`. [Next.js](https://www.afterpack.dev/docs/frameworks/nextjs) writes to `.next/`, [Nuxt](https://www.afterpack.dev/docs/frameworks/nuxt) to `.output/`.

## Examples

```bash
npx afterpack@latest                                        # detects the build output, light default
npx afterpack@latest dist/                                  # a directory you choose
npx afterpack@latest dist/ --preset=hard                    # heavier posture
npx afterpack@latest .next/ --seed=git                      # reproducible output, pinned to the commit
npx afterpack@latest build/widget.js                        # one file
npx afterpack@latest dist/ --diagnostics.level=all          # every diagnostic, not the rolled-up summary
AFTERPACK_complexity=40 npx afterpack@latest dist/           # any flag also works as an env var
```

## What a run does

1. Collects the JavaScript at `<path>`. It recurses when the path is a directory, skips its own [`.backup.<hash>` copies](#files), and skips `node_modules/` unless you include it with [`--paths.include='**/node_modules/**'`](https://www.afterpack.dev/docs/config#paths-include). If nothing is collected, the build fails.
2. Adds the artifact guard globs to the working directory's `.gitignore` if they are missing: `.afterpack/`, `*.protectionMap.html`, `protectionMap.html`, `*.backup.*`, `*.map`.
3. Obfuscates every collected file in place, at the requested [preset](https://www.afterpack.dev/docs/config#preset), or at [`--complexity`](https://www.afterpack.dev/docs/config#complexity) when you set one explicitly.
4. Writes the artifacts the flags allow, and always the [protection receipt](#files). The combined [Protection Map](https://www.afterpack.dev/docs/protection-map) always lands in `.afterpack/protectionMap.html`, gitignored and never served, because it embeds your original source. A source map or backup contains your full source. Never deploy either. If one lands under a served path segment, the CLI warns once.
5. Prints [diagnostics](https://www.afterpack.dev/docs/diagnostics), then the bundler nudge if it found one.

> **Runs are not idempotent**
>
> The command overwrites your build output in place and keeps no copy to re-obfuscate from. Build, then run it once. In [CI](https://www.afterpack.dev/docs/builds) that ordering is automatic.

### Do not run it twice on the same output

AfterPack refuses to obfuscate a file that already carries its own obfuscated output from a prior run: it matches the collected files against the [protection receipt](#afterpack-verify-dir) by content hash and fails the build with `DIAG_ALREADY_OBFUSCATED` rather than obfuscating already-obfuscated code. Rebuild from source first — delete the output directory or run your bundler's clean step.

## Flags

Every configuration key is a flag: `--<key>=<value>`, for example `--complexity=40` or `--identifiers.reserved=Hls,Foo` for a list. A boolean flag is bare for `true` (`--build.backup`) and `=false` turns it off (`--protectionMap.enabled=false`). `-h, --help` and `-v, --version` print and exit `0`.

Every scalar or list key in the [option reference](https://www.afterpack.dev/docs/config) works as a flag this way. Structured values belong in `afterpack.json`. Environment variables follow the same names with dots turned to underscores, see [Configuration](https://www.afterpack.dev/docs/config), for example [`AFTERPACK_key`](https://www.afterpack.dev/docs/config#key), [`AFTERPACK_preset`](https://www.afterpack.dev/docs/config#preset) and [`AFTERPACK_telemetry_enabled`](https://www.afterpack.dev/docs/config#telemetry-enabled).

## Files

| Path | Role |
|---|---|
| `afterpack.json` | The config file. AfterPack walks up from the working directory to the nearest match. It holds the whole [engine config](https://www.afterpack.dev/docs/config#example-afterpackjson): build options, [`key`](https://www.afterpack.dev/docs/config#key), everything. |
| `.afterpack/protectionMap.html` | The combined [Protection Map](https://www.afterpack.dev/docs/protection-map). Gitignored automatically. It embeds your original source. Never commit or serve it. |
| `.afterpack/backup/` | Your originals, verbatim, mirrored under the project root with a `manifest.json` of what each file became. Written on every run ([`--build.backup=false`](https://www.afterpack.dev/docs/config#build-backup) opts out), and [`afterpack restore`](#afterpack-restore-dir) reads it to undo the run. It is your source. Never commit or deploy it. |
| `.afterpack-protection.json` | The protection receipt, written into the directory the run walked after its last write: every output file with the hash of the bytes written, the engine version and the seed. [`afterpack verify`](#afterpack-verify-dir) re-hashes against it, and a later run over the same unrebuilt tree is refused because of it. Safe to deploy; it contains no source. |
| `<file>.js.map` | The emitted source map, when [`sourceMap.enabled`](https://www.afterpack.dev/docs/config#sourceMap-enabled) is on. Off by default in [production](https://www.afterpack.dev/docs/config#build-mode). A shipped source map maps the output straight back to your source. |

## `afterpack verify [dir]`

The deploy gate. A build that protected its own output wrote a protection receipt into it; `verify` re-hashes every file the receipt names and fails when one no longer matches, when the receipt is missing, or when it is from a different build.

```bash
npx afterpack@latest verify .        # a project root: looks in ./ then ./.next/
npx afterpack@latest verify dist     # or the build output directory itself
```

It reads no engine configuration — only [`--diagnostics.format`](#machine-readable-output) and `--diagnostics.level`. Run it as the last step before deploy; see [Builds & CI](https://www.afterpack.dev/docs/builds).

## `afterpack restore [dir]`

The undo. A run mirrors your originals into `.afterpack/backup/` before it rewrites anything; `restore` puts them back and deletes the backup and the stale receipt it recorded.

```bash
npx afterpack@latest restore          # the project root, or anywhere beneath it
```

It searches upward from `[dir]` for the project root that holds `.afterpack/backup/`, and it fails closed: every recorded file is checked against the hash the run obfuscated it to *before* anything is written, so a file you edited since the run stops the restore rather than being overwritten. With no backup manifest it refuses outright. Reads no engine configuration beyond [`--diagnostics.format`](#machine-readable-output).

## `afterpack audit <url>`

Scans a **deployed** site for leaked secrets, exposed source and unprotected JavaScript, streaming the findings as the scan runs. It reads no configuration and sends nothing but the URL. See [Audit](https://www.afterpack.dev/docs/audit) for the full command reference.

```bash
npx afterpack@latest audit example.com
```

## Machine-readable output

`--diagnostics.format=json` puts exactly one JSON document on stdout and nothing else; every human-readable line moves to stderr. It works from the flag, `AFTERPACK_diagnostics_format=json`, or `diagnostics.format` in `afterpack.json`, and `verify` and `audit` honor it too. Pair it with `diagnostics.level=none` for a run that prints nothing but the document. This is the shape CI steps and AI agents should read instead of parsing text output; the package also ships a `SKILL.md` that documents the CLI for an agent invoking it directly.

```bash
npx afterpack@latest dist/ --diagnostics.format=json --diagnostics.level=none
```

## Exit codes

| Code | Meaning |
|---|---|
| `0` | Success. Every collected file was obfuscated and written. Also returned by [`--help` and `--version`](#usage). |
| `1` | Failure. Nothing usable was produced: a path that does not exist, no JavaScript found under it, any engine error, a failed `verify`, a failed `audit` scan, or a Pro-keyed build whose cloud call failed. |
| `2` | Partial. Some files shipped unobfuscated — only reachable with [`allowUnobfuscated`](https://www.afterpack.dev/docs/config#allowUnobfuscated). |
| `3` | Size cap. [`inflation.max`](https://www.afterpack.dev/docs/config#inflation-max) could not reach the complexity target (`DIAG_SIZE_CAP_REACHED`). |
| `4` | **Reserved.** A Pro feature requested without a key, or a lapsed entitlement. Documented, never emitted. |
| `5` | **Reserved.** Runtime reflection detected without [`reflection.allow`](https://www.afterpack.dev/docs/config#reflection-allow). Documented, never emitted. |
| `64` | Misuse. An unknown flag or command, a malformed value, or a duplicated path argument. |

Use [`paths.exclude`](https://www.afterpack.dev/docs/config#paths-exclude) to carve out a file that must ship untouched.

A Pro build with a resolved [`key`](https://www.afterpack.dev/docs/config#key) fails closed. If the cloud call fails, the build stops instead of falling back to Free-tier output. `4` and `5` are reserved for diagnostics the engine does not emit yet — they are documented here so a script can branch on them safely, but no build returns them today. See [Exit codes](https://www.afterpack.dev/docs/diagnostics#exit-codes) for the full contract.

## Next

- [Configuration](https://www.afterpack.dev/docs/config): every option, and the four forms that can set it.
- [Presets](https://www.afterpack.dev/docs/presets): the five-rung ladder and what each rung costs.
- [Audit](https://www.afterpack.dev/docs/audit): the full `afterpack audit` reference.
- [Diagnostics](https://www.afterpack.dev/docs/diagnostics#exit-codes): the exit contract, the fail-closed rule, and every `DIAG_*` code.
- [Frameworks](https://www.afterpack.dev/docs/frameworks): the plugin path, better when a plugin exists for your stack.
- [Quickstart](https://www.afterpack.dev/docs/quickstart): the shortest route from install to a [Protection Map](https://www.afterpack.dev/docs/quickstart#check-the-result).
