The host
Luce code has no direct system-call or libc escape hatch. Every observable effect crosses one published table of host callbacks. loom is one host for that table; a standalone executable uses the same implementation.
This is a capability boundary, not an after-the-fact sandbox. If a callback
is absent, the operation traps host_unavailable. It does not invent
a result, silently succeed, or switch to a second implementation.
One boundary, several service families
The table currently carries these families:
The exhaustive source-language names and signatures live in the Built-in Reference and Library. Those pages are checked against compiler rosters and executable samples. This page explains the boundary and policy, not a duplicate list.
Files are effects and resources
Whole-file conveniences and open handles are fallible because the world
decides whether an operation succeeds. Luce code must use try or
catch; a preflight existence check never makes the next operation
race-free.
Text reads validate UTF-8. Binary reads use byte collections and make no text
claim. Open file values are shared resource references. Their ARC
contract closes the native handle at the last strong release. Function exits,
error propagation, shared handles, and runtime teardown all exercise that same
path on both engines.
The terminal belongs behind the host
The host controls raw mode, alternate-screen entry, native event decoding, buffering, and escape sequences. Luce programs work with typed terminal operations and event values. A held event is a snapshot; reading the next event does not change the earlier one.
Raw mode starts only when a program uses terminal services, and loom restores the terminal before reporting a trap or uncaught error. Text written through a host-owned terminal or diagnostic channel is sanitized so program data cannot forge cursor movement or terminal state. Standard output remains the program's own byte channel and is not rewritten.
Keys, text, mouse, resize, and close are different events
The current terminal surface does not make applications parse escape
sequences or overload one string with every event shape. std.term
and the maintained termui package expose named keys, printable text,
mouse coordinates/buttons/modifiers, resize, and closed input as distinct
members. Application keymaps remain application policy.
Workers do not share a heap
The host starts and joins native threads, but the runtime defines Luce worker semantics. Every worker owns another runtime and heap. Permitted values and container graphs are rebuilt there; files, tasks, function values, and other non-sendable resources are refused transitively. loom supplies threads, not locks or a shared-memory language.
Windows and surfaces stay low-level
std.ui opens a window and std.gpu exposes its drawing
surface. Metal, Vulkan, AppKit, and native handles never become Luce values.
Higher-level widgets, layout, retained UI state, and callbacks belong in Luce
packages above this narrow boundary.
The current published installer targets macOS ARM64, where loom supplies the
window channel. A host without that channel reports host_unavailable
instead of emulating a window invisibly.
Arguments are inputs, not a service call
A program receives command-line arguments through its main
parameter. A program that does not need them writes func main():.
The argument list follows the same reference rules as every other list; no
special ownership syntax or ambient query is involved.
The compiler and runtime both guard the boundary
Host names are compile-time gated by allow_host. Pure embedding
can reject a program that names an effect before producing an artifact. When
host use is allowed, each runtime callback is still optional and fails closed
if the actual host does not provide it. Compile permission and runtime
availability answer different questions, so both checks remain.
The ABI is versioned and shared
LuceHost is append-only within an ABI version. An artifact records
the version it expects, and loom checks that tag before loading it. A mismatch
is refused by name instead of calling a shifted function pointer.
Compiled execution and the differential oracle call the same runtime semantics. The host supplies effects; it does not decide language rules. That is why a program reports the same trap, error, and exit status whether loom starts its library artifact or a standalone executable starts directly.