Skip to content

xenomorphtech/lux

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Lux

Minimalist compiler targeting Erlang/BEAM with content-addressed function modules.

License

MIT. See LICENSE.

Run Example

This compiles one .core and one .beam per function (module names are hashes), and writes metadata for introspection.

cd /home/sdancer/lux
cargo run -- examples/fib.lux

Generated artifacts live under target/lux/ by default:

cat target/lux/artifacts/fib.meta.json

Run the compiled entry function (replace module hash if different):

erl -noshell -pa /home/sdancer/lux/target/lux/artifacts -eval "io:format(\"~p~n\", ['e8a56de9f0f836e0':apply()]), halt()."

Expected output for examples/fib.lux:

55

Set LUX_HOME to use a different workspace:

LUX_HOME=/tmp/lux-dev cargo run -- examples/fib.lux

Local Service

Start the local HTTP service:

LUX_HOME=/path/to/durable/lux-state cargo run -- --serve

LUX_HOME contains the SQLite source-of-record plus rebuildable runtime materializations. Set it outside target/ for durable development; the default target/lux location is intended for disposable local evaluation.

Health check:

curl -sS http://127.0.0.1:4002/health

The durable development unit is an atomic function or type symbol in a database-backed namespace. Create a function without creating or resubmitting a .lux file:

curl -sS -X POST http://127.0.0.1:4002/symbols/put \
  -H 'content-type: application/json' \
  --data '{"namespace":"dev","kind":"function","symbol":"fib","source":"fn fib(n) { if n <= 1 { n } else { fib(n - 1) + fib(n - 2) } }","expected_generation":0}'

That single operation validates the symbol identity, compiles it against the current namespace, publishes generation 1, and creates a snapshot. To replace the function, send only its new definition plus the returned generation and revision ID:

curl -sS -X POST http://127.0.0.1:4002/symbols/put \
  -H 'content-type: application/json' \
  --data '{"namespace":"dev","kind":"function","symbol":"fib","source":"fn fib(n) { if n < 2 { n } else { fib(n - 1) + fib(n - 2) } }","expected_generation":1,"expected_revision_id":"REVISION_ID"}'

Because artifacts contain immutable dependency hashes, a replacement also rebuilds the transitive callers of the old artifact in that same atomic generation. Their source revisions do not change, and unrelated bindings are carried forward.

Inspect symbol metadata or one symbol’s immutable source revision:

curl -sS 'http://127.0.0.1:4002/symbols?namespace=dev'
curl -sS 'http://127.0.0.1:4002/symbol?namespace=dev&kind=function&symbol=fib'

Type symbols use kind: "type"; a type edit rebuilds affected published function artifacts and commits all resulting bindings in the same generation. The older changeset endpoints remain for bulk import and provenance compatibility, not normal editing.

Use the namespace REPL for the same workflow interactively:

cargo run --bin repl -- --namespace dev

Or expose the same symbol-native operations over MCP stdio while the service is running:

cargo run --bin lux-mcp -- --server http://127.0.0.1:4002

The authoring MCP surface is lux.list_symbols, lux.get_symbol, and lux.put_symbol. lux.put_symbol is the atomic compile/publish/snapshot operation; it never asks an agent to calculate source offsets or submit a whole namespace.

See Namespace-Native Development for atomic symbol revisions, optimistic concurrency, MCP tools, and inspection endpoints. The current Albion guest-account port and its remaining capability work are tracked in Albion Clientless Port.

Run published code from a snapshot:

curl -sS -X POST http://127.0.0.1:4002/run \
  -H 'content-type: application/json' \
  --data '{"snapshot_id":1,"target":"main"}'

Deploy a long-running instance from a snapshot:

curl -sS -X POST http://127.0.0.1:4002/deploy \
  -H 'content-type: application/json' \
  --data '{"snapshot_id":1,"target":"main"}'

List and inspect instances:

curl -sS http://127.0.0.1:4002/instances
curl -sS http://127.0.0.1:4002/instances/<instance_id>

Stop an instance:

curl -sS -X POST http://127.0.0.1:4002/instances/<instance_id>/stop

Evaluate an ephemeral snippet against a snapshot:

curl -sS -X POST http://127.0.0.1:4002/eval \
  -H 'content-type: application/json' \
  --data '{"snapshot_id":1,"source":"fn main() { fib(10) }"}'

Execution limits can be set per request:

curl -sS -X POST http://127.0.0.1:4002/eval \
  -H 'content-type: application/json' \
  --data '{"snapshot_id":1,"source":"fn main() { main() }","limits":{"timeout_ms":50}}'
curl -sS -X POST http://127.0.0.1:4002/eval \
  -H 'content-type: application/json' \
  --data '{"snapshot_id":1,"source":"fn main() { \"...large value...\" }","limits":{"output_limit_bytes":1024}}'

Inspect recent execution summaries:

curl -sS http://127.0.0.1:4002/executions

Inspect namespaces and frozen snapshots:

curl -sS http://127.0.0.1:4002/namespaces
curl -sS http://127.0.0.1:4002/namespaces/dev
curl -sS http://127.0.0.1:4002/snapshots/1

Diff two namespace generations:

curl -sS 'http://127.0.0.1:4002/namespaces/dev/diff?from=1&to=2'

Filter and page execution summaries:

curl -sS 'http://127.0.0.1:4002/executions?request_kind=eval&status=success&limit=10'

Prune old execution summaries:

curl -sS -X POST http://127.0.0.1:4002/executions/prune \
  -H 'content-type: application/json' \
  --data '{"finished_before_ms":9999999999999}'

POC REPL

Start the service first:

cargo run -- --serve

Use a different port:

cargo run -- --serve --port 4010

Then start the REPL client:

cargo run --bin repl

Point the REPL at a specific port:

cargo run --bin repl -- --port 4010

Minimal session:

:namespace repl
:symbol function fib 1
:paste
fn fib(n) { if n <= 1 { n } else { fib(n - 1) + fib(n - 2) } }
:end
:save
fib(10)

The REPL is a thin client over the HTTP service. Bare lines are evaluated as expressions by wrapping them in fn main() { ... }.

Protected Namespace Resources

Lux stores credentials and device/session state as encrypted, versioned namespace resources in SQLite. By default the service creates or loads $LUX_HOME/resource-key.hex; on Unix it requires a private regular file and creates it with mode 0600. LUX_MASTER_KEY_FILE selects another key file and LUX_MASTER_KEY_HEX provides an explicit process-level override.

Back up the key with the database: losing either one loses access to the protected values. Normal resource APIs and MCP tools expose metadata only. Plaintext reveal is a local administrative path gated by LUX_ALLOW_RESOURCE_REVEAL=1, and is never an MCP tool.

Security Model

The intended security boundary for Lux is the language and type/capability system, not OS sandboxing.

  • No syscall / network isolation is intended as the primary enforcement mechanism.
  • No per-job OS sandboxing is intended as the primary enforcement mechanism.
  • The goal is to make unsafe APIs unavailable at compile time by not admitting them into the typed execution environment.
  • In other words, code should be unable to name or typecheck against capabilities that are not explicitly present in the environment.

This is meant to be a stronger and more principled model than relying on ad hoc runtime blocking. It should ultimately stand on the correctness of the language, type system, and capability surface, rather than on post hoc process restrictions.

Current runtime limits such as timeouts and output caps are still useful operational controls, but they are not the long-term security story.

Releases

Packages

Contributors

Languages