Skip to content
Genesis v2.0 and Soteria v7.0 are here. Read the announcement
  • Macros
    • Get Started
    • SOTR_OBFUSCATED
    • SOTR_LINE
    • SOTR_KILL
    • SOTR_GUID
    • SOTR_TIMESTAMP
    • SOTR_VERSION
    • SOTR_TARGET
    • SOTR_ENC_STR
    • SOTR_ENC_NUM
    • SOTR_ENC_FUNC
    • SOTR_EXPOSE
    • SOTR_SECURE_CALL
    • SOTR_ENC_BUF
    • SOTR_ENC_UDIM
    • SOTR_ENC_VEC
    • SOTR_INLINE
    • SOTR_CLEAR_STACK
    • SOTR_PRECHECK
    • SOTR_REWRITE
    • SOTR_NO_UPVALUES
    • SOTR_JIT
    • SOTR_JIT_MAX
  • Attributes
  • Obfuscate API
  • Generating Keys with API
  • Editing Keys with API
  • Using the Oracle SDK
  • Setup
  • Commands

Getting Started

Macros

Soteria exposes a set of compile-time macros that let you query and control the runtime environment directly from your Luau scripts. Each macro either expands (its call site is replaced with generated source) or annotates (it records an instruction for a later pass and unwraps to its inner value). After the build, the macro identifier never survives in the output.

Get Started

Before writing any macro logic, paste this snippet at the top of your script. It stubs out the SOTR macro functions so your code runs normally in an unobfuscated environment.

--!nonstrict

if not SOTR_OBFUSCATED then

    local function check(cond, msg)
        if not cond then error("[soteria-sdk] " .. msg, 3) end
    end

    SOTR_ENC_STR = function(s) check(type(s) == "string", "SOTR_ENC_STR expects a string literal"); return s end
    SOTR_ENC_NUM = function(n) check(type(n) == "number", "SOTR_ENC_NUM expects a number literal"); return n end
    SOTR_ENC_FUNC = function(f) check(type(f) == "function", "SOTR_ENC_FUNC expects a function"); return f end
    SOTR_ENC_VEC = function(v) check(v ~= nil, "SOTR_ENC_VEC expects a Vector2/Vector3/vector"); return v end
    SOTR_ENC_UDIM = function(v) check(v ~= nil, "SOTR_ENC_UDIM expects a UDim/UDim2"); return v end

    if buffer then
        local buffer_fromstring = buffer.fromstring
        SOTR_ENC_BUF = function(s) check(type(s) == "string", "SOTR_ENC_BUF expects a string"); return buffer_fromstring(s) end
    else
        SOTR_ENC_BUF = function(s) check(type(s) == "string", "SOTR_ENC_BUF expects a string"); return s end
    end

    SOTR_EXPOSE = function(f) check(type(f) == "function", "SOTR_EXPOSE expects a function"); return f end
    SOTR_JIT = function(f) check(type(f) == "function", "SOTR_JIT expects a function"); return f end
    SOTR_JIT_MAX = function(f) check(type(f) == "function", "SOTR_JIT_MAX expects a function"); return f end
    SOTR_NO_UPVALUES = function(f) check(type(f) == "function", "SOTR_NO_UPVALUES expects a function"); return f end
    SOTR_SECURE_CALL = function(f) check(type(f) == "function", "SOTR_SECURE_CALL expects a function"); return f end

    SOTR_INLINE = function(f, ...) check(type(f) == "function", "SOTR_INLINE expects a function"); return f(...) end

    SOTR_PRECHECK = function(fn) check(type(fn) == "function", "SOTR_PRECHECK expects a function"); fn() end

    SOTR_REWRITE = function(n) check(type(n) == "number", "SOTR_REWRITE expects a number"); return n end

    SOTR_KILL = function() end
    SOTR_CLEAR_STACK = function() end

    -- Line macro (call form: SOTR_LINE()):

    SOTR_LINE = function()
        local line = debug and debug.info and debug.info(2, "l")
        return type(line) == "number" and line or 0
    end

    -- Attributes:

    local __attribute = function() end

    SOTR_ATTRIBUTES = __attribute

    ENCRYPT = __attribute
    VM = __attribute
    PRESET = __attribute
    OPTIMIZE = __attribute
    NO_UPVALUES = __attribute
    ERROR_HANDLING = __attribute

    UNROLL = __attribute
    INLINE = __attribute

    TRANSFORM = __attribute

    -- VM Options: (DEFAULT is shared with the PRESET options below)

    DEFAULT = __attribute
    NONE = __attribute
    FAST = __attribute
    SWIFT = __attribute

    -- PRESET Options: (FAST is shared with the VM options above)

    BALANCED = __attribute
    SECURE = __attribute
    SECURE_MAX = __attribute

    -- TRANSFORM Options:

    EXTRACT = __attribute
    CONTROL_FLOW = __attribute
    REWRITE_NAMECALLS = __attribute

    -- EXTRACT Options:

    GLOBALS = __attribute
    CONSTANTS = __attribute

    LPH_ENCSTR, MV_ENC_STR, WYNF_ENC_STRING = SOTR_ENC_STR, SOTR_ENC_STR, SOTR_ENC_STR
    LPH_ENCNUM, WYNF_ENC_NUM = SOTR_ENC_NUM, SOTR_ENC_NUM
    LPH_ENCFUNC, MV_ENC_FUNC, WYNF_ENC_FUNC = SOTR_ENC_FUNC, SOTR_ENC_FUNC, SOTR_ENC_FUNC
    LPH_ENCBUF = SOTR_ENC_BUF
    SOTR_NO_VIRTUALIZE, LPH_NO_VIRTUALIZE, MV_OMIT_VM, WYNF_NO_VIRTUALIZE = SOTR_EXPOSE, SOTR_EXPOSE, SOTR_EXPOSE, SOTR_EXPOSE
    LPH_JIT = SOTR_JIT
    LPH_JIT_MAX = SOTR_JIT_MAX
    LPH_NO_UPVALUES = SOTR_NO_UPVALUES
    WYNF_SECURE_CALL = SOTR_SECURE_CALL
    LPH_INLINE = SOTR_INLINE
    LPH_PRECHECK = SOTR_PRECHECK
    LPH_REWRITE = SOTR_REWRITE
    LPH_ATTRIBUTES = SOTR_ATTRIBUTES
    SOTR_CRASH, LPH_CRASH, MV_CRASH, WYNF_CRASH = SOTR_KILL, SOTR_KILL, SOTR_KILL, SOTR_KILL
    WYNF_LINE = SOTR_LINE -- call-form alias of SOTR_LINE (LPH_LINE / MV_LINE are markers, not defined)
end

SOTR_OBFUSCATED

Type: boolean

Returns true if the current script is obfuscated, and false otherwise. Useful for toggling debug behavior or hiding sensitive logic paths in plain builds.

Aliases: LPH_OBFUSCATED, MV_OBFUSCATED, WYNF_OBFUSCATED

if SOTR_OBFUSCATED then
    print("Obfuscated by Soteria")
end

SOTR_LINE

Type: () → number

Expands to the current source line number at the point of use. Helpful for logging, error reporting, and runtime diagnostics without relying on debug.traceback.

Aliases: LPH_LINE (variable), MV_LINE (variable), WYNF_LINE (function)

local function assert_eq(a, b)
    if a ~= b then
        error("Assertion failed at line " .. SOTR_LINE() .. ": expected " .. tostring(b) .. ", got " .. tostring(a))
    end
end

assert_eq(1 + 1, 2)

SOTR_KILL

Type: () → never

Immediately crashes the Lua VM. Use this as a hard tripwire, placing it behind anti-tamper checks, license validation, or integrity guards ensures the process cannot continue if the condition is violated.

This is irreversible. Once called, the VM is terminated and no further code executes. Do not use in hot paths or without a deliberate condition guard.

Aliases: SOTR_CRASH, LPH_CRASH, MV_CRASH, WYNF_CRASH

local expired = true

if expired then
    SOTR_KILL()
end

SOTR_GUID

Type: string

Expands to a random hex string generated once at obfuscation time. Every obfuscation run produces a different value, but all uses of SOTR_GUID within the same run expand to the same string. Useful for unique script instance identification or anti-leak fingerprinting.

local scriptId = SOTR_GUID
print("Script ID:", scriptId) --> e.g. "817d3b823e8f867"

SOTR_TIMESTAMP

Type: number

Expands to the Unix timestamp of when the script was obfuscated. Useful for expiry logic, build tracking, or logging when a particular build was generated.

local obfuscatedAt = SOTR_TIMESTAMP
local ageInDays = (os.time() - obfuscatedAt) / 86400

print(string.format("This script was obfuscated %.1f days ago", ageInDays))

SOTR_VERSION

Type: string

Expands to the current Soteria obfuscator version string at the time of obfuscation. Useful for debugging, watermarking, or asserting a minimum obfuscator version at runtime.

print("Protected with Soteria " .. SOTR_VERSION)

SOTR_TARGET

Type: string

Expands to the target the script was obfuscated for, such as Luau, Roblox, or Studio. Useful for branching behavior that only makes sense on a specific runtime.

if SOTR_TARGET == "Roblox" then
    print("Running on Roblox")
end

SOTR_ENC_STR

Type: (string, key?, runtimeKey?) → string

Rewrites a string literal into self-decrypting source, so the plaintext never appears in the compiled output. An optional static key (a build-time literal) folds in an extra XOR layer; an optional runtime key is instead XORed at runtime, so the output only decrypts when your runtime key provider returns the same value. A runtime key requires a static key to encrypt against.

Aliases: LPH_ENCSTR, MV_ENC_STR, WYNF_ENC_STRING

local pw  = SOTR_ENC_STR("hunter2")                -- no key: pure obfuscation
local pw2 = SOTR_ENC_STR("hunter2", 123)           -- integer key baked in
local pw3 = SOTR_ENC_STR("hunter2", "MYKEY")       -- string key baked in
local pw4 = SOTR_ENC_STR("hunter2", "MYKEY", getKey()) -- only decrypts if getKey() == "MYKEY"

SOTR_ENC_NUM

Type: (number, key?, runtimeKey?) → number

Rewrites a numeric literal so it is rebuilt at runtime via rotate and double-XOR steps instead of appearing as a plain constant. Takes the same optional static/runtime key layers as SOTR_ENC_STR. Useful for obscuring license codes, version checks, or magic constants.

Aliases: LPH_ENCNUM, WYNF_ENC_NUM

local licenseCode = SOTR_ENC_NUM(209715200)
local n = SOTR_ENC_NUM(1337, "MYKEY")

if code == licenseCode then
    print("License valid")
end

SOTR_ENC_FUNC

Type: (function, a, b) → function

Splices an equality guard into the start of the function body: if a ~= b then return end. The two values are evaluated wherever the function is called, so the body only proceeds when they match, otherwise the call silently returns early.

a and b are compared every call, not just once at definition. Pick values that are cheap to compute and only equal under the condition you want to gate on.

Aliases: LPH_ENCFUNC, MV_ENC_FUNC, WYNF_ENC_FUNC

local secureHandler = SOTR_ENC_FUNC(function(eventType, payload, ...)
    print(eventType, payload, ...)
end, getKey(), "expected-key")

-- runs normally when getKey() == "expected-key", otherwise returns immediately
secureHandler("AUTH_EVENT", "user_login", 42)

SOTR_EXPOSE

Type: (function) → function

Wraps a function to exclude it from virtualization entirely (no-virtualize), keeping the body native and readable. Use this for hot paths where virtualization overhead is unacceptable and the logic inside has nothing worth hiding.

Exposed functions receive no virtualization protection. Sensitive logic inside an exposed function can be more easily reverse engineered. Reserve this macro for performance-critical code that contains no secrets.

Aliases: SOTR_NO_VIRTUALIZE, LPH_NO_VIRTUALIZE, MV_OMIT_VM, WYNF_NO_VIRTUALIZE

local multiply = SOTR_EXPOSE(function(a, b, size)
    local result = {}
    for i = 1, size do
        result[i] = {}
        for j = 1, size do
            local sum = 0
            for k = 1, size do
                sum = sum + a[i][k] * b[k][j]
            end
            result[i][j] = sum
        end
    end
    return result
end)

local out = multiply(matA, matB, 64)

SOTR_SECURE_CALL

Type: (function) → function

Splices a stack-root guard into the function body. If the closure is lifted out of its original call chain and replayed from a foreign stack root (a common technique for hooking or re-firing callbacks), the guard trips and the call bails.

Aliases: WYNF_SECURE_CALL

local secureHandler = SOTR_SECURE_CALL(function(eventType, payload, ...)
    print(eventType, payload, ...)
end)

-- Invoke normally, bails if replayed from outside its original stack root
secureHandler("AUTH_EVENT", "user_login", 42)

SOTR_ENC_BUF

Type: (buffer | string, key?, runtimeKey?) → buffer

Encrypts buffer bytes (or a string, wrapped into a buffer) at compile-time and rewraps them at runtime. The raw byte contents are never present in the compiled output. Takes the same optional static/runtime key layers as SOTR_ENC_STR.

Aliases: LPH_ENCBUF

local payload = SOTR_ENC_BUF(buffer.fromstring("secret-payload"))
print(buffer.tostring(payload)) --> "secret-payload" (decrypted at runtime)

SOTR_ENC_UDIM

Type: (UDim | UDim2, key?) → UDim | UDim2

Encrypts each numeric component of a UDim/UDim2 constructor at compile-time, keeping the constructor call itself intact. Useful for hiding layout values that would otherwise leak information about a GUI's structure to someone reading the compiled output.

local size = SOTR_ENC_UDIM(UDim2.new(0.5, 0, 0.5, 0))
frame.Size = size

SOTR_ENC_VEC

Type: (Vector2 | Vector3 | vector, key?) → Vector2 | Vector3 | vector

Encrypts each numeric component of a Vector2/Vector3/vector constructor at compile-time, keeping the constructor call itself intact. Non-numeric components (a variable passed in place of a literal) are left as-is. Useful for obscuring spawn points, hitbox sizes, or other positional data embedded in a script.

local spawnPoint = SOTR_ENC_VEC(Vector3.new(120, 4, -85))
character:PivotTo(CFrame.new(spawnPoint))

SOTR_INLINE

Type: (function, …args) → any

Rewrites the call site into an immediately-invoked function expression, so the body runs in place instead of through a real function call. Best suited for small, one-off blocks where the call overhead itself is what you want to avoid.

This runs the function once, at the call site, rather than binding a reusable function. Pass any arguments the body needs after the function.

Aliases: LPH_INLINE

local x = SOTR_INLINE(function()
    return heavy()
end)

local y = SOTR_INLINE(function(a, b)
    return a + b
end, 2, 3)

SOTR_CLEAR_STACK

Type: () → nil

Assigns nil to every local declared in the enclosing block so far, scrubbing them from the Lua stack so they cannot be recovered by a debugger or memory scanner once they've gone out of logical use.

Must be used in statement position, not as part of an expression.
local key = "super-secret-key"
local verified = validate(key)
SOTR_CLEAR_STACK() -- key, verified = nil, nil

if verified then
    print("Access granted")
end

SOTR_PRECHECK

Type: (() → any, expected?) → nil

Hoists an integrity check to the top of the chunk. Runs the function, and if its result doesn't equal expected (a scalar or a table compared element-wise), the whole script returns immediately instead of continuing. With no expected argument, the function just runs for its side effects. Multiple SOTR_PRECHECK calls chain in source order, any mismatch halts the script.

The function runs at the top of the chunk, not at the point in your source where you wrote the call. Keep it fast and side-effect free.

Aliases: LPH_PRECHECK

SOTR_PRECHECK(function()
    return { game.PlaceId, plr.UserId }
end, { 0xAAAA, 0xBBBB })

SOTR_PRECHECK(function()
    return typeof(getgenv) == "nil"
end, true)

SOTR_REWRITE

Type: (number, opts?) → number

Rewrites a non-negative integer constant less than 2^32 as an equivalent mixed boolean-arithmetic (MBA) expression at compile-time, so the literal value never appears in the compiled output. Any other value (a float, a non-constant, or out of that range) is left exactly as written. The optional opts.preset controls nesting depth: fast, standard/strong (default), or extreme.

Aliases: LPH_REWRITE

local threshold = SOTR_REWRITE(100)
local deep = SOTR_REWRITE(987654, { preset = "extreme" })

if score >= threshold then
    print("Passed")
end

SOTR_NO_UPVALUES

Type: (function) → function

Wraps a function so the resulting callable has zero upvalues, for compatibility with tools that hook or inspect via hookfunction/hookmetamethod and expect an upvalue-free target.

Requires runtime loadstring/load. On hosts without it (e.g. Roblox Studio) the function still runs normally, just without the zero-upvalue guarantee.

Aliases: LPH_NO_UPVALUES

local handler = SOTR_NO_UPVALUES(function(...)
    print(...)
end)

SOTR_JIT

Type: (function) → function

Wraps a function to keep it native for speed, while still control-flow flattening it with a plain dispatch, so it's fast without shipping as cleartext the way SOTR_EXPOSE does. Use this on hot, performance-critical functions where the overhead of interpretation is measurable.

Aliases: LPH_JIT

local hotPath = SOTR_JIT(function()
    -- performance-critical work
end)

SOTR_JIT_MAX

Type: (function) → function

Like SOTR_JIT, but flattens the control flow at full opacity instead of a plain dispatch. Trades a heavier flatten for peak runtime performance while staying native.

Aliases: LPH_JIT_MAX

local process = SOTR_JIT_MAX(function(data)
    -- work that benefits from aggressive optimization
end)
Attributes
SoteriaSoteria

Secure your Luau scripts and accelerate your developer experience.

Products

  • Oracle
  • Genesis

Tools

  • Script Library
  • DevEx Converter
  • FFlags Directory

Docs & Support

  • Documentation
  • Roblox Status
  • Error Codes
  • Runtimes

Company

  • About
  • Blog
  • Discord
  • Contact

Legal

  • Terms of Service
  • Privacy Policy
  • Cookie Policy
  • Acceptable Use

© 2026 Soteria. All rights reserved.

SoteriaSoteria
Pricing
Get Started