Obfuscate an Astro site

@afterpack/astro is a thin integration that wires @afterpack/vite into Astro's own Vite pipeline.

Astro compiles every island and client bundle through its own Vite pipeline, and AfterPack integrates at the bundler level. @afterpack/astro exists so you don't have to hand-wire vite: { plugins: [...] } yourself.

Everything happens in @afterpack/vite's generateBundle, inside Astro's own Vite pipeline.

Install

$ npm install -D @afterpack/astro
// astro.config.mjs
import { defineConfig } from "astro/config";
import afterpack from "@afterpack/astro";

export default defineConfig({
  integrations: [afterpack()],
});

The default export and the named afterpackAstro export are the same function.

Build

astro build now emits obfuscated output. astro dev is untouched, because the plugin only runs on the build's generateBundle, the hook @afterpack/vite always uses. Astro writes the obfuscated chunks; the originals never reach dist/.

The obfuscated surface is the JavaScript Astro emits: island bundles and client scripts, typically under dist/_astro/. Server-rendered .astro template logic that never becomes client JS is out of scope.

Verify

The Protection Map is written by default only when Vite emits a source map (vite.build.sourcemap) and the build is not in CI (CI=true or CI=1); the production build mode does not turn it off. Ask for it explicitly when you want to inspect one:

integrations: [afterpack({ protectionMap: { enabled: true } })],

It writes to the gitignored .afterpack/protectionMap.html. The source-map side effect and the handling rules are @afterpack/vite's.

entitlements.jslight · complexity 5Open full size
entitlements.js, 54 lines, built at the light preset with a directive around the signature check. Click any token to see what was applied to it.

Options

Every configuration key can be passed here as the options object, forwarded verbatim to @afterpack/vite, for example afterpack({ preset: "hard", complexity: 40 }). The type alias AfterpackAstroOptions is AfterpackViteOptions:

integrations: [afterpack({ seed: "git", preset: "medium" })],

Directives

Directives are a Pro feature. On a Free build, a directive that raises protection stops the build with DIAG_PRO_DIRECTIVE_REQUIRES_KEY.

Directives are read from your source by the plugin, before minification. Block comments only:

Directives inside .astro component files themselves are not captured. Put the directive in the island's .jsx/.tsx file:

/* @afterpack preset=hard */
export function LicenseGate({ token }) {
  // ...
}
/* @afterpack end */

Next