@volter/browser-wali
v0.1.300
Published
A browser host for WebAssembly Linux Interface programs
Readme
@volter/browser-wali
A browser execution engine for native programs compiled to the
wasm32-wali-linux-musl ABI. It provides bounded processes, pthread workers,
signals, pipes, sockets, loopback servers, terminal I/O, and synchronization
with the shared Browser Substrate filesystem.
BrowserWaliProgram installs a generic BrowserProgramManifest: it fetches
the declared artifact, mounts declared private files from an origin-local
vault, exposes only declared network hosts and browser URLs, and invokes named
actions. Grok and Codex are therefore data under programs/; this package has
no provider-specific execution path.
Program runs use terminal: { columns, rows } to attach a terminal. Direct
BrowserWaliRuntime.run calls use terminal: true; a supplied WaliStdin
can provide its dimensions through terminalSize(). Without this opt-in,
stdio is headless and terminal queries return ENOTTY, including in pthreads.
Missing syscalls and undeclared capabilities fail closed. Native TCP/TLS is forwarded as opaque records by the optional bounded gateway; TLS still terminates inside the unchanged program.
WALI toolchain pins and reusable compatibility patches live in
toolchains/wali. Program-specific build recipes and upstream locks live in
programs/<id>/<version>.
Autoconf cross builds use CONFIG_SITE=<checkout>/toolchains/wali/config.site.
It supplies target ABI facts that configure cannot discover by running a
target executable, including Bash's exit-code offset in the Linux wait word.
Changing a configure answer requires reconfiguring and rebuilding the program;
existing artifacts retain their previous answer.
WALI_LLVM_ROOT selects a retained pinned LLVM directory independently of
WALI_ROOT's sysroot; compiler and linker wrappers consume that directory
read-only, defaulting to $WALI_ROOT/build/llvm.
The Rust toolchain's browser spawn bridge supports argv, environment, cwd and
stdio, but cannot execute child-only pre_exec hooks or change child user/group
credentials. New builds reject those requests with ErrorKind::Unsupported
before dispatch instead of silently skipping them or running them in the
parent. This does not implement Landlock. Existing WASM artifacts must be
rebuilt to receive this toolchain fix.
Native programs can explicitly import wali.SYS_browser_spawn_confined_v1.
It takes the original seven SYS_browser_spawn arguments, followed by an i32
pointer and byte length for UTF-8 JSON { "writableRoots": ["/workspace"] }.
An empty list is read-only. The policy is limited to 64 KiB and the runtime's
128-root limit. Invalid policy data returns -22, invalid policy memory ranges
return -14, and an unnegotiated bridge returns -95. Successful dispatch
returns a child PID; ordinary wait4 observes its completion.
The patched Rust standard library exposes the same request on CommandExt,
only for the WALI target, behind its wali_browser_spawn feature:
#![feature(wali_browser_spawn)]
use std::os::unix::process::CommandExt;
use std::process::Command;
let output = Command::new("sh")
.args(["-c", "printf allowed > /workspace/result.txt"])
.browser_filesystem_policy_json(br#"{"writableRoots":["/workspace"]}"#)
.output()?;The command owns a copy of the policy bytes; changing its environment cannot
clear them. spawn, status and output use the confined operation, including
when Tokio wraps the standard command. Invalid policies fail at spawn. exec
with a policy, unsupported hooks and credential changes refuse. New binaries
require a runtime that supplies the new import; existing artifacts are unchanged.
Child launch checkpoints pending parent writes through the ordered filesystem
channel before dispatch. Reaping imports child patches atomically with their
original metadata instead of replaying them as new parent writes.
Child ownership and pidfds are shared across pthreads. wait4 applies the child
filesystem patch once; poll and epoll observe completion without a consumer
timer. The host requires Atomics.waitAsync for child execution. Tokio uses its
upstream Linux process implementation, including with its time feature disabled.
Cooperative SIGCHLD delivery lets upstream Tokio reap abandoned children and
commit their writes. Signal dispositions are shared; thread masks are inherited.
Unblocked pending signals interrupt shared poll/epoll waits. Delivery occurs
at syscall boundaries, without CPU-loop preemption or a native register context.
Alternate stacks, recursive handlers and SIGCHLD auto-reap dispositions refuse;
other blocking syscalls are not yet signal-interruptible.
The linker-supplied syscall entry requires the
updated runtime when rebuilt; older artifacts are unchanged.
BrowserWaliWorkerProgram negotiates this operation only when its spawn
backend declares capabilities.filesystemPolicy: true; the receiver validates
and forwards the policy to that backend. A direct BrowserWaliRuntime embedder
can declare browserProcess.filesystemPolicyVersion: 1 only if its spawn
callback enforces the requested policy. Root and pthread callers use the same
contract. The shell and WALI child engines enforce it; Node and Python still
refuse it. This operation does not add Landlock or automatically convert a Rust
pre_exec hook into a policy.
WALI program/command handles expose capabilities.filesystemPolicy: true for
registration with the process host. Their run options accept the same
filesystemPolicy; direct BrowserWaliRuntime.run accepts it as well. Guest
write opens, mutations, metadata and mapped-file writeback are checked on the
process-wide filesystem owner for root and pthread callers. Reads remain broad.
Descendants inherit the grant even through ordinary spawn; additional requests
can only narrow it. Host bootstrap and peer restoration are separate from guest
authority. This does not add OS hard-link isolation or durable confined jobs.
