This page is available in English only.

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.

npx afterpack@latest obfuscates a build directory. Use it when there is no plugin for your bundler, or in CI. If a plugin exists for your framework, install @afterpack/<fw> instead: your normal build then emits obfuscated output with no separate step. See Frameworks.

This page covers the argument, examples, what a run does, verify, audit, machine-readable output, and the exit codes. Every option is defined once, on Configuration.

Usage

afterpack [path] [--<key>=<value> ...] [-h | --help] [-v | --version]
afterpack verify [dir]
afterpack audit <url>
$ 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 at the end of a run.

Point it at your bundler's output, not your source. Most bundlers (Vite, Rollup, esbuild, webpack, Astro, Parcel) write to dist/. Next.js writes to .next/, Nuxt to .output/.

Examples

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, and skips node_modules/ unless you include it with --paths.include='**/node_modules/**'. 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, or at --complexity when you set one explicitly.
  4. Writes the artifacts the flags allow, and always the protection receipt. The combined 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, 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 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 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 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, for example AFTERPACK_key, AFTERPACK_preset and AFTERPACK_telemetry_enabled.

Files

PathRole
afterpack.jsonThe config file. AfterPack walks up from the working directory to the nearest match. It holds the whole engine config: build options, key, everything.
.afterpack/protectionMap.htmlThe combined 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 opts out), and afterpack restore reads it to undo the run. It is your source. Never commit or deploy it.
.afterpack-protection.jsonThe 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 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.mapThe emitted source map, when sourceMap.enabled is on. Off by default in production. 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.

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 and --diagnostics.level. Run it as the last step before deploy; see Builds & CI.

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.

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.

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 for the full command reference.

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.

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

Exit codes

CodeMeaning
0Success. Every collected file was obfuscated and written. Also returned by --help and --version.
1Failure. 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.
2Partial. Some files shipped unobfuscated — only reachable with allowUnobfuscated.
3Size cap. inflation.max could not reach the complexity target (DIAG_SIZE_CAP_REACHED).
4Reserved. A Pro feature requested without a key, or a lapsed entitlement. Documented, never emitted.
5Reserved. Runtime reflection detected without reflection.allow. Documented, never emitted.
64Misuse. An unknown flag or command, a malformed value, or a duplicated path argument.

Use paths.exclude to carve out a file that must ship untouched.

A Pro build with a resolved 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 for the full contract.

Next

  • Configuration: every option, and the four forms that can set it.
  • Presets: the five-rung ladder and what each rung costs.
  • Audit: the full afterpack audit reference.
  • Diagnostics: the exit contract, the fail-closed rule, and every DIAG_* code.
  • Frameworks: the plugin path, better when a plugin exists for your stack.
  • Quickstart: the shortest route from install to a Protection Map.