loom
Luce Engineering LuciaOS

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:

Console and processOutput, diagnostics, line input, environment variables, monotonic and wall clocks, sleeping, shell execution, and process exit.
Files and directoriesWhole-file text and bytes, path queries and mutation, directory listing, and fallible open file handles for streaming bytes.
TerminalSize, frame drawing, styles, cursor placement, flush, and snapshots of key, text, mouse, resize, and closed events.
WorkersSpawn and join callbacks for isolated runtimes. Data graphs are copied between heaps; object identity is not shared.
Window and GPULow-level windows and backend-neutral drawing surfaces. The macOS host supplies AppKit and Metal with a CPU fallback.
Machine factsTotal and available memory and logical processor count, reported as fallible facts rather than guessed zeros.

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.