Security Boundaries
Sandbox Policy
Last updated: 2026-10-15
Macro Handler runs Lua scripts inside a sandboxed runtime. This page documents which APIs are open to scripts, which dangerous constructs are kept closed, and which Android permissions back the runtime calls — all in one place.
1. Forbidden APIs (closed)
The constructs below are closed in the sandbox. A script that uses one of them is rejected before it starts with "Script blocked by security policy" and the reason; a call to a removed function raises a Lua error. Nothing fails silently.
- Blocked
loadstring / load / loadfile / dofileTurning text into code at run time is disabled: loadstring, load, loadfile, and dofile are removed, and a script that calls them is rejected before it starts. What a shared script does must stay readable before it runs.
- Blocked
require / packageExternal module loading is blocked. The entire API surface is shipped with the app; no unexpected external code can be injected.
- Blocked
io.* / os.executeThere is no shell, subprocess, or direct file access: the io library (including io.popen) and os.execute are removed. File work goes through the sandboxed File API. This is the core safety guarantee for shared macros.
- Blocked
os.exit / os.remove / os.rename / os.tmpname / os.getenv / os.setlocaleOnly the time functions remain in the os library: os.clock, os.date, os.time, and os.difftime.
- Blocked
debug.*The Lua debug library is fully disabled. A sandboxed script cannot reach host-side state (upvalues, getlocal, sethook).
- Blocked
setmetatable / getmetatablesetmetatable and getmetatable are removed once the app's own helpers have loaded, and a script that calls them is rejected before it starts; the string library is read-only. User scripts cannot install metatables.
- Blocked
rawget / rawset / getfenv / setfenvRaw table access and environment changes are closed: these functions are removed, and a script that calls them directly or in disguised form is rejected.
- Blocked
coroutine.create / wrap / resume / yieldCreating and running coroutines is closed; only coroutine.status and coroutine.isyieldable remain. Use Async.defer, Async.every, and the promise APIs for timers and waiting.
- Blocked
collectgarbage / string.dumpControlling the garbage collector and exporting functions as bytecode are closed. A script that calls string.dump is rejected before it starts.
- Blocked
luajava / Java.type / Class.forNameThere is no Java bridge or reflection access: luajava is removed, and a script that uses luajava, Java.type, Class.forName, Runtime.getRuntime, or ProcessBuilder is rejected before it starts.
- Blocked
// & | ~ << >> (Lua 5.4)The Lua 5.4-only floor division (//) and bitwise operators (&, |, ~, <<, >>) are not available. So that scripts behave the same on the LuaJ and native Lua runtimes, a script that uses them is rejected before it starts. Use math.floor(a / b) for floor division; the not-equal comparison (~=) is fine. A // at the start of a line is read as a comment.
- Blocked
System.reboot / lockScreen / screenOff / powerDialogCommands that reboot the device, lock or turn off the screen, or open the power menu are blocked, including sending lock_screen or power_dialog through System.globalAction, System.pressKey, or System.keyevent. System.openApp, System.closeApp, and System.takeScreenshot are allowed but noted in the runtime log as high-impact actions.
- Blocked
Request("http://…")Network requests must use HTTPS. A literal http:// address in a script is rejected before the script starts, and any other non-HTTPS request is refused when it is made.
- Blocked
Control flow inside inline expressions ({{var}})Inline expressions in Visual Builder parameter fields support variable reads + basic math only. ; loadstring require if/while/for and similar are rejected at the block level.
2. Allowed constructs
The public API surface is fully open. The categories below are the most commonly used; see the API documentation for the full 800+ method list.
- Open
The entire public API surface60 global objects + 800+ methods. Region, Asset, Snap, Touch, Request, JSON, Hud, KV, Async, System, etc.
- Open
Error catching with pcallThe pcall(function() ... end) pattern is fully supported across scripts. Unexpected LuaError values can be caught and logged.
- Open
string.* / table.* / math.*The pure (side-effect-free) parts of the standard library are open: string.format / string.sub / table.insert / math.floor and so on.
- Open
Async.defer / every / promise / awaitA sandboxed timer + promise API: MAX 128 timers + 128 promises. All active timers are auto-cancelled when the script stops.
3. Android permissions backing the runtime
Even though the Lua APIs are open inside the sandbox, several calls depend on Android-side permissions. When a permission is denied, the call returns nil/false or silently no-ops; the script does not crash.
- Permission
Display Over Other AppsThe floating overlay panel, HUD, and runtime overlays require this permission. The macro still runs without it, but on-screen controls cannot be drawn.
- Permission
Accessibility ServiceIMAGE / TEXT / COLOR scans, touch simulation, and screen reading depend on this service. If it is disabled, Region:find / quickTap / OCR calls return nil or error out. From version 1.0.51, the layer that receives your taps during optional Teach tap recording and passes them to the app when you lift your finger, the taps of a routine you approved in My Agent Routine, and holding the joystick during Navigation also run through this service; their limits are described in Section 6.2 of the Terms of Service and Section 3.9 of the Privacy Policy.
- Permission
MediaProjection (capture only)Snap.screenRefresh() uses the MediaProjection token under the hood. The Android 14+ user prompt appears on the first capture; if revoked, the next capture re-prompts.
- Permission
Foreground Service (specialUse / mediaProjection)The macro runtime and overlay services are declared with Android 14+ Play-compliant foreground service types. The system keeps them alive alongside a visible notification.
- Permission
POST_NOTIFICATIONS (Android 13+)System.noti() / NOTIFY_TOAST request this permission at runtime. If denied, the call silently no-ops; the macro does not crash.