-
Notifications
You must be signed in to change notification settings - Fork 1
feat(runtime): add Generic WASM and Python Reactor execution #31
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
bkmashiro
wants to merge
4
commits into
lambda-feedback:main
Choose a base branch
from
bkmashiro:pr/python-reactor-minimal-v1
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
642b832
feat(runtime): add Generic WASM and Python Reactor
bkmashiro 1528607
test(wasm): add Linux Python Reactor HTTP E2E
bkmashiro ea13dd4
feat(examples): add safe Python Reactor evaluator
bkmashiro 4f1c382
fix(runtime): address WASM review feedback
bkmashiro File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| package cmd | ||
|
|
||
| import ( | ||
| "testing" | ||
|
|
||
| "github.com/stretchr/testify/assert" | ||
| "github.com/stretchr/testify/require" | ||
|
|
||
| "github.com/lambda-feedback/shimmy/config" | ||
| "github.com/lambda-feedback/shimmy/internal/execution/supervisor" | ||
| ) | ||
|
|
||
| func TestValidateRootConfigRequiresCommandForProcessInterfaces(t *testing.T) { | ||
| for _, iface := range []supervisor.IOInterface{supervisor.RpcIO, supervisor.FileIO} { | ||
| t.Run(string(iface), func(t *testing.T) { | ||
| var cfg config.Config | ||
| cfg.Runtime.Supervisor.IO.Interface = iface | ||
| err := validateRootConfig(cfg, "") | ||
| require.Error(t, err) | ||
| assert.Contains(t, err.Error(), "--command") | ||
| }) | ||
| } | ||
| } | ||
|
|
||
| func TestValidateRootConfigAcceptsWasmModuleOverride(t *testing.T) { | ||
| var cfg config.Config | ||
| cfg.Runtime.Supervisor.IO.Interface = supervisor.WasmIO | ||
| require.NoError(t, validateRootConfig(cfg, "/opt/evaluator.wasm")) | ||
| } | ||
|
|
||
| func TestValidateRootConfigRequiresWasmModulePath(t *testing.T) { | ||
| var cfg config.Config | ||
| cfg.Runtime.Supervisor.IO.Interface = supervisor.WasmIO | ||
| err := validateRootConfig(cfg, "") | ||
| require.Error(t, err) | ||
| assert.Contains(t, err.Error(), "FUNCTION_WASM_MODULE") | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,74 @@ | ||
| // shimmy-artifact-check validates caller-produced WebAssembly artifacts without | ||
| // starting Shimmy's production request path. | ||
| package main | ||
|
|
||
| import ( | ||
| "context" | ||
| "encoding/json" | ||
| "flag" | ||
| "fmt" | ||
| "os" | ||
| "os/exec" | ||
|
|
||
| "github.com/lambda-feedback/shimmy/internal/execution/wasm" | ||
| ) | ||
|
|
||
| func main() { | ||
| os.Exit(run(os.Args[1:])) | ||
| } | ||
|
|
||
| func run(args []string) int { | ||
| flags := flag.NewFlagSet("shimmy-artifact-check", flag.ContinueOnError) | ||
| flags.SetOutput(os.Stderr) | ||
| profile := flags.String("profile", "generic", "runtime ABI: generic or python-reactor") | ||
| module := flags.String("module", "", "path to a prebuilt WebAssembly module") | ||
| manifest := flags.String("manifest", "", "Python Reactor manifest path") | ||
| buildCommand := flags.String("build-command", "", "explicit producer command to run before validation") | ||
| buildDir := flags.String("build-dir", ".", "working directory for --build-command") | ||
| jsonOutput := flags.Bool("json", false, "emit a JSON report") | ||
| if err := flags.Parse(args); err != nil { | ||
| return 2 | ||
| } | ||
| if flags.NArg() != 0 { | ||
| fmt.Fprintf(os.Stderr, "unexpected arguments: %v\n", flags.Args()) | ||
| return 2 | ||
| } | ||
|
|
||
| if *buildCommand != "" { | ||
| command := exec.Command("/bin/sh", "-c", *buildCommand) | ||
| command.Dir = *buildDir | ||
| command.Stdout = os.Stdout | ||
| command.Stderr = os.Stderr | ||
| if err := command.Run(); err != nil { | ||
| fmt.Fprintf(os.Stderr, "artifact build failed: %v\n", err) | ||
| return 1 | ||
| } | ||
| } | ||
|
|
||
| report, err := wasm.CheckArtifact(context.Background(), wasm.ArtifactCheckOptions{ | ||
| Profile: *profile, | ||
| ModulePath: *module, | ||
| ManifestPath: *manifest, | ||
| }) | ||
| if err != nil { | ||
| fmt.Fprintln(os.Stderr, err) | ||
| return 1 | ||
| } | ||
| if *jsonOutput { | ||
| encoder := json.NewEncoder(os.Stdout) | ||
| encoder.SetIndent("", " ") | ||
| if err := encoder.Encode(report); err != nil { | ||
| fmt.Fprintf(os.Stderr, "encode report: %v\n", err) | ||
| return 1 | ||
| } | ||
| return 0 | ||
| } | ||
|
|
||
| fmt.Printf("OK %s artifact: %s\n", report.Profile, report.Module) | ||
| fmt.Printf("exports: %v\n", report.Exports) | ||
| fmt.Printf("imports: %v\n", report.Imports) | ||
| for _, warning := range report.Warnings { | ||
| fmt.Printf("WARNING: %s\n", warning) | ||
| } | ||
| return 0 | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,118 @@ | ||
| # WebAssembly execution paths | ||
|
|
||
| Shimmy keeps the existing `rpc` and `file` process interfaces and adds two | ||
| explicit, opt-in WebAssembly paths. Selection is configuration-driven; Shimmy | ||
| does not inspect source files or silently retry a request under another backend. | ||
|
|
||
| ## Generic WebAssembly | ||
|
|
||
| ```bash | ||
| FUNCTION_INTERFACE=wasm | ||
| FUNCTION_WASM_PROFILE=generic | ||
| FUNCTION_WASM_MODULE=/opt/evaluator/evaluator.wasm | ||
| ``` | ||
|
|
||
| The module runs in-process under wazero and exports `memory`, `alloc`, and | ||
| `dispatch`. Shimmy copies each request into guest linear memory, copies the | ||
| response out, and restores the prepared memory before reusing the instance. | ||
|
|
||
| Memory reset uses one portable implementation: a full copy of linear memory. | ||
| There is no snapshot-strategy selector in this path. If a request grows linear | ||
| memory, the instance is discarded because WebAssembly memory cannot shrink back | ||
| to the captured size. | ||
|
|
||
| The generic path has no host filesystem access unless paths are explicitly | ||
| allowed with `FUNCTION_WASM_ALLOWED_PATHS`. Environment variables are similarly | ||
| allowlisted with `FUNCTION_WASM_ALLOWED_ENV`. | ||
|
|
||
| ## Python Reactor | ||
|
|
||
| ```bash | ||
| FUNCTION_INTERFACE=wasm | ||
| FUNCTION_WASM_PROFILE=python-reactor | ||
| FUNCTION_WASM_MODULE=/opt/runtime/python-reactor.wasm | ||
| FUNCTION_WASM_MANIFEST=/opt/runtime/manifest.json | ||
| FUNCTION_WASM_PYTHON_SCRIPT=/opt/evaluator/evaluator.py | ||
| FUNCTION_WASM_PYTHON_LIFECYCLE=snapshot | ||
| ``` | ||
|
|
||
| The prepared trusted script owns `dispatch(method, payload)`. Shimmy verifies | ||
| the following before serving requests: | ||
|
|
||
| - artifact SHA-256 against the manifest; | ||
| - Reactor ABI name and version; | ||
| - required imports, exports, and function signatures; and | ||
| - manifest-declared `python_modules` against the artifact capability section. | ||
|
|
||
| This proves that the selected artifact and manifest are internally consistent. | ||
| Artifact authenticity, trusted Producer commit policy, signatures, and release | ||
| provenance remain deployment-system responsibilities. | ||
|
|
||
| ### Lifecycle choices | ||
|
|
||
| | Value | Behavior | | ||
| |---|---| | ||
| | `snapshot` | Prepare once per slot and restore the full linear-memory copy after each successful request. Failed or timed-out slots are discarded and replenished asynchronously. | | ||
| | `single-use` | Prepare candidates ahead of time, serve each candidate once, then replace it. | | ||
| | `fresh` | Instantiate and prepare a new module for every request. | | ||
|
|
||
| `snapshot` is the default and the only lifecycle that restores memory. Its reset | ||
| implementation is always full-memory copy; there is no snapshot-strategy | ||
| configuration. `single-use` and `fresh` are lifecycle alternatives, not hidden | ||
| fallbacks. Shimmy never changes lifecycle after a request fails. | ||
|
|
||
| Python Reactor does not expose host paths. Leave | ||
| `FUNCTION_WASM_ALLOWED_PATHS` unset. Runtime modules are selected by the | ||
| manifest-validated artifact profile, for example `base`, `numpy-core`, or | ||
| `sympy`. | ||
|
|
||
| ### Linux HTTP verification | ||
|
|
||
| Run the HTTP startup and request-flow check against a real Producer artifact and | ||
| its exact manifest: | ||
|
|
||
| ```bash | ||
| SHIMMY_PYTHON_REACTOR_WASM=/opt/runtime/python-reactor.wasm \ | ||
| SHIMMY_PYTHON_REACTOR_MANIFEST=/opt/runtime/manifest.json \ | ||
| scripts/e2e-python-reactor.sh | ||
| ``` | ||
|
|
||
| The check starts Shimmy, sends two `eval` requests and one `preview` request, | ||
| and verifies prepared-state restoration between requests. | ||
|
|
||
| ## Safe Python evaluator example | ||
|
|
||
| [`examples/safe-eval-python`](../examples/safe-eval-python/README.md) is a | ||
| backend-level Python Reactor example for student Python in `demo`, `io_test`, | ||
| `unit_test`, and `preview` modes. It uses: | ||
|
|
||
| - wazero's WebAssembly capability boundary; | ||
| - request deadlines; | ||
| - artifact, ABI, and manifest-capability validation; | ||
| - full-copy memory reset and failed-slot replacement; and | ||
| - evaluator limits for code, input, tests, and output. | ||
|
|
||
| It does not depend on nsjail, privileged Lambda configuration, Node, Docker, or | ||
| runtime package installation. AST checks provide early feedback and defense in | ||
| depth; they are not a containment boundary. | ||
|
|
||
| ```bash | ||
| SHIMMY_PYTHON_REACTOR_WASM=/path/to/base.wasm \ | ||
| SHIMMY_PYTHON_REACTOR_MANIFEST=/path/to/base.manifest.json \ | ||
| scripts/e2e-safe-eval-python.sh | ||
| ``` | ||
|
|
||
| For the guided base, NumPy, and SymPy examples, follow the | ||
| [quick start](../examples/safe-eval-python/README.md#start-here-first-successful-evaluation). | ||
|
|
||
| ## Security boundary | ||
|
|
||
| WebAssembly isolation, request deadlines, state reset, and evaluator-level | ||
| limits do not form a complete operating-system sandbox. Deployment policy still | ||
| owns process memory, aggregate concurrency, authentication, request-size limits, | ||
| logging, artifact provenance, and network exposure. | ||
|
|
||
| AWS Lambda cannot grant the namespaces or capabilities required to use nsjail | ||
| as a security boundary. On supported Linux hosts or containers, an external OS | ||
| sandbox may be added as a separate deployment layer; Shimmy does not claim that | ||
| boundary for Lambda. |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.