CogniGraph / Lua Sandbox

Program
your graph.

CogniGraph's Lua Sandbox lets you combine database operations into one server-side program. Query documents, follow relationships, search, transform the results and return JSON to your application. Lua scripting is part of Community.

01 / More than one query

Keep the steps together.

Some questions need a sequence: find a starting point, follow its connections, apply a rule and shape the answer. Lua keeps that sequence on the server, while your application makes one HTTP request.

  • Assemble retrieval context. Combine text or vector hits with graph traversal, remove duplicate records and return the passages your application needs.
  • Enrich connected records. Read documents, apply your own conditions and make authorized document or relationship updates.
  • Build a focused response. Filter, group and sort results using Lua tables, strings and arithmetic, then return a JSON object.

Use CGQL when the question fits a query. Add Lua when the surrounding control flow belongs with the data. Lua itself does not require a language model.

02 / Three accounts. Two handsets.

Follow the link. Return the answer.

This synthetic Native-backend example uses the homepage case: rook_77, marla.v and dispatch-04 in accounts, all in case CS-0412. The first two are registered on Handset 8813; dispatch-04 on Handset 2207. Directed edges live in registered_on, and handset records carry kind: "handset".

  1. 01 Select the case’s accounts
  2. 02 Follow registered_on edges
  3. 03 Deduplicate and return
Lua / Read-only / Native
local accounts = graph.query([[
  FOR a IN accounts
    FILTER a.case == @case
    SORT a.handle ASC
    RETURN a
]], { case = "CS-0412" })

local handsets, seen = {}, {}
for _, account in ipairs(accounts) do
  local paths = graph.traverse(account._id, {
    edge_collection = "registered_on",
    direction = "outbound",
    max_depth = 1
  })
  for _, path in ipairs(paths) do
    local handset = path.vertices[#path.vertices]
    if handset.kind == "handset" and not seen[handset._id] then
      seen[handset._id] = true
      table.insert(handsets, {
        id = handset._id, label = handset.label
      })
    end
  end
end
table.sort(handsets, function(a, b) return a.label < b.label end)
return { account_count = #accounts, handsets = handsets }

Send the script as the script string in a JSON body to POST /api/lua/execute on your CogniGraph server. With this dataset, the HTTP response is:

JSON / Synthetic example result
{
  "result": {
    "account_count": 3,
    "handsets": [
      {
        "id": "handsets/2207",
        "label": "Handset 2207"
      },
      {
        "id": "handsets/8813",
        "label": "Handset 8813"
      }
    ]
  }
}

Both accounts registered on Handset 8813 contribute to one handset result. The returned object has the shape chosen by the script; the HTTP endpoint wraps it in result.

03 / A database API inside Lua

Read. Search. Traverse. Write.

Documents
get_document and find_documents read records. create_document, update_document, replace_document and delete_document provide authorized writes.
Relationships
neighbors reads adjacent edges, traverse follows paths, and upsert_edge creates or updates an authorized relationship.
Search and queries
text_search performs BM25 retrieval; similarity searches with a supplied vector. query runs parsed CGQL on the Native backend.
Atomic batches
batch groups supported writes into one atomic operation on Native. A whole Lua script is not automatically a transaction: earlier writes can remain if a later step fails.

These functions live on the graph table. Backend capabilities still apply: opaque AQL is unavailable through the public Lua endpoint. Consult the full reference for arguments, return values and supported operations.

04 / Explicit execution boundaries

A program with limits.

  • Restricted environment. System and file I/O libraries, the debug library, module loading and dynamic code loading are removed. Scripts are loaded as text.
  • Instruction budget. The default is one million instructions, accounted in 1,000-instruction intervals. JIT compilation is disabled so compiled loops cannot bypass the hook.
  • Time and cancellation. Configured time budgets are shared across the script and graph callbacks. Dropped requests signal cancellation to the worker. Backend work stops cooperatively when it yields or returns.
  • Separate write authority. Script execution permission does not grant writes. Mutations require documents:write; with authentication disabled, Lua mutations remain disabled.

The sandbox runs inside the server process. Instruction and time controls do not imply a separate container or a per-script memory quota. Enterprise execution also keeps graph operations within the caller's tenant.

Start with a read-only workflow.

Choose a small dataset and a result you can check. Keep CGQL bind values separate from query text, and expand permissions only when the workflow needs them.

Set up your first integration