Lua API Reference
This is the complete API surface available to Lua scripts running inside CUST/OS. Anything not listed here is not available -- there is no os, io, debug, dofile, or loadfile.
Core globals
These functions are available in every Lua script.
import(className)
Returns a Java class object so you can call its static methods or use it as a constructor.
local MapView = import("com.atakmap.android.maps.MapView")
local mv = MapView:getMapView()
Only classes from the configured package allowlist can be imported. Attempting to import a disallowed class raises a security error.
classForName(name)
Returns the Java Class object for reflection -- useful for enumerating methods or checking annotations.
local clazz = classForName("com.atakmap.android.maps.MapView")
local methods = clazz:getMethods()
runOnUiThread(fn)
Executes a Lua function on the Android main (UI) thread. Blocks the calling thread until done. Use this for any ATAK API that requires UI-thread execution (most marker mutations, dialogs, layout changes).
runOnUiThread(function()
CotMapComponent:getInstance():processCotEvent(event, Bundle())
end)
The function may return a value, which is propagated back to the calling thread.
plugin.list()
Returns a Lua array of loaded ATAK plugin package names.
local pkgs = plugin.list()
for i = 1, #pkgs do
print(pkgs[i])
end
plugin.get(packageName)
Returns a reference to a loaded plugin's main class, if available. Useful for cross-plugin interop, though note that third-party plugins are usually obfuscated -- broadcast intents are generally a better interop path.
Services
These services are available as Lua globals. Each provides a focused API for a specific capability.
scheduling -- Automation management
| Method | Description |
|---|---|
:listAutomations() |
Returns a list of all loaded automation definitions with their state |
:toggleAutomation(name, enabled) |
Enable or disable the named automation |
:deleteAutomation(name) |
Delete the automation's file and unregister it |
:reload() |
Re-scan the automations directory and reload. Returns number of loaded automations. |
scriptManager -- Skill management
| Method | Description |
|---|---|
:reloadSkills() |
Re-scan the skills directory and rebuild the registry |
:embedLoadedSkills() |
Re-embed every loaded skill for the skill selector |
vision -- Object detection
| Method | Description |
|---|---|
:hasDetectionProvider() |
Returns whether at least one detection provider is online |
:getDetectionAdapter() |
Returns the active detection adapter (or nil) for direct use by advanced callers |
Most skills call the detect_buildings / similar tools rather than touching vision directly. The service is mainly useful when authoring a new detection-driven skill.
tts -- Text-to-speech
| Method | Description |
|---|---|
:speak(text) |
Synthesize and play spoken text through the active TTS provider (or Android TTS fallback) |
:stop() |
Stop the current utterance |
rag -- Vector store
| Method | Description |
|---|---|
:store(id, text, metadata, namespace) |
Embed and persist a text chunk |
:retrieve(query, topK, namespace) |
Return the top-K nearest chunks by semantic similarity |
delegation -- Agent delegation
| Method | Description |
|---|---|
:delegate(agentName, task) |
Run a sub-agent and return its final response (blocking) |
In most cases you'll call delegation through the delegate tool (tools.call("delegate", {...})) rather than the service — the tool route goes through the normal audit and approval path.
memory -- Persistent facts
Facts are scoped by (category, key). Valid categories: position, threat, status, preference, sop, reference, general (unknown categories are coerced to general).
| Method | Description |
|---|---|
:saveFact(category, key, value) |
Store a fact |
:getFact(category, key) |
Retrieve a single fact value (or nil) |
:deleteFact(category, key) |
Remove a fact |
:getByCategory(category) |
List every fact in a category |
:getAll() |
List every persistent fact across all categories |
:recallRelevant(query, limit) |
Keyword-match facts across all categories |
Java interop conventions
Use : for methods, . for static fields
local Math = import("java.lang.Math")
Math:atan2(dy, dx) -- method call, colon
local PI = Math.PI -- static field, dot
local mv = MapView:getMapView()
local point = mv:getPoint() -- instance method, colon
Using dot syntax for method calls silently fails because it passes the argument as self incorrectly.
Constructors
Call the class object as a function:
local Bundle = import("android.os.Bundle")
local b = Bundle() -- empty constructor
local Intent = import("android.content.Intent")
local i = Intent("com.foo.ACTION") -- one-arg constructor
Java arrays from Lua tables
Lua tables do not auto-convert to Java arrays. Build Java arrays explicitly:
local Array = import("java.lang.reflect.Array")
local GeoPoint = import("com.atakmap.coremap.maps.coords.GeoPoint")
local pts = Array:newInstance(GeoPoint, 4)
Array:set(pts, 0, p1)
Array:set(pts, 1, p2)
Array:set(pts, 2, p3)
Array:set(pts, 3, p4)
Reading Java arrays
Java arrays do not auto-expose length or []. Use Array:
local methods = clazz:getMethods()
local n = Array:getLength(methods)
for i = 0, n - 1 do
local m = Array:get(methods, i)
print(m:getName())
end
Iterators
Use :iterator() rather than forEach-style callbacks:
local items = rootGroup:getItemsRecursive()
local iter = items:iterator()
while iter:hasNext() do
local item = iter:next()
-- ...
end
Interface callbacks
For interface-based callbacks, declare self as the first argument:
tileCapture:capture(params, {
onCaptureTile = function(self, tile, tileNum, col, row)
-- self is the proxy; tile is the real first arg
end,
})
Missing math functions
The Lua math library is missing some standard entries (e.g., atan2). Use java.lang.Math instead:
local Math = import("java.lang.Math")
local angle = Math:atan2(dy, dx)
Editor scratch pad
When executing code in the editor scratch pad (the Run button), all registered tool functions are pre-loaded as globals. You can call any tool directly:
local pos = get_self_position()
focus_map({ lat = pos.lat, lon = pos.lon })
local nearby = find_nearby({ radius_m = 1000 })
Helper functions from custos.helpers are also available:
local bearing = calc_bearing({ lat1 = 41.63, lon1 = -93.85, lat2 = 42.0, lon2 = -93.0 })
This applies only to scratch pad execution. Scripts running inside the normal agent loop or automations receive only their skill's registered tools.
Sandbox limits
| Limit | Default | What happens on overflow |
|---|---|---|
| Max instructions per execution | 1,000,000 | Error: instruction limit exceeded |
| Max call depth | 50 | Error: stack overflow |
| Max concurrent scripts | 3 | New script waits for an open slot |
| File path scoping | /sdcard/atak/custos |
Security error on file access outside scope |
What is NOT available
For security, these are explicitly removed from the Lua environment:
os.*(includingos.execute,os.exit,os.getenv)io.*(includingio.open,io.popen)debug.*dofile,loadfile,loadstringpackage.*,require
For file I/O, use import("java.io.File") and the java.nio.file.* classes. Path scoping enforces that scripts can only access files within the allowed directories.
Dangerous Java methods (System.exit, Runtime.exec, ProcessBuilder, ClassLoader, Socket, HttpURLConnection) are blocked even if you successfully import the class.