Docs
/

Leumas LMX

Write, run or extend an LMX script — the 16 interpreters, incl. lma (call any tool), lmi (ask the install), lmq (query), lmai (model), lmg (grids). Read before writing one or adding a mode.

Writing LMX

LMX (shared/engines/lmx, @leumas/lmx) runs 16 small languages in a sandboxed, time-bounded worker thread. Nine are general computing. Five speak Leumas, and two of those are dynamic — their vocabulary is a live registry, so what they can do grows with the ecosystem:

Want to…UseNeeds
call any tool this install haslma dynamicthe tools bridge → POST /api/lmx/run
ask what this deployment can dolmi dynamicthe install bridge → POST /api/lmx/run
read stored rowslmqthe db bridge
drive a language modellmaithe ai bridge
compose a programming gridlmgnothing — runs anywhere
general logic + one adapter calllmxadapters for adapter()
a quick calculationlmy · lmm · lmc
events, notificationslmz · lmn
shell, GUI, network, timerslms lmk lmd lmnet lmtadmin + an env flag

Full generated list with syntax and snippets: shared/engines/lmx/src/INTERPRETERS.md. Deep references: reference/lma.md · reference/lmi.md · reference/lmq.md · reference/lmai.md · reference/lmg.md.

Reach for lma first

Before writing a bespoke script, remember lma can already call every adapter tool, every stored functioncall and user integration, every automation action, every MCP tool and every vendor-neutral capability — ~1,800 verbs, resolved live:

PLANES                                      # what planes exist here
DISCOVER "invoice"                          # search all of them
DESCRIBE a-text.Base64Encode                # declared params
USE a-text.Base64Encode { "text": "{{input}}" } -> e
EACH row IN rows USE a-text.Summarize { "text": "{{row.body}}" } -> summaries
TRACE                                       # what actually ran, and which provider served a cap:

And lmi answers "what can this install do" rather than "what can Leumas do":

INSTALL
CAN ai.text.generate            # this USER's entitlement, three-state
PROVIDERS FOR email.send        # who would actually serve it here
LIST tools WHERE category = "media" LIMIT 20

Run one

leumas-lmx ./script.lmg                     # lmg and the general modes need no host
curl -s -X POST localhost:3000/api/lmx/run -H 'content-type: application/json' \
  -d '{"mode":"lmi","source":"INSTALL"}'    # lma/lmi/lmq/lmai need bridges

GET /api/lmx/interpreters is the live catalog for a given install; GET /api/lmx/demos returns the bundled scripts with source — the fastest working example of any mode.

The six traps

  1. [critical] An lmai reply is not proof a model ran. resolve() never fails — auto falls back to an

echo stub. Branch on usage.stub. Worst case, verified live: a shaped ask on the stub returns your own SHAPE example as a flawless-looking answer, with stub:true, tokens:0 the only tell.

  1. [critical] lmi must never answer "yes" by default. No entitlement engine wired ⇒ unknown, never

true. An unknown reported as yes is a confident wrong claim about someone else's deployment.

  1. [critical] Entitled ≠ fulfillable. CAN and PROVIDERS FOR are different questions; a user can be

entitled to a capability this install has nothing connected to serve.

  1. [critical] lmg cells go in specialConfig, not props. Both execute; only specialConfig (and a

top-level code) is read by the dependency graph, so a props-authored grid prints an empty GRAPH.

  1. lmq timestamps are epoch milliseconds. Use days_ago(n), not a date string.
  2. A bridge-needing mode REFUSES without its bridge rather than returning empty. *"needs the …

bridge"* means you are on the CLI or the lmx adapter — use POST /api/lmx/run.

What bounds a script

Everything a script can reach is a named channel the host wired. Nothing else escapes the worker.

ChannelForBounded by
adapterthe adapter() builtin(unmetered; see below)
dblmqread-only by construction · /db's deny lists · per-row canRead · 5000 rows
ailmairesolveKeyForUser · 20 calls · 60s
toolslmahost scope + USING · 50 calls · passnode.charge · 30s
installlmiread-only; there is no method that changes anything

All are factories called per request with req.user. Never cache one across users.

[critical] Metering asymmetry. /api/adapters/:system/:fn is priced by passnode.guard, but that lives in the HTTP router — registry.run() is not metered, so non-HTTP callers are free. lma closes this for itself by charging the same tool: target; the older adapter() builtin is still unmetered.

Adding an interpreter

FileAdd
src/interpreters/<id>/{index,parser,runner}.jsrun({ filename, source, bridges, inputs })
src/host/interpreters.catalog.jsone entry — lights up the API, Playground, tooltips and Reference tab
src/host/worker.jsone case in dispatch() (+ a channel proxy if it needs one)
src/host/demos.jsone EXT_TO_MODE entry
src/index.jsresolveMode() + CLI dispatch

Prefer a dynamic design. If the new language needs a vocabulary, ask whether a registry already holds it — planes.js turns any registry's exported functions into verbs, and install.js takes the index-service source map. A language built that way never goes stale. Put policy in the mounting app, never in the engine: @leumas/lmx depends only on express and @leumas/grids.

Then a demo in src/scripts/, a case in ops/infra/scripts/check-lmx.mjs, tests in src/test/, and

pnpm docs:lmx        # regenerate INTERPRETERS.md from the catalog
pnpm smoke:lmx       # every safe mode, every bridge, every gate, + docs drift
node --test src/test/*.test.js
pnpm skills:sync && pnpm check:skills

INTERPRETERS.md is generated. Editing it by hand is how it came to document 7 of 11 modes.

Source .claude/skills/leumas-lmx/SKILL.md (no-git)markdownjson
Generated from the Leumas repository. Every page cites the file it came from.leumas.techllms.txt