Tursox Procedures

CI

Tursox Procedures is a standalone companion to Tursox. It executes user-defined, versioned Lua 5.3 procedures through a small database capability API while one checked-out Tursox connection and transaction cover the complete procedure call tree.

The Lua VM is the pure-Elixir lua implementation. No Lua NIF, Turso runtime extension, filesystem API, or network API is loaded.

Installation

def deps do
[
{:tursox_procedures, "~> 0.1.0"}
]
end

Tursox Procedures 0.1.x requires Tursox 0.2.1 and lua 1.0.2.

Quick start

Start a Tursox pool, a source, and a procedure service under your supervisor:

children = [
{Tursox.Pool,
name: MyDatabase,
database: "application.db",
pool_size: 4,
busy_timeout: 5_000},
{Tursox.Procedures.Source.Memory, name: MyProcedureSource},
{Tursox.Procedures,
name: MyProcedures,
pool: MyDatabase,
source: {Tursox.Procedures.Source.Memory, MyProcedureSource}}
]

Publish user source:

{:ok, procedure} =
Tursox.Procedures.Source.Memory.publish(
MyProcedureSource,
"approve_order",
"""
local order = db.one(
"SELECT status FROM orders WHERE id = ?",
{args.order_id}
)
if order == nil then
fail("not_found", "order does not exist")
end
procedures.call("reserve_inventory", {
order_id = args.order_id
})
db.exec(
"UPDATE orders SET status = ? WHERE id = ?",
{"approved", args.order_id}
)
return {order_id = args.order_id, status = "approved"}
"""
)

Call it by name:

{:ok, result} =
Tursox.Procedures.call(
MyProcedures,
"approve_order",
%{"order_id" => 42},
caller: %{user_id: "alice"}
)
result.value
#=> %{"order_id" => 42, "status" => "approved"}

Database-backed catalog

Catalog creation is explicit; starting a service never changes application schema:

alias Tursox.Procedures.Source.Database
{:ok, _} = Database.install(MyDatabase)
{:ok, procedure} = Database.publish(MyDatabase, "approve_order", lua_source)
children = [
{Tursox.Procedures,
name: MyProcedures,
pool: MyDatabase,
source: {Database, []}}
]

Every publication creates a new immutable {name, version, source_hash}. Compiled chunks use that identity, so an update cannot replace code underneath a running call.

Transaction and composition semantics

The top-level host owns commit and rollback. Nested procedures:

Any child failure makes the complete transaction uncommittable, even when Lua catches the child error with pcall. Recoverable child savepoints are not part of 0.1.x.

Security model

Deflua blocks filesystem, process, environment, module-loading, and dangerous OS paths by default. Tursox Procedures additionally applies finite instruction, Lua depth, string, source, argument, result, statement, row, database-byte, procedure-call, host timeout, and BEAM heap limits.

A language sandbox is not database authorization. The default Policy.AllowAll is only for trusted procedure authors. Applications accepting untrusted procedure authors should provide a policy module that approves each :call, :query, and :execute operation. Raw SQL has the authority of the configured Tursox connection.

See the guides for the catalog, Lua API, composition, security/limits, operations, architecture, and exact compatibility contract.

Development

mix deps.get
bin/qa_check.sh

License

MIT. Dependency notices are in THIRD_PARTY_NOTICES.md.