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.* (including os.execute, os.exit, os.getenv)
  • io.* (including io.open, io.popen)
  • debug.*
  • dofile, loadfile, loadstring
  • package.*, 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.

See also