Fetching the Bytecode Array Key from Backend
Keep the VM bytecode decryption key off the client: fetch it from your backend at load time with an asynchronous key getter, gated behind auth so a stolen bundle is inert.
The problem
vmBytecodeArrayEncoding encrypts the VM bytecode with a key. If that key ships inside the bundle, anyone with the file has everything needed to decrypt it. A vmBytecodeArrayEncodingKeyGetter lets the key live somewhere else and be produced at runtime — but a synchronous getter can only read what is already present on the client (a global, a cookie, localStorage). To fetch the key from your server — so you can gate it behind authentication and revoke it — the getter has to be asynchronous.
The solution
Enable vmAsyncExecutor (v7.3.0+). With the async executor the key getter may return a Promise, so it can fetch the key from your backend before the VM runs. Three options work together:
vmBytecodeArrayEncoding: true— encrypt the bytecode array.vmBytecodeArrayEncodingKey— the key used at compile time (kept on your server, not in the bundle).vmBytecodeArrayEncodingKeyGetter— a JS expression returning that same key at runtime. WithvmAsyncExecutorenabled it may return aPromise; without it the getter must return the key synchronously.
Client obfuscation config
JavaScriptObfuscator.obfuscate(sourceCode, {
vmObfuscation: true,
vmAsyncExecutor: true,
vmBytecodeArrayEncoding: true,
vmBytecodeArrayEncodingKey: process.env.VM_KEY, // e.g. 'mySecretKey123'
vmBytecodeArrayEncodingKeyGetter:
'fetch("/api/vm-key", { credentials: "include" }).then((res) => res.text())'
});The getter expression is embedded verbatim and evaluated in the browser. Keep the compile-time vmBytecodeArrayEncodingKey out of your client repo — inject it from an environment variable or secret at build time, and serve the exact same string from the endpoint below.
How the two keys work
Your key is never used on its own — on both sides it is mixed with an internal key the obfuscator controls:
- Compile time.
vmBytecodeArrayEncodingKeyis combined with an internal key that the obfuscator derives, and the bytecode array is encoded with the resulting mixed key. - Runtime. The value your
vmBytecodeArrayEncodingKeyGetterresolves to — returned by your server — is combined with the same internal key, reconstructed on the client from various runtime factors, to decode the bytecode.
Because both sides mix your key with the internal key, the getter must resolve to the exact same string you passed as vmBytecodeArrayEncodingKey. Neither piece is sufficient alone: your key without the internal key cannot decode the bytecode, and the internal key is useless without yours — which is why serving your key only to authenticated callers keeps a stolen bundle inert.
Server side
The endpoint decides which key to return based on whatever your application trusts — a valid session, an expected Origin or Referer, a license check, and so on. The twist: instead of rejecting untrusted callers, return a wrong key. The bytecode then decodes to garbage and the protected code fails on its own, which is stealthier than an obvious 401 that tells an attacker exactly what to bypass.
// Express example — the exact checks depend on your app
app.get('/api/vm-key', (req, res) => {
const origin = req.get('origin');
const trusted =
req.session?.user && // a valid session, and
origin === 'https://app.example.com'; // the expected production origin
res.type('text/plain').send(
// Real key for valid users; a decoy for everyone else
// (no session, or a localhost / unexpected origin).
trusted ? process.env.VM_KEY : process.env.VM_DECOY_KEY
);
});