Ludi is a web framework where application code is Lua and the runtime is
Rust (hyper + tokio). The native
module (ludi_core) runs hyper worker threads; your app runs on a single
Lua thread, Node-like.
HTTP clients→Rust corehyper + tokio→mpsc channelone Job per request→Lua threadrouting + handlers
Rust is transport only
It parses HTTP, enforces limits, and forwards each request through a
channel. It knows nothing about routes.
Lua stays single-threaded
All Lua runs on the interpreter that called app:listen() — no locks, no
data races in application code.
No FFI callbacks
Worker threads never touch the interpreter. Each hyper worker awaits its
response on a oneshot channel, so slow handlers don’t block accept.
- Lua code is always single-threaded. A handler never runs in parallel
with another handler on the same VM. No locks, no data races in
application code.
- Concurrency comes from the runtime: socket I/O is parallel in Rust.
- Every handler runs inside a coroutine from v1, even though nothing yields
yet. This makes the future async API a non-breaking change.
- A hyper worker reads the request and enforces the body size limit.
- The request crosses to the Lua side as a plain table; the router matches
method + path segments, extracting
:params.
- Global middlewares, route middlewares, handler — run inside one
coroutine, chained by explicit
next() calls.
- Handler errors are caught (
pcall): the client gets a 500, the error
is logged to stderr, never leaked.
- The response table crosses back; hyper writes it out.
- ✅ Single VM, stable public API.
- Async standard library (HTTP client, SQL, filesystem) on the coroutine
bridge.
- Multiple workers: SO_REUSEPORT, one hyper + one VM per worker — the Node
cluster model.
- Memory optimizations: fewer copies on the request/response path.
- Exposing Rust, tokio or mlua concepts to application code.
- Multi-threaded Lua. Parallelism is per-VM, never intra-VM.
- Being a batteries-included MVC framework. Ludi is the HTTP layer.