Throwaway prototypes for Sausage v3. Plan and decisions: ~/research/sausage-v3/PLAN.md. Results and evidence: the spike READMEs.
4.7 KiB
S2 shell experiment
The target is a brush shell inside WASI with external uutils commands in separate Wasmtime stores.
All instances share one world root through sausage-vfs.
The process import must preserve streaming, EOF, broken pipes, and exit codes.
Reproduction
The scratch checkout must use brush revision 943553433ae04f5b3bd55d2cf207e39eb9ef2af7.
The toolchain comes from the repository devshell.
git clone --depth 1 https://github.com/reubeno/brush /tmp/s2-shell/brush
git -C /tmp/s2-shell/brush fetch --depth 1 origin 943553433ae04f5b3bd55d2cf207e39eb9ef2af7
git -C /tmp/s2-shell/brush checkout --detach 943553433ae04f5b3bd55d2cf207e39eb9ef2af7
nu --no-config-file /home/surma/src/gitea.surma.technology/surma/sausage/spikes/s2-shell/patch-brush.nu
nix develop -c cargo build --manifest-path /tmp/s2-shell/brush/Cargo.toml -p brush-shell --release --no-default-features --features minimal --target wasm32-wasip2 -j 2
nix develop -c wasm-tools strip /tmp/s2-shell/brush/target/wasm32-wasip2/release/brush.wasm -o /tmp/s2-shell/brush-release.wasm
nix develop -c cargo build --manifest-path spikes/s2-shell/userland/Cargo.toml --release --target wasm32-wasip2 -j 2
nix develop -c cargo build --manifest-path spikes/s2-shell/Cargo.toml -j 2
nu --no-config-file /home/surma/src/gitea.surma.technology/surma/sausage/spikes/s2-shell/compare.nu /home/surma/src/gitea.surma.technology/surma/sausage/spikes/s2-shell/target/debug/s2-shell /tmp/s2-shell/brush-release.wasm
# Run the same comparison again after the cache is warm.
nu --no-config-file /home/surma/src/gitea.surma.technology/surma/sausage/spikes/s2-shell/compare.nu /home/surma/src/gitea.surma.technology/surma/sausage/spikes/s2-shell/target/debug/s2-shell /tmp/s2-shell/brush-release.wasm
nu --no-config-file /home/surma/src/gitea.surma.technology/surma/sausage/spikes/s2-shell/timings.nu /home/surma/src/gitea.surma.technology/surma/sausage/spikes/s2-shell/target/debug/s2-shell /tmp/s2-shell/brush-release.wasm
userland/ builds the six commands from the published uutils 0.12.0 crates.
Its entry point calls libc's chdir with the host-supplied S2_CWD before it dispatches the command.
The packaged uutils component does not import initial-cwd and otherwise starts every command at /.
compare.nu copies the rebuilt multicall component into each bin alias in the test worlds.
The host checks the bin alias and reuses one compiled uutils component for these identical files.
It does not implement arbitrary executable resolution.
corpus.sh covers the requested syntax and traversal into another top-level folder.
compare.nu creates separate native and guest fixtures under /tmp/s2-shell/comparison.
It compares stdout and exit codes, and preserves stderr separately.
It removes the native fixture prefix from stdout because the guest sees the fixture as /.
Bridge
patch-brush.nu copies guest-process.rs into the wasm launcher seam of the scratch checkout.
It adds the WIT import and its dependencies to that checkout only.
The guest launcher transfers bounded chunks between brush's open files and host-backed pipes.
It starts an independent Tokio task for each child's IO pump.
Brush waits for child processes sequentially, so a pump cannot run only inside the child's wait future.
Each pump yields between exchanges so the downstream pipeline stage can execute.
It closes the host output pipe when brush reports a broken pipe.
The host starts each child component in a separate store.
Unix socket pairs provide bounded, streamed stdin, stdout, and stderr.
The host uses the default Cranelift optimization level.
The dev profile optimizes dependencies at level 2, so Cranelift itself does not run unoptimized.
Wasmtime stores compiled modules in /tmp/s2-shell/cache and reuses them across host invocations.
The first comparison warms this cache. Cold compilation can take minutes.
timings.nu compares and times each corpus construct after the cache is warm.
Every child receives the same VFS root, its environment, and its initial working directory.
The guest process exchanges do not buffer a whole command's output.
Brush's existing in-memory pipes still buffer between cooperative shell tasks.
Scope
The experiment supports only the acceptance corpus and its six uutils aliases.
It does not implement exec, signals, process groups, or interactive stdin inheritance.
It treats the shell's original stdin as EOF for external commands.
It does forward redirected stdin and pipeline stdin.
The fixture uses an ordinary file at /dev/null, not a device implementation.
The process import is spike-specific, not a proposed stable ABI.
AgentOS supplies the prior art, not code in this implementation.
Its standard library patches target host_process imports in its V8 runtime.
This experiment does not patch Rust's standard library.