Getting Started
Macros
Compile-time macros that let you query and control the runtime environment directly from your Luau scripts. Each one expands into generated source at build time, so the macro identifier never survives in the output.
Get Started
To keep your code running normally while unobfuscated, paste this stub at the top of your file. At build time the whole if not SOTR_OBFUSCATED block is stripped and each macro call is replaced.
Environment
Query the build and runtime: whether you are obfuscated, the target, and build metadata.
SOTR_OBFUSCATED
booleanReturns 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")
endSOTR_LINE
() -> numberExpands 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_GUID
stringExpands 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
numberExpands 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
stringExpands 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
stringExpands 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")
endEncryption
Rewrite literals so the plaintext never appears in the compiled output, with optional static and runtime keys.
SOTR_ENC_STR
(string, key?, runtimeKey?) -> stringRewrites 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
(number, key?, runtimeKey?) -> numberRewrites 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")
endSOTR_ENC_FUNC
(function, a, b) -> functionSplices 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.
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_ENC_BUF
(buffer | string, key?, runtimeKey?) -> bufferEncrypts 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
(UDim | UDim2, key?) -> UDim | UDim2Encrypts 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 = sizeSOTR_ENC_VEC
(Vector2 | Vector3 | vector, key?) -> Vector2 | Vector3 | vectorEncrypts 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))Protection
Tamper tripwires, integrity checks, and guards for anti-hooking and anti-leak enforcement.
SOTR_KILL
() -> neverImmediately 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.
Aliases: SOTR_CRASH, LPH_CRASH, MV_CRASH, WYNF_CRASH
local expired = true
if expired then
SOTR_KILL()
endSOTR_SECURE_CALL
(function) -> functionSplices 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_PRECHECK
(() -> any, expected?) -> nilHoists 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.
Aliases: LPH_PRECHECK
SOTR_PRECHECK(function()
return { game.PlaceId, plr.UserId }
end, { 0xAAAA, 0xBBBB })
SOTR_PRECHECK(function()
return typeof(getgenv) == "nil"
end, true)SOTR_CLEAR_STACK
() -> nilAssigns 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.
local key = "super-secret-key"
local verified = validate(key)
SOTR_CLEAR_STACK() -- key, verified = nil, nil
if verified then
print("Access granted")
endSOTR_NO_UPVALUES
(function) -> functionWraps 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.
Aliases: LPH_NO_UPVALUES
local handler = SOTR_NO_UPVALUES(function(...)
print(...)
end)SOTR_EXPOSE
(function) -> functionWraps 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.
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)Performance
Keep hot paths fast: inline blocks, stay native, and rewrite constants without shipping them in cleartext.
SOTR_INLINE
(function, ...args) -> anyRewrites 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.
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_JIT
(function) -> functionWraps 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
(function) -> functionLike 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)SOTR_REWRITE
(number, opts?) -> numberRewrites 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