Pro API for JSVMP: call VEILVM's JS VM obfuscator from CI
I built VEILVM as a browser workspace that compiles JavaScript into a custom VM (JSVMP) — high-performance or dynamic mode — so small critical fragments are harder to reverse than with classic string/control-flow obfusca
I built VEILVM as a browser workspace that compiles JavaScript into a custom VM (JSVMP) — high-performance or dynamic mode — so small critical fragments are harder to reverse than with classic string/control-flow obfuscators.
Pro plans can now hit the same engines over HTTP, which is what you want for CI, batch jobs, or a private build step. API runs share the monthly quota with the web UI.
Quick facts (from the live docs)
POST https://vm.ldvmp.com/api/v1/obfuscate- Header:
Authorization: Bearer veil_live_…(key shown once; up to 3 active keys per account) - Body:
{ "code": "<js>", "mode": "performance" | "dynamic" } - Max 2 MB source per request (UTF-8)
- Success
200:{ ok, output, inputBytes, outputBytes, ms, engine, engineVersion }plusX-Quota-Limit/X-Quota-Remaining - Plans: Try free 3 times a day, no sign-up; 20 runs a month after free signup; Pro $9/month or $79/year → 1,000 runs/month + API
Full reference: API documentation.
curl
curl -X POST https://vm.ldvmp.com/api/v1/obfuscate \
-H "Authorization: Bearer $VEILVM_API_KEY" \
-H "Content-Type: application/json" \
-d '{"code":"console.log(\"hello\")","mode":"performance"}'
Node.js 18+ with Retry-After on 503
Cold starts or engine timeouts return 503 with ENGINE_UNAVAILABLE or TIMEOUT. Those runs are not charged. Wait for the Retry-After header (seconds), then retry. 500 INTERNAL_ERROR is also refunded — retry later. 422 COMPILE_FAILED is refunded (bad source, not a transient fault).
// Node.js 18+ — keep the key in an env var, never in the repo
import { readFile, writeFile } from "node:fs/promises";
async function obfuscate(code, mode = "performance", { maxAttempts = 4 } = {}) {
let attempt = 0;
while (true) {
attempt += 1;
const response = await fetch("https://vm.ldvmp.com/api/v1/obfuscate", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.VEILVM_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ code, mode }),
});
const data = await response.json();
if (response.ok && data.ok) return data;
const retriable =
response.status === 503 ||
response.status === 500 ||
data.error === "ENGINE_UNAVAILABLE" ||
data.error === "TIMEOUT" ||
data.error === "INTERNAL_ERROR" ||
data.error === "UNAVAILABLE" ||
data.error === "QUOTA_UNAVAILABLE";
if (!retriable || attempt >= maxAttempts) {
throw new Error(`${response.status} ${data.error}: ${data.message}`);
}
const retryAfterHeader = response.headers.get("Retry-After");
const waitSec = Number(retryAfterHeader);
const delayMs =
Number.isFinite(waitSec) && waitSec > 0
? waitSec * 1000
: Math.min(8000, 1000 * 2 ** (attempt - 1));
await new Promise((r) => setTimeout(r, delayMs));
}
}
const code = await readFile("input.js", "utf8");
const data = await obfuscate(code, "dynamic");
await writeFile("output.js", data.output);
console.log(
`${data.engine}: ${data.inputBytes} → ${data.outputBytes} bytes in ${data.ms} ms`
);
Error cheat sheet
| HTTP | error |
Notes |
|---|---|---|
| 401 | INVALID_KEY |
Missing, malformed, revoked, or unknown |
| 403 | PLAN_REQUIRED |
Account is no longer Pro — keys die immediately on downgrade |
| 422 | COMPILE_FAILED |
Source would not compile — run refunded |
| 429 | QUOTA_EXCEEDED |
Body includes limit / used / remaining / resetAt
|
| 503 |
ENGINE_UNAVAILABLE / TIMEOUT
|
Wait Retry-After seconds — run refunded
|
| 500 | INTERNAL_ERROR |
Retry later — run refunded |
Honest caveats
- VM output is larger and slower; protect small high-value functions, not your whole bundle.
- No client-side obfuscation is unbreakable.
- Keep keys server-side; revoke from the Account page if one leaks.
If you try it, I’d love feedback on CI integration vs a future CLI. Links: workspace · pricing · docs.
Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.