Orvex API Reference
Everything you need to integrate Orvex obfuscation into your workflow — from the web dashboard to the full REST API.
Introduction
OrvexSecurity compiles your Lua source code into a custom proprietary bytecode that runs inside a generated virtual machine (OrvexVM / VM L14). This documentation covers the REST API and the official @orvex/sdk Node.js package.
Base URL for all API requests:
https://www.orvex-sc.xyzCLI Installation
Coming soon — The @orvex/cli package is currently in development and not yet available on npm. Use the web dashboard or the Node.js SDK in the meantime.
Once released, the CLI will let you protect scripts directly from your terminal — no browser needed after the first login. Requires Node.js 18+.
Windows
npm install -g @orvex/cliIf orvex is not recognized after installation, the npm global bin directory is not in your PATH. Open PowerShell as administrator and run:
[System.Environment]::SetEnvironmentVariable(
"PATH",
[System.Environment]::GetEnvironmentVariable("PATH","User") + ";$env:APPDATA\npm",
"User"
)Close and reopen your terminal. orvex should now work.
Alternatively, add the path manually: Start → "Edit environment variables" → Path → New → paste %APPDATA%\npm → OK.
Linux / macOS
npm install -g @orvex/cliIf orvex is not found, add the npm global bin to your shell profile:
# Find the npm global prefix (works with all npm versions)
npm config get prefix
# The binaries are at <prefix>/bin — add it to your shell (bash example)
echo 'export PATH="$(npm config get prefix)/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc
# Verify
orvexOn macOS with Homebrew Node, the global bin is usually at /usr/local/bin which is already in PATH — no extra step needed.
First launch
# Run without arguments to see the startup animation (first time only)
orvex
# Then authenticate:
orvex login
# Protect a script:
orvex obfuscate
# View connected devices:
orvex sessionsNote: The CLI requires an active internet connection for authentication and obfuscation. All processing is done server-side — obfuscation runs are deducted from your plan quota exactly like the web dashboard.
Quick Start
Install the SDK and protect your first script in under a minute.
npm install @orvex/sdkimport { OrvexClient } from "@orvex/sdk"
const client = new OrvexClient({ apiKey: process.env.ORVEX_API_KEY })
const { output } = await client.obfuscate({
code: `
local function hello()
print("Hello, World!")
end
hello()
`,
luaVersion: "fivem",
})
// output contains the protected Lua bytecode
console.log(output)Authentication
All API requests require your API key in the Authorization header as a Bearer token. Find your key in the API Keys section of the dashboard.
curl https://www.orvex-sc.xyz/api/obfuscate \
-H "Authorization: Bearer your_api_key_here" \
-H "Content-Type: application/json" \
-d '{"code": "print(\"hello\")", "luaVersion": "fivem"}'Note: API access requires a Pro plan or higher. Free users can obfuscate through the web dashboard only.
POST /api/obfuscate
Obfuscate a single Lua script. The request is processed synchronously — the protected output is returned directly in the response body. No polling required.
Request Body
| Parameter | Type | Required | Description |
|---|---|---|---|
| code | string | Yes | Raw Lua source code to protect. Max 500 KB. |
| filename | string | No | Original filename — used in history. Defaults to "script.lua". |
| luaVersion | string | No | "lua51" | "lua54" | "luajit" | "roblox" | "fivem" | "luau". Defaults to "lua51". |
| options.controlFlow | boolean | No | Flatten control flow into a state machine dispatch loop. Default: true. |
| options.deadCode | boolean | No | Inject realistic dead-code blocks. Default: true. |
| options.vmInjection | boolean | No | Compile into OrvexVM (L14) with anti-tamper fingerprint. Requires Pro+. Default: false. |
| options.multiLayer | boolean | No | Dual-layer VM (L15): encrypted bytecode, implies vmInjection. Requires Premium+. Default: false. |
Response
{
"ok": true,
"output": "-- [OrvexSecurity | Lua 5.1]\nlocal _unpack=unpack..."
}Error response
{
"error": "[L14:Parser] expected 'end', got ':' at 12:5"
}Status codes
| Code | Meaning | Description |
|---|---|---|
| 400 | Bad Request | Missing code, or script exceeds the 500 KB limit. |
| 401 | Unauthorized | Missing or invalid API key / session. |
| 403 | Forbidden | Option requires a higher plan (vmInjection → Pro+, multiLayer → Premium+), or account banned. |
| 422 | Unprocessable Entity | Lua syntax error — details in the error message with line:col. |
| 429 | Too Many Requests | Monthly obfuscation quota reached. Upgrade your plan or wait for the next cycle. |
POST /api/obfuscate/batch
Obfuscate up to 50 Lua files in a single request. Quota is deducted per file. Requires Pro plan or above.
Request Body
| Parameter | Type | Required | Description |
|---|---|---|---|
| files | array | Yes | Array of { path: string, code: string } objects. Max 50 items, 500 KB per file. |
| luaVersion | string | No | Applied to all files in the batch. |
| options | object | No | Same options as /api/obfuscate — applied to all files. |
Response
{
"results": [
{ "path": "server/main.lua", "ok": true, "output": "..." },
{ "path": "server/config.lua", "ok": false, "error": "[L14:Lexer] unexpected character..." }
]
}cURL example
curl https://www.orvex-sc.xyz/api/obfuscate/batch \
-H "Authorization: Bearer your_api_key" \
-H "Content-Type: application/json" \
-d '{
"luaVersion": "fivem",
"files": [
{ "path": "server/main.lua", "code": "print(\"main\")" },
{ "path": "server/config.lua", "code": "print(\"cfg\")" }
]
}'GET /api/history
Returns your obfuscation history (metadata only — source code is never stored). History retention: 7 days (Free), 90 days (Pro), unlimited (Premium+).
Note: Accessing this endpoint via API key requires a Pro plan or higher. Session-authenticated requests (web dashboard) work on all plans.
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| offset | number | No | Number of entries to skip (for pagination). Defaults to 0. |
| limit | number | No | Max entries to return. Capped at 100. Defaults to 50. |
curl "https://www.orvex-sc.xyz/api/history?offset=0&limit=50" \
-H "Authorization: Bearer your_api_key"{
"history": [
{
"id": "abc123",
"filename": "main.lua",
"size": 4096,
"plan": "pro",
"status": "success",
"createdAt": "2026-06-24T14:32:00Z"
}
],
"total": 142,
"offset": 0,
"limit": 50,
"retentionDays": 90
}Rate Limits
Limits are applied per account (never per IP — shared CI runners are safe) over a sliding 60-second window. A batch request counts as one unit per file, so batching is neither penalized nor a loophole.
| Plan | Requests / minute | Max files / batch |
|---|---|---|
| Pro | 120 | 50 |
| Premium | 300 | 50 |
| Enterprise | 600 | 50 |
Every response includes standard rate-limit headers so your client can pace itself:
X-RateLimit-Limit: 120 # your plan's per-minute budget
X-RateLimit-Remaining: 117 # units left in the current window
X-RateLimit-Reset: 1750792080 # unix time when the window resets
# On 429, additionally:
Retry-After: 12 # seconds to wait before retryingWhen you exceed the limit the API returns 429 with a Retry-After header — honor it and the request will succeed on retry. Rate limits are independent from your monthly obfuscation quota.
Protection Levels
Every plan runs the full 16-layer pipeline (L0–L15 — identifier renaming, 8-mode string encryption, integer encoding, dead code, opaque predicates, and more). Paid plans additionally get denser junk bytecode and unlock the options below. All options can be combined.
controlFlowFreeFlattens all branches into a state-machine dispatch loop (L13). Breaks static decompilers.
deadCodeFreeInjects realistic dead code (24 factories — stdlib calls, math, pcall) with self-modifying seeds to obscure real logic.
vmInjectionProCompiles the script into OrvexVM (L14): custom bytecode with a per-build randomized instruction set, plus a Murmur3 anti-tamper fingerprint. Any file modification crashes at runtime.
multiLayerPremiumDual-layer VM (L15): the bytecode program is additionally encrypted at rest with a position-dependent key stream and decrypted once at load — two independent barriers, near-native runtime speed.
Control Flow Obfuscation
All branching logic is compiled into a flat dispatch loop driven by an opaque state variable. The original structure becomes impossible to recover from the bytecode alone.
-- Original source
for i = 1, 10 do
print(i)
end
-- After OrvexSecurity (VM L14 bytecode, simplified representation)
-- The actual output is unreadable bytecode, not Lua source.
-- This is an illustration of the transformation applied internally.
local _state = 7
while _state ~= 40 do
if _state == 7 then _state = 12
elseif _state == 12 then
-- loop body, encoded as opcode stream
_state = 40
end
endMulti-Layer Pass
When multiLayer is enabled (Premium+), OrvexSecurity activates the dual-layer VM (L15): your script is compiled once into a per-build randomized-ISA bytecode program that is additionally encrypted at rest with a position-dependent key stream and decrypted a single time at load. Two independent barriers — custom instruction set + bytecode cipher — with single-pass execution, so runtime performance stays near-native.
Enabling multiLayer automatically implies vmInjection — you don't need to set both.
const { output } = await client.obfuscate({
code: myScript,
luaVersion: "fivem",
options: {
controlFlow: true,
deadCode: true,
vmInjection: true, // Pro+
multiLayer: true, // Premium+
},
})Node.js SDK — @orvex/sdk
The official @orvex/sdk package provides a fully-typed TypeScript/JavaScript client. Supports Node.js 18+ and any environment with native fetch.
npm install @orvex/sdkimport { OrvexClient, OrvexQuotaError, OrvexParseError } from "@orvex/sdk"
const client = new OrvexClient({
apiKey: process.env.ORVEX_API_KEY!,
timeout: 30_000,
})
try {
// Single file
const { output } = await client.obfuscate({
code: luaSource,
luaVersion: "fivem",
options: { vmInjection: true },
})
await fs.writeFile("dist/main.lua", output)
// Batch (Pro+)
const { results } = await client.obfuscateBatch({
files: [
{ path: "server/main.lua", code: await fs.readFile("server/main.lua", "utf8") },
{ path: "server/config.lua", code: await fs.readFile("server/config.lua", "utf8") },
],
luaVersion: "fivem",
})
for (const r of results) {
if (r.ok) await fs.writeFile(`dist/${r.path}`, r.output!)
else console.error(r.path, r.error)
}
} catch (err) {
if (err instanceof OrvexQuotaError) console.error("Quota exceeded")
else if (err instanceof OrvexParseError) console.error("Syntax error at", err.location)
else throw err
}CI/CD Integration
Protect scripts automatically as part of your build pipeline. Store your API key as a repository secret — never hard-code it.
# .github/workflows/protect.yml
name: Protect Lua scripts
on:
push:
branches: [main]
jobs:
protect:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm install @orvex/sdk
- run: node scripts/protect.mjs
env:
ORVEX_API_KEY: ${{ secrets.ORVEX_API_KEY }}// scripts/protect.mjs
import { OrvexClient } from "@orvex/sdk"
import { readFile, writeFile, mkdir } from "fs/promises"
import { glob } from "glob"
const client = new OrvexClient({ apiKey: process.env.ORVEX_API_KEY })
const files = await glob("server/**/*.lua")
await mkdir("dist/server", { recursive: true })
const { results } = await client.obfuscateBatch({
files: await Promise.all(
files.map(async path => ({ path, code: await readFile(path, "utf8") }))
),
luaVersion: "fivem",
})
let failed = 0
for (const r of results) {
if (r.ok) {
await writeFile(`dist/${r.path}`, r.output)
console.log("✓", r.path)
} else {
console.error("✗", r.path, r.error)
failed++
}
}
if (failed > 0) process.exit(1)Ready to protect your scripts?
Start for free — no credit card required. Upgrade when you need API access or more obfuscations.