Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 96 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,10 @@ GLOBAL OPTIONS:
function

--arg value, -a value [ --arg value, -a value ] additional arguments for to the worker process. [$FUNCTION_ARGS]
--command value, -c value the command to invoke to start the worker process. [$FUNCTION_COMMAND]
--command value, -c value the command to invoke to start the worker process, or the WASM module path when --interface=wasm. [$FUNCTION_COMMAND]
--cwd value, -d value the working directory for the worker process. [$FUNCTION_WORKING_DIR]
--env value, -e value [ --env value, -e value ] additional environment variables for the worker process. [$FUNCTION_ENV]
--interface value, -i value the interface to use for worker process communication. Options: rpc, file. (default: "rpc") [$FUNCTION_INTERFACE]
--interface value, -i value the interface to use for worker communication. Options: rpc, file, wasm. (default: "rpc") [$FUNCTION_INTERFACE]
--max-workers value, -n value the maximum number of worker processes to run concurrently. (default: number of CPU cores) [$FUNCTION_MAX_PROCS]

rpc
Expand Down Expand Up @@ -245,6 +245,100 @@ For example, a Wolfram Language evaluation function in `evaluation.wl` would be
wolframscript -file evaluation.wl /tmp/shimmy/abc/request-data-123 /tmp/shimmy/abc/response-data-456
```

#### WebAssembly (`--interface wasm`, opt-in)

The WASM interface executes a pre-built WebAssembly module in-process using
wazero. The module can be a WASI module or a small freestanding module as long
as it exports the Shimmy adapter ABI below. This is an execution backend only:
Shimmy still owns the public HTTP/API contract, request validation, command
routing, cases, and response handling.

Shimmy does not compile evaluator source code at request time and does not infer
a source language from dependency files. Language-specific work belongs in build
or deployment recipes that produce an `eval.wasm` artifact.

A generic WASM evaluator module must export:

| Export | Purpose |
|--------|---------|
| `memory` | Guest linear memory. |
| `alloc(size: i32) -> i32` | Reserves memory where Shimmy writes the request JSON. |
| `evaluate(ptr: i32, len: i32) -> i32` | Executes one command and returns a response pointer. |

Shimmy writes this internal adapter envelope into guest memory:

```json
{
"method": "eval",
"params": {
"response": "...",
"answer": "...",
"params": {}
}
}
```

The response pointer returned by `evaluate` must point at:

```text
[p:p+4] little-endian uint32 JSON length
[p+4:p+4+len] JSON object bytes
```

Run a pre-built WASI module with:

```shell
FUNCTION_INTERFACE=wasm \
FUNCTION_WASM_MODULE=/path/to/eval.wasm \
FUNCTION_MAX_PROCS=1 \
shimmy serve
```

`FUNCTION_COMMAND=/path/to/eval.wasm` is also accepted for compatibility, but
`FUNCTION_WASM_MODULE` is clearer for new deployments.

Example build recipes, including package-shaped evaluators:

```shell
# Go package/module
cd examples/demo-go-package
GOOS=wasip1 GOARCH=wasm go build -buildmode=c-shared -o eval.wasm ./cmd/evaluator

# Rust crate/package
cd examples/demo-rust-package
cargo build --target wasm32-unknown-unknown --release

# C++ package with Makefile + Zig's clang driver
cd examples/demo-cpp-package
make wasm OUT=eval.wasm

# C/C++ with wasi-sdk also works when the project exposes the same ABI
/opt/wasi-sdk/bin/clang --target=wasm32-wasip1 ... -o eval.wasm
/opt/wasi-sdk/bin/clang++ --target=wasm32-wasip1 ... -o eval.wasm
```

Shimmy intentionally does not run these build commands from `shimmy serve`.
Build recipes can be overridden in Makefiles, CI, Dockerfiles, or deployment
scripts; the runtime boundary remains the pre-built `eval.wasm` module.

The backend keeps a warm module instance pool and restores a full linear-memory
snapshot after each request. This gives warm reuse without leaking guest mutable
state between requests. Dirty-page restore, Python runtimes, Pyodide, and package
bundling are intentionally out of scope for this generic backend.

Try the state-isolation examples. Linux, or a Linux container, is the reference
environment for evaluator build/test recipes. The scripts also run on macOS when
the same toolchain is installed, but CI/reviewer instructions should assume
Linux by default. These are intentionally small synthetic evaluators for the
Go/C++ artifact path; real language/runtime packaging such as Pyodide is a
separate profile/follow-up.

```shell
scripts/demo-wasm.sh
scripts/demo-cpp-wasm.sh
go test ./internal/execution/wasm -run 'Test(GoStateful|RustCompare|CppCompare|GoPackage|RustPackage|CppPackage)Example_CompilesAndRunsThroughDispatcher' -v
```

### Sandboxed Execution (Linux only, experimental)

Shimmy can wrap each worker process in an [nsjail](https://github.com/google/nsjail) sandbox to safely execute arbitrary, untrusted code. The sandbox provides:
Expand Down
5 changes: 2 additions & 3 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,18 +47,17 @@ functions on arbitrary, serverless platforms.`
&cli.StringFlag{
Name: "interface",
Aliases: []string{"i"},
Usage: "the interface to use for worker process communication. Options: rpc, file.",
Usage: "the interface to use for worker communication. Options: rpc, file, wasm.",
Value: "rpc",
Category: "function",
EnvVars: []string{"FUNCTION_INTERFACE"},
},
&cli.StringFlag{
Name: "command",
Aliases: []string{"c"},
Usage: "the command to invoke to start the worker process.",
Usage: "the command to invoke to start the worker process, or the WASM module path when --interface=wasm.",
Category: "function",
EnvVars: []string{"FUNCTION_COMMAND"},
Required: true,
},
&cli.StringFlag{
Name: "cwd",
Expand Down
69 changes: 69 additions & 0 deletions examples/demo-cpp-compare/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# C++ Compare Evaluator for Shimmy WASM

This example is a minimal, self-contained C++ evaluator that compiles to a
WebAssembly module and runs through Shimmy's opt-in WASM backend. It is not a
port of an existing Lambda Feedback repository; it is a small Go/C++-style
artifact example for validating the generic WASM execution path.

It intentionally mirrors the shape of a simple Lambda Feedback evaluator:

- input: `response`, `answer`, and optional feedback strings in `params`
- output: `{ "command": "eval", "result": { "is_correct", "feedback" } }`

The evaluator also reports `guest_invocation_count` and `snapshot_isolation_ok`
so the demo and integration test can prove that Shimmy reuses a warm WASM
instance while restoring guest memory after each request.

## Build

The reference environment for this example is Linux, or a Linux container, with
Zig installed. The same command also works on macOS when Zig is installed; the
point is to rely on an explicit WASM-capable toolchain rather than the host's
default C++ compiler.

```bash
zig c++ \
-target wasm32-freestanding \
-Oz \
-nostdlib \
-fno-exceptions \
-fno-rtti \
-Wl,--no-entry \
-Wl,--export=alloc \
-Wl,--export=evaluate \
-Wl,--export-memory \
-Wl,--initial-memory=2097152 \
-o eval.wasm \
evaluator.cpp
```

The source avoids libc/libc++ and implements only the small amount of JSON
handling needed for this evaluator, so it can be built as a small freestanding
WebAssembly module. Real C++ evaluators can use a richer build setup, but still
need to expose the same Shimmy WASM ABI:

```text
memory
alloc(size: i32) -> i32
evaluate(req_ptr: i32, req_len: i32) -> i32
```

`evaluate` returns a pointer to `[uint32 little-endian response_len][response JSON bytes]`.

## Run the end-to-end demo

From the repository root:

```bash
./scripts/demo-cpp-wasm.sh
```

The script builds Shimmy, compiles this evaluator to `eval.wasm`, starts Shimmy
with `FUNCTION_INTERFACE=wasm`, sends two HTTP requests, and asserts that both
requests see `guest_invocation_count == 1`.

The Go test suite also compiles this example when `zig` is available:

```bash
go test ./internal/execution/wasm -run TestCppCompareExample_CompilesAndRunsThroughDispatcher -v
```
Loading