Install
Three official npm packages. Pick whichever fits your workflow — they all hit the same REST API under the hood.
# CLI (CI / scripts / ad-hoc) npm install --save-dev shieldmycode-cli # Webpack 5 npm install --save-dev shieldmycode-webpack-plugin # Vite / Rollup npm install --save-dev shieldmycode-vite-plugin
Requires Node 18+. The CLI binary is exposed as shield(and shieldmycode as an alias).
Authentication
Every authenticated endpoint uses a Bearer token. Create a personal key at Dashboard → API keys (every plan). Keys are 32 random bytes prefixed with shield_. Treat them like passwords — they authenticate against your account and consume your monthly quota.
# Header on every request Authorization: Bearer shield_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx # Or as the SHIELD_API_KEY env var for the CLI / plugins export SHIELD_API_KEY=shield_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
First API call
Smoke-test from your terminal:
curl -s -X POST https://shield.shieldmycode.com/api/v1/obfuscate \
-H "Authorization: Bearer $SHIELD_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"source": "const secret = \"alpha\"; function unlock(c) { return c === secret; }",
"kind": "js",
"options": { "level": "hard", "antiLlm": true }
}' | jq .You should get back something like:
{
"output": "(function(){\"use strict\";var _0xH=\"...\";...})();",
"bytesIn": 67,
"bytesOut": 12450,
"durationMs": 8,
"kind": "js",
"level": "hard",
"quota": { "used": 1, "limit": 3, "plan": "free" }
}POST /api/v1/obfuscate
Obfuscate a single JavaScript or HTML source. The workhorse endpoint: the CLI, Webpack plugin, and Vite plugin all call this under the hood.
Request body
{
"source": "string (required) — JS or HTML source to obfuscate. Max 50 MB.",
"kind": "\"js\" | \"html\" (required)",
"options": {
"level": "\"soft\" | \"medium\" | \"hard\" (default \"hard\")",
"encodeStrings": "boolean (default true)",
"obfuscateNumbers": "boolean (default true)",
"mangleLocals": "boolean (default true)",
"integrity": "boolean (default true)",
"debuggerTrap": "boolean (default true)",
"devToolsCheck": "boolean (default true)",
"heartbeatMs": "number (50–5000, default 250)",
"noscriptFallback": "boolean (default true)",
"stealth": "boolean (default false) — HTML only",
"headlessCheck": "boolean (default true)",
"antiLlm": "boolean (default true) — anti-LLM defenses",
"deadCode": "boolean (default false) — inject decoy branches",
"domainLock": "string — comma-separated hostnames",
"expiresAt": "string — ISO 8601 datetime",
"geoAllow": "string — comma-separated ISO 3166-1 country codes",
"browserBlocklist": "string — comma-separated UA substrings",
"osBlocklist": "string — comma-separated OS names",
"ipBlocklist": "string — comma-separated IPv4/IPv6",
"telemetryUrl": "string — POST tamper events here"
}
}Response (200 OK)
{
"output": "string — the obfuscated payload",
"bytesIn": "number — input size",
"bytesOut": "number — output size",
"durationMs": "number — server-side processing time",
"kind": "\"js\" | \"html\"",
"level": "the strictness that ran",
"quota": { "used": 1, "limit": 1000, "plan": "pro" }
}Examples
curl -X POST https://shield.shieldmycode.com/api/v1/obfuscate \ -H "Authorization: Bearer $SHIELD_API_KEY" \ -H "Content-Type: application/json" \ -d @payload.json
// Node 18+ (uses native fetch)
const SOURCE = require('fs').readFileSync('app.js', 'utf8');
const r = await fetch('https://shield.shieldmycode.com/api/v1/obfuscate', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.SHIELD_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
source: SOURCE,
kind: 'js',
options: { level: 'hard', antiLlm: true, domainLock: 'mycompany.com' }
})
});
if (!r.ok) throw new Error(`HTTP ${r.status}: ${await r.text()}`);
const { output, quota } = await r.json();
require('fs').writeFileSync('app.shield.js', output);
console.log(`${quota.used}/${quota.limit} this month`);# Python 3.8+
import os, requests
src = open('app.js').read()
r = requests.post(
'https://shield.shieldmycode.com/api/v1/obfuscate',
headers={ 'Authorization': f"Bearer {os.environ['SHIELD_API_KEY']}" },
json={
'source': src,
'kind': 'js',
'options': { 'level': 'hard', 'antiLlm': True }
},
timeout=30,
)
r.raise_for_status()
data = r.json()
open('app.shield.js', 'w').write(data['output'])
print(f"{data['quota']['used']}/{data['quota']['limit']} used")POST /api/batch/zip
Upload a .zip archive of mixed HTML + JS files and get back a protected archive of the same shape. Static assets (CSS, images, fonts) pass through unchanged. Business plan and up.
Each .js / .mjs / .cjs /.html / .htm entry counts as one obfuscation against your monthly quota. The response includes custom X-Shield-* headers (X-Shield-Files-Processed, X-Shield-Files-Errored, X-Shield-Bytes-In, X-Shield-Bytes-Out, X-Shield-Stopped-Early) for batch stats.
POST /api/telemetry/[userId]
Receives tamper events from obfuscated code in the wild and feeds them into your threat-intel dashboard. You don’t call this directly — the obfuscated payload calls it automatically when options.telemetryUrl is set to your personal endpoint (visible on the threat-intel page).
No auth header required — the URL itself embeds an opaque user id that’s rate-limited and verified server-side. Bounded retention: 10,000 events per account; older events rotate out.
Options reference
All options are optional. Defaults shown below match the dashboard’s “Hard” preset.
| Key | Type | Default | Scope | Notes |
|---|---|---|---|---|
| level | enum | "hard" | all | soft / medium / hard |
| encodeStrings | boolean | true | all | String array (XOR + base64) |
| obfuscateNumbers | boolean | true | all | Numeric expression replacement |
| mangleLocals | boolean | true | all | Scope-aware identifier rename |
| antiLlm | boolean | true | all | Semantic decoy rename + IIFE wrap |
| deadCode | boolean | false | all | Inject decoy branches |
| integrity | boolean | true | all | FNV-1a hash check |
| debuggerTrap | boolean | true | all | Timing-based debugger detection |
| devToolsCheck | boolean | true | all | 3-signal DevTools detection |
| heartbeatMs | number | 250 | all | 50–5000, watchdog interval |
| headlessCheck | boolean | true | all | Puppeteer / Playwright / Selenium |
| stealth | boolean | false | HTML only | Hide visible body in payload |
| noscriptFallback | boolean | true | HTML only | SEO-safe <noscript> mirror |
| domainLock | string | "" | all | Comma-separated hostnames |
| expiresAt | string | "" | all | ISO 8601 datetime |
| geoAllow | string | "" | all | ISO 3166-1 alpha-2 list |
| browserBlocklist | string | "" | all | UA substrings, comma-separated |
| osBlocklist | string | "" | all | Windows, Mac, Linux, Android, iOS, ChromeOS |
| ipBlocklist | string | "" | all | IPv4 or IPv6 list |
| telemetryUrl | string | "" | all | POST tamper events here |
Rate limits & quotas
Monthly quotas are the only rate limit in normal use. No per-second throttle today. Use whatever concurrency makes sense for your pipeline; we recommend 5–10 in flight.
| Plan | Monthly | Per-file | API | ZIP batch |
|---|---|---|---|---|
| Free | 3 | 50 KB | ✓ | ✗ |
| Pro | 1,000 | 5 MB | ✓ | ✗ |
| Business | 10,000 | 50 MB | ✓ | ✓ |
| Enterprise | Custom | 500 MB | ✓ | ✓ |
Quotas reset on the 1st of each calendar month (UTC). Hitting the limit returns 429 over_quota with your current count and the plan limit.
Error codes
All errors return JSON with an error string and (for most) a detail message. HTTP status codes map directly to the cause.
| Status | error | Meaning & fix |
|---|---|---|
| 400 | bad_request | Schema validation failed. Check field types. |
| 401 | unauthorized | Missing / unknown / revoked Bearer token. |
| 403 | level_not_allowed | Strictness not allowed on your plan. |
| 403 | zip_batch_not_allowed | ZIP batch requires Business+. |
| 413 | input_too_large | Source exceeds plan maxInputBytes. |
| 413 | zip_too_large | Archive exceeds 50 MB / 5,000 entries. |
| 429 | over_quota | Monthly limit hit. Upgrade or wait. |
| 500 | obfuscation_failed | Engine threw. Likely unparseable source. |
shieldmycode-cli
Walks files, directories, or globs. Parallel by default (5 concurrent). Auto-detects file kind from extension.
# single file -> next to it
SHIELD_API_KEY=shield_xxx npx shield obfuscate app.js
# explicit output
npx shield obfuscate app.js -o dist/app.shield.js
# whole dist/ tree, mirrored under dist-shield/
npx shield obfuscate dist -o dist-shield --recursive
# glob with config file (shield.config.js auto-detected)
npx shield obfuscate "src/**/*.{js,html}" -o dist-shieldFull flag reference: npx shield --help.
shieldmycode-webpack-plugin
Hooks into Webpack 5 right after the minifier. Every npm run build ships protected. Source maps and other non-JS/HTML assets pass through.
// webpack.config.js
const ShieldPlugin = require('shieldmycode-webpack-plugin');
module.exports = {
// ...your existing config...
plugins: [
new ShieldPlugin({
apiKey: process.env.SHIELD_API_KEY,
options: {
level: 'hard',
antiLlm: true,
domainLock: 'mycompany.com'
}
})
]
};shieldmycode-vite-plugin
Hooks into Rollup’s generateBundle stage. Only runs during vite build — HMR stays instant.
// vite.config.js
import { defineConfig } from 'vite';
import shield from 'shieldmycode-vite-plugin';
export default defineConfig({
plugins: [
shield({
apiKey: process.env.SHIELD_API_KEY,
options: { level: 'hard', antiLlm: true }
})
]
});CI / GitHub Actions
name: Build & protect
on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: '20' }
- run: npm ci && npm run build
- run: npx shieldmycode-cli obfuscate dist -o dist-protected --recursive
env:
SHIELD_API_KEY: ${{ secrets.SHIELD_API_KEY }}
- uses: actions/upload-artifact@v4
with: { name: protected, path: dist-protected }Bash loop over /dist
#!/usr/bin/env bash
# Obfuscate every .js in ./dist into ./dist-protected
mkdir -p dist-protected
for f in dist/**/*.js; do
rel="${f#dist/}"
out="dist-protected/${rel}"
mkdir -p "$(dirname "$out")"
source=$(cat "$f")
curl -sS -X POST https://shield.shieldmycode.com/api/v1/obfuscate \
-H "Authorization: Bearer $SHIELD_API_KEY" \
-H "Content-Type: application/json" \
-d "$(jq -nc --arg s "$source" '{source:$s, kind:"js", options:{level:"hard"}}')" \
| jq -r '.output' > "$out"
echo " $f -> $out"
donePython wrapper
# shield.py — minimal Shield API wrapper
import os, requests, pathlib
API_KEY = os.environ['SHIELD_API_KEY']
BASE = 'https://shield.shieldmycode.com'
def obfuscate(source: str, kind: str = 'js', **options) -> dict:
r = requests.post(
f'{BASE}/api/v1/obfuscate',
headers={ 'Authorization': f'Bearer {API_KEY}' },
json={ 'source': source, 'kind': kind, 'options': options },
timeout=30,
)
r.raise_for_status()
return r.json()
# Usage
src = pathlib.Path('app.js').read_text()
result = obfuscate(src, level='hard', antiLlm=True)
pathlib.Path('app.shield.js').write_text(result['output'])
print(f"{result['quota']['used']}/{result['quota']['limit']} this month")Ready to wire it up?
Create a free account, generate an API key, and have your first build-time obfuscation running in under five minutes.