Lua scripting architecture (P1Start)¶
This document is the contract for the Lua stack: thin daemon, isolated workers, explicit IPC, and RegCube for paths and policy. It complements the spawn order in userland-matrix.md and boot-and-init.md.
1. Binaries¶
Artifact |
Role |
|---|---|
|
Always-on control plane: publishes |
|
Worker: separate address space; argv is |
|
CLI: writes |
The stub luad.elf is removed; init now starts p1-luasvc.elf first in the daemon list.
Naming (matches established services)¶
Role |
Binary |
Pattern |
|---|---|---|
Daemon |
|
Same as |
CLI |
|
Same class as |
Worker |
|
Short executable name; not a |
Control-plane model (Option A — chosen)¶
This stack deliberately implements Option A: p1-luasvc is a thin control plane only (policy, RegCube, IPC 273, execve, waitpid). All language execution (today P1 minimal Lua in Rust, tomorrow PUC-Rio Lua or P1SCode→native if you replace the worker body) stays in lua.elf in a separate address space.
Option B (embed the Lua VM inside p1-luasvc) is not the v1 direction: it trades a short implementation path for worse fault isolation (one interpreter bug can kill the whole scripting service), a larger auditable surface in a long-lived process, and mixed concerns (orchestration + semantics) in one binary. For P1Start’s “explicit, minimal, auditable” style, Option A is the proper default.
RegCube hierarchy¶
All keys use hive HIVE_LOCAL_MACHINE and a topical prefix lua/ — the same style as time/, display/, game_engine/, pxl/, etc. P1Start does not currently use a separate system/lua/ or service/lua/ tree; renaming only Lua would diverge from the rest of userland. Unless the project adopts a global new RegCube layout, lua/* remains the canonical prefix (see §3).
P1 minimal Lua dialect (worker implementation)¶
Source of truth: apps/p1-lua/src/minimal.rs.
Supported:
nil;true/false;local x(same aslocal x = nil);notandor; integers and comparisons (same rules as before);..;#on strings or tables (dense numeric prefix 1…n); tables:{}array part,t[k]withknumber or string,t.fieldsugar,nilremoves slot, sharedRcsemantics;function name(…) … end(global) andlocal function name(…) … end(Lua-style recursion via a forward slot); closures: inner functions capture outerlocal/ parameter bindings through shared cells (upvalues);f(...)calls built-ins orfunctionvalues; built-instype,tonumber,tostring,assert,error,require(one string:dlopenafter path normalize — barem→\lib\m.so,/lib/...→\lib\..., rejects..; success returnstrue, failurerequire failed+ stderr line);print,local, assignment (resolution order: innerdo/localscopes → upvalue → global / outer),do … end(lexical block —localdoes not leak; seeapps/p1-lua/src/minimal.rs),if/while/repeat/ numericfor,break,return(chunkreturnn= exit code; insidefunction,returnyields a value to the caller); comments /;/ ASCII strings.Not supported (yet): varargs
…, metatables, floats, genericfor, multiple return values;if/while/repeat/forbodies still flattenlocaldeclarations into the enclosing function for static capture analysis (Lua-correctdois the scoped exception), …Sample scripts: …
esp/bin/test_lua_table.lua,esp/bin/test_lua_func_table.lua(tables + functions),esp/bin/test_lua_closure.lua(local function+ upvalues),esp/bin/test_lua_do_block.lua(do … endscope + closure).Full Lua 5.x: plan vendored PUC-Rio C core (or another proven runtime) cross-compiled for
x86_64-unknown-none-elfwith a narrowly defined platform layer — not another quick dependency that assumesstd.
2. IPC (cstd::lua_runtime)¶
Wire:
WIRE_LUA_SVC = 273(between time 271 and display 272 in numbering; dedicated Lua plane).Verbs (
IpcMessage::p1from client):RUN = 1,PING = 2.Replies (
p1from daemon to sender):OK = 100,DENIED = 101,EXEC_FAILED = 102,BAD_PATH = 103,DISABLED = 104.p2: whenp1 == OK, the low 32 bits carry the worker’si32exit code (from RegCubelua/last_worker_exitafterwaitpid). Otherwisep2 = 0.
Clients must not pass arbitrary script pointers in IPC payload; the daemon reads the path from RegCube (cross-address-space safe).
3. RegCube (HIVE_LOCAL_MACHINE)¶
Key |
Purpose |
|---|---|
|
Written by |
|
|
|
Default |
|
Optional; if set, script path must start with this prefix (policy hook). |
|
Informative short string updated by the daemon ( |
|
Decimal exit code written by |
Artifact path (not RegCube): lua-last-run.log under \tmp\. The ramdisk includes tmp/ (see image manifest); lua.elf also calls sys_mkdir("\\tmp") before opening the log so older images without the directory still get a log file. Worker tees stdout here; lua.elf uses sys_dup2 (syscall 33, Linux ABI) to map the log file onto fd 2 after open, so code that writes stderr without Rust helpers still lands in the same session file. err_line avoids double-writing when fd 2 is already the log. luactl run dumps this after RUN returns OK and exits with the worker’s exit code (so luactl is script-friendly when a script fails). If open fails, luactl prints a short notice instead of an empty log.
4. Kernel integration¶
sys_execvewith explicitargvreturns the child PID to the parent; the parent is not replaced (P1Start behavior). The daemon uses this to spawnlua.elf, thensys_waitpidfor completion.SERVICE_READY: daemon sends IPC 100 to PID 1 after publishing RegCube defaults (same pattern asp1-timesvc,deskgui).
5. P1SCode and .so libraries¶
P1SCode and host-side tooling are not required for the daemon/worker protocol. If a future tier compiles Lua or P1SCode to native code, keep outputs as normal ELFs or loaded
.sobehind the existing dynamic loader; do not tie the compositor or IDE directly intop1-luasvcwithout updating this document.Shared Lua or policy code belongs in a well-versioned SDK or ramdisk library only when it reduces duplication between worker and host tools; the default worker should remain a small, auditable binary.
6. Operator flow¶
Boot ensures
p1-luasvc.elfis running (init order).From a terminal:
luactl status— RegCube snapshot.luactl run \bin\test.lua— sets path, runs worker; on successluactlprints worker exit code, then dumps\tmp\lua-last-run.log. A bare name (luactl run test.lua) is rewritten to\\bin\\test.luabecauselua.elfinheritsp1-luasvc’s cwd, not the shell’s.
7. Source layout¶
Path |
Notes |
|---|---|
|
Wire + RegCube key constants. |
|
Daemon. |
|
CLI. |
|
Worker binary |
8. Roadmap (phased)¶
Aligned with external review: the daemon + worker + RegCube + wire 273 split stays; evolve lua.elf and policy, not the compositor boundary.
Dialect and runtime in
lua.elf
Shipped: auditable P1 minimal Lua for demos and bring-up. Next proper steps: grow the subset in-tree or replace the worker core with vendored PUC-Rio Lua (static link + OS hooks forno_std) once the toolchain story is solid. Piccolo / mlua remain out until they support this target withoutstd. P1SCode → native ELF /.soremains compatible with the samep1-luasvcboundary.Sandboxing
Process isolation already limits blast radius; next layers are API surface (few syscalls / stubs), path allowlists (extendlua/allow_path_prefixand friends), optional quotas (memory, script size, CPU), and capability-style exposure if the VM exposes native callbacks.P1SCode vs Lua
Decide product stance: Lua as interop/embedding bridge, P1SCode as authoritative app language, or both with clear tiers. Either way,p1-luasvcshould keep only orchestration (exec, policy, IPC), not language semantics.luactlsynchronous vs async
luactl runblocking until the daemon’swaitpidcompletes is appropriate for a minimal CLI. Later: e.g.RUN_DEFERREDverb,lua/last_job_pidor status keys, andluactl run --detachthat returns after ack (optional second reply or RegCube-only completion).Desktop / IDE
Shortcuts or “Run script” actions should shell out toluactlor send 273 via a small helper—not embed policy in deskgui beyond launch convenience.Native stderr from embedded runtimes
Thelua.elfworker maps the session log onto fd 2 viasys_dup2afteropen, sowrite(2, …)from a future Lua VM or C library lands inlua-last-run.log. Other worker binaries still need the same pattern if they want native stderr in-session without going througherr_line.
9. See also (load / link ABI)¶
p1-luasvc uses execve to spawn lua.elf. If the worker (or a future native Lua build) uses PT_INTERP / .so dependencies, the same auxv + dynamic linking contracts apply:
elf-and-loaders.md — kernel
AT_PHDR, stack, interpreter loaddynamic-linking-abi.md —
DT_*,R_X86_64_*as implemented inld.elfipc-wire-reference.md — daemon/CLI wire
273(WIRE_LUA_SVC) in context with othermessage_typevalues