From 91e3716cd06e1aaf16b20eb04b98ce60bebdb815 Mon Sep 17 00:00:00 2001 From: Simon Davies Date: Tue, 10 Nov 2026 10:31:21 +0000 Subject: [PATCH 1/4] Update dependencies to hyperlight v0.19 Signed-off-by: Simon Davies --- Cargo.lock | 71 ++++++++++++++++++++++++------------------------------ Cargo.toml | 8 +++--- 2 files changed, 35 insertions(+), 44 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c72f8527..fb243d2b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -26,6 +26,12 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" +[[package]] +name = "allocator-api2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c880a97d28a3681c0267bd29cff89621202715b065127cd445fa0f0fe0aa2880" + [[package]] name = "android_system_properties" version = "0.1.5" @@ -289,15 +295,6 @@ dependencies = [ "serde_core", ] -[[package]] -name = "buddy_system_allocator" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1af5a01adeeade54c9f5060300227a8ee64c05b6376f28e9b8ee3b72dd2056f" -dependencies = [ - "spin 0.10.0", -] - [[package]] name = "bumpalo" version = "3.20.3" @@ -1454,7 +1451,7 @@ version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" dependencies = [ - "allocator-api2", + "allocator-api2 0.2.21", "equivalent", "foldhash", ] @@ -1465,7 +1462,7 @@ version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" dependencies = [ - "allocator-api2", + "allocator-api2 0.2.21", "equivalent", "foldhash", "serde", @@ -1623,9 +1620,9 @@ dependencies = [ [[package]] name = "hyperlight-common" -version = "0.18.0" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "360d5876788b26af66bf055bca6b4f2c9c930a0fdcaebda4bd7f808c6bd93068" +checksum = "b8255ebc8f04272f64137e19f5c42cdcc1c92df56a63df964d258923c553add6" dependencies = [ "anyhow", "bitflags 2.13.2", @@ -1635,7 +1632,7 @@ dependencies = [ "flatbuffers", "log", "smallvec", - "spin 0.12.3", + "spin", "thiserror", "tracing", "tracing-core", @@ -1643,9 +1640,9 @@ dependencies = [ [[package]] name = "hyperlight-guest" -version = "0.18.0" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7faceeab52cbc0f128e75fa4b0ee9fce17c3c462d7776feca66f41e98bf2a4bb" +checksum = "b2d8614341a6365a37395a975077a6472f0065919fc4e1bbe551097f8682ec2c" dependencies = [ "anyhow", "flatbuffers", @@ -1658,11 +1655,10 @@ dependencies = [ [[package]] name = "hyperlight-guest-bin" -version = "0.18.0" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7eff3c3b94ae4da51f03d535e9a13a4132f5f87694a19514c038a9517cfd750" +checksum = "9e81e9b723d4fb0804f0c6ef57383df6c66e768e443cf9d5bf8548d2e233fe57" dependencies = [ - "buddy_system_allocator", "flatbuffers", "hyperlight-common", "hyperlight-guest", @@ -1671,15 +1667,16 @@ dependencies = [ "hyperlight-libc", "linkme", "log", - "spin 0.12.3", + "spin", + "talc", "tracing", ] [[package]] name = "hyperlight-guest-macro" -version = "0.18.0" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f957baa8d97e2787589bf084a18096c08ab4dd22d4f5a040f153f9f719330ec3" +checksum = "79986640467531455c7a4189f11c6f3a9cb63225bad1525bb5a0c2523390a15a" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -1689,24 +1686,24 @@ dependencies = [ [[package]] name = "hyperlight-guest-tracing" -version = "0.18.0" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a12c240f87acd92e80ea08e9a405e781e4a15b9f38fee5fe261e9ce69b816d3" +checksum = "386e053d56679610902ef68feb1966e043c67c55006954a6afdca89567a2ce21" dependencies = [ "hyperlight-common", - "spin 0.12.3", + "spin", "tracing", "tracing-core", ] [[package]] name = "hyperlight-host" -version = "0.18.0" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40f8b85ddd688890477158b9921b228973d1b70c84b866698838df0f32d2aa44" +checksum = "48ed7feb4bdb2ea3fe07de299f9fb1cbc4246c91991d6ccc2a29eb387f7c7625" dependencies = [ "anyhow", - "bindgen 0.72.1", + "bindgen 0.73.2", "bitflags 2.13.2", "blake3", "bytemuck", @@ -1732,7 +1729,7 @@ dependencies = [ "mshv-bindings", "mshv-ioctls", "oci-spec", - "opentelemetry 0.32.0", + "opentelemetry 0.33.1", "page_size", "parking_lot", "rand 0.10.3", @@ -1745,7 +1742,7 @@ dependencies = [ "thiserror", "tracing", "tracing-core", - "tracing-opentelemetry 0.33.0", + "tracing-opentelemetry 0.34.0", "uuid", "vmm-sys-util", "windows", @@ -1827,19 +1824,19 @@ dependencies = [ "serde", "serde_json", "sha2 0.10.9", - "spin 0.12.3", + "spin", "tempfile", "tracing", ] [[package]] name = "hyperlight-libc" -version = "0.18.0" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a74af1c1cc60140c000a6ebd4eba3121bc2b8f56f134163c25eadca1146b73e0" +checksum = "4f0631fc2defbb09008f2dbcff3c995a8378e7e0efa614228cd6250f7b52c3be" dependencies = [ "anyhow", - "bindgen 0.72.1", + "bindgen 0.73.2", "cc", "glob", ] @@ -3711,12 +3708,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "spin" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5fe4ccb98d9c292d56fec89a5e07da7fc4cf0dc11e156b41793132775d3e591" - [[package]] name = "spin" version = "0.12.3" diff --git a/Cargo.toml b/Cargo.toml index 8cd010aa..7a59200c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,10 +13,10 @@ readme = "README.md" [workspace.dependencies] # Keep the host and guest on the same release for transport compatibility. -hyperlight-guest-bin = { version = "0.18.0", features = ["libc"] } -hyperlight-host = { version = "0.18.0", default-features = false } -hyperlight-common = { version = "0.18.0", default-features = false } -hyperlight-guest = { version = "0.18.0" } +hyperlight-guest-bin = { version = "0.19.0", features = ["libc"] } +hyperlight-host = { version = "0.19.0", default-features = false } +hyperlight-common = { version = "0.19.0", default-features = false } +hyperlight-guest = { version = "0.19.0" } hyperlight-js = { version = "0.5.0", path = "src/hyperlight-js" } hyperlight-js-runtime = { version = "0.5.0", path = "src/hyperlight-js-runtime" } From 5f040cee11c6869347b792a22aa28c081ffa8e01 Mon Sep 17 00:00:00 2001 From: Simon Davies Date: Thu, 12 Nov 2026 11:42:54 +0000 Subject: [PATCH 2/4] update to hyperlight v0.19 and fix breaking changes Signed-off-by: Simon Davies --- Cargo.lock | 58 ++++++------------- Justfile | 1 + README.md | 15 +++++ docs/persistent-snapshots.md | 2 +- .../fixtures/extended_runtime/Cargo.lock | 55 ++++++++++-------- .../examples/runtime_debugging/main.rs | 3 +- .../src/sandbox/sandbox_builder.rs | 53 +++++------------ src/hyperlight-js/tests/host_functions.rs | 2 +- src/hyperlight-js/tests/runtime.rs | 37 ++++++++++++ src/js-host-api/README.md | 8 +-- src/js-host-api/examples/README.md | 6 +- src/js-host-api/examples/calculator.js | 2 +- src/js-host-api/examples/host-functions.js | 3 +- src/js-host-api/examples/mcp-server/README.md | 20 ++++--- .../examples/mcp-server/demo-copilot-cli.ps1 | 19 ++---- .../examples/mcp-server/demo-copilot-cli.sh | 26 +++------ src/js-host-api/examples/mcp-server/server.js | 38 ++++++------ .../examples/mcp-server/tests/config.test.js | 54 ++++++++++++----- .../mcp-server/tests/prompt-examples.test.js | 7 +-- src/js-host-api/examples/simple.js | 3 +- src/js-host-api/examples/unload.js | 2 +- src/js-host-api/examples/user-modules.js | 10 +--- src/js-host-api/lib.js | 1 - src/js-host-api/src/lib.rs | 30 +++------- src/js-host-api/test-examples.sh | 46 +-------------- .../tests/persistent-snapshots.test.js | 2 +- src/js-host-api/tests/sandbox.test.js | 48 ++++++++++++--- 27 files changed, 264 insertions(+), 287 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fb243d2b..83bf7491 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1729,7 +1729,7 @@ dependencies = [ "mshv-bindings", "mshv-ioctls", "oci-spec", - "opentelemetry 0.33.1", + "opentelemetry", "page_size", "parking_lot", "rand 0.10.3", @@ -1742,7 +1742,7 @@ dependencies = [ "thiserror", "tracing", "tracing-core", - "tracing-opentelemetry 0.34.0", + "tracing-opentelemetry", "uuid", "vmm-sys-util", "windows", @@ -1775,7 +1775,7 @@ dependencies = [ "metrics", "metrics-exporter-prometheus", "metrics-util", - "opentelemetry 0.33.1", + "opentelemetry", "opentelemetry-otlp", "opentelemetry-semantic-conventions", "opentelemetry_sdk", @@ -1789,7 +1789,7 @@ dependencies = [ "tokio", "tracing", "tracing-forest", - "tracing-opentelemetry 0.34.0", + "tracing-opentelemetry", "tracing-subscriber", "tracing-tracy", "uuid", @@ -2564,20 +2564,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" -[[package]] -name = "opentelemetry" -version = "0.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0142c63252a9e054e68a4c61a5778f7b14f576274d593f8ce883d191a099682" -dependencies = [ - "futures-core", - "futures-sink", - "js-sys", - "pin-project-lite", - "thiserror", - "tracing", -] - [[package]] name = "opentelemetry" version = "0.33.1" @@ -2601,7 +2587,7 @@ dependencies = [ "async-trait", "bytes", "http", - "opentelemetry 0.33.1", + "opentelemetry", "reqwest", ] @@ -2613,7 +2599,7 @@ checksum = "0756af24841e367128ef31c831999937b8b5c5197d47914590d8f4e2e88c2e1a" dependencies = [ "http", "httpdate", - "opentelemetry 0.33.1", + "opentelemetry", "opentelemetry-http", "opentelemetry-proto", "opentelemetry_sdk", @@ -2631,7 +2617,7 @@ version = "0.33.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d576f5dddc3299341bcd85a04be17b2564844c0c9563214f02d85f2524d1afae" dependencies = [ - "opentelemetry 0.33.1", + "opentelemetry", "opentelemetry_sdk", "prost", "tonic", @@ -2653,7 +2639,7 @@ dependencies = [ "futures-channel", "futures-executor", "futures-util", - "opentelemetry 0.33.1", + "opentelemetry", "percent-encoding", "portable-atomic", "rand 0.9.4", @@ -3801,6 +3787,16 @@ dependencies = [ "syn 2.0.119", ] +[[package]] +name = "talc" +version = "5.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53f8ad14fa1c1b8635114abf9721076fe1e869572a5f26a52690f407292c80dc" +dependencies = [ + "allocator-api2 0.4.0", + "lock_api", +] + [[package]] name = "tempfile" version = "3.27.0" @@ -4141,22 +4137,6 @@ dependencies = [ "tracing-core", ] -[[package]] -name = "tracing-opentelemetry" -version = "0.33.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adbc64cba7137545b8044cb1fe9814f7aacf3c6b5f9b45be8bb5db538befdb26" -dependencies = [ - "js-sys", - "opentelemetry 0.32.0", - "smallvec", - "tracing", - "tracing-core", - "tracing-log", - "tracing-subscriber", - "web-time", -] - [[package]] name = "tracing-opentelemetry" version = "0.34.0" @@ -4164,7 +4144,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0a904802a1b902f43638b677ff2a650847e3b4404101b6c586d648e8c1e3e8fe" dependencies = [ "js-sys", - "opentelemetry 0.33.1", + "opentelemetry", "smallvec", "tracing", "tracing-core", diff --git a/Justfile b/Justfile index d3c12a7f..de10ebf5 100644 --- a/Justfile +++ b/Justfile @@ -138,6 +138,7 @@ run-examples target=default-target features="": (build target) cargo run --profile={{ if target == "debug" {"dev"} else { target } }} {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F " + features } }} --example run_handler echo cargo run --profile={{ if target == "debug" {"dev"} else { target } }} {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F " + features } }} --example run_handler fibonacci cargo run --profile={{ if target == "debug" {"dev"} else { target } }} {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F " + features } }} --example run_handler regex + cargo run --profile={{ if target == "debug" {"dev"} else { target } }} {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F " + features } }} --example user_modules cargo run --profile={{ if target == "debug" {"dev"} else { target } }} {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F function_call_metrics," + features } }} --example metrics cargo run --profile={{ if target == "debug" {"dev"} else { target } }} {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F " + features } }} --example metrics cargo run --profile={{ if target == "debug" {"dev"} else { target } }} {{ if features =="" {'--features guest-call-stats,monitor-wall-clock,monitor-cpu-time'} else if features=="no-default-features" {"--no-default-features -F guest-call-stats,monitor-wall-clock,monitor-cpu-time" } else {"--no-default-features -F guest-call-stats,monitor-wall-clock,monitor-cpu-time," + features } }} --example execution_stats diff --git a/README.md b/README.md index 63500947..deac1743 100644 --- a/README.md +++ b/README.md @@ -91,6 +91,21 @@ To run the tests, run: just test ``` +Use `just test-all` to also run monitor and Node.js binding tests. + +## Guest Memory + +`SandboxBuilder::with_guest_scratch_size(bytes)` controls the shared +writable-memory budget for the guest heap, stack, transport pools, page tables, +and copy-on-write pages. The default and minimum are 5 MiB; smaller requests +are clamped to that minimum. +Increase scratch for allocation-heavy handlers or larger transport pools. This +budget is not a JavaScript-only heap limit or a total host-process memory limit. + +Exhaustion aborts the guest with `GuestAborted(MallocFailed, ...)` and poisons the +sandbox. Restore a snapshot or build a replacement before executing again. +Node.js uses `SandboxBuilder.setScratchSize(bytes)` and reports `ERR_GUEST_ABORT`. + ## Running the examples ### run_handler example diff --git a/docs/persistent-snapshots.md b/docs/persistent-snapshots.md index bcec6eb8..94adb04e 100644 --- a/docs/persistent-snapshots.md +++ b/docs/persistent-snapshots.md @@ -67,7 +67,7 @@ snapshot. TypeScript types the result as `JSSandbox | LoadedJSSandbox`. Narrow i with `instanceof`. Configure process-local host resources on the builder before calling `build()`. -A snapshot's memory layout overrides heap, scratch, and transport settings. +A snapshot's memory layout overrides scratch and transport settings. `Snapshot::checked_load()` in Rust and `Snapshot.checkedLoad()` in Node check every OCI blob against its recorded digest. `Snapshot::load()` and diff --git a/src/hyperlight-js-runtime/tests/fixtures/extended_runtime/Cargo.lock b/src/hyperlight-js-runtime/tests/fixtures/extended_runtime/Cargo.lock index 8b515fbb..190fed92 100644 --- a/src/hyperlight-js-runtime/tests/fixtures/extended_runtime/Cargo.lock +++ b/src/hyperlight-js-runtime/tests/fixtures/extended_runtime/Cargo.lock @@ -17,6 +17,12 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" +[[package]] +name = "allocator-api2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c880a97d28a3681c0267bd29cff89621202715b065127cd445fa0f0fe0aa2880" + [[package]] name = "anstream" version = "1.0.0" @@ -144,15 +150,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "buddy_system_allocator" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1af5a01adeeade54c9f5060300227a8ee64c05b6376f28e9b8ee3b72dd2056f" -dependencies = [ - "spin 0.10.0", -] - [[package]] name = "bytemuck" version = "1.25.0" @@ -413,7 +410,7 @@ version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" dependencies = [ - "allocator-api2", + "allocator-api2 0.2.21", "equivalent", "foldhash", "serde", @@ -443,9 +440,9 @@ dependencies = [ [[package]] name = "hyperlight-common" -version = "0.18.0" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "360d5876788b26af66bf055bca6b4f2c9c930a0fdcaebda4bd7f808c6bd93068" +checksum = "b8255ebc8f04272f64137e19f5c42cdcc1c92df56a63df964d258923c553add6" dependencies = [ "anyhow", "bitflags", @@ -462,9 +459,9 @@ dependencies = [ [[package]] name = "hyperlight-guest" -version = "0.18.0" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7faceeab52cbc0f128e75fa4b0ee9fce17c3c462d7776feca66f41e98bf2a4bb" +checksum = "b2d8614341a6365a37395a975077a6472f0065919fc4e1bbe551097f8682ec2c" dependencies = [ "anyhow", "flatbuffers", @@ -476,11 +473,10 @@ dependencies = [ [[package]] name = "hyperlight-guest-bin" -version = "0.18.0" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7eff3c3b94ae4da51f03d535e9a13a4132f5f87694a19514c038a9517cfd750" +checksum = "9e81e9b723d4fb0804f0c6ef57383df6c66e768e443cf9d5bf8548d2e233fe57" dependencies = [ - "buddy_system_allocator", "flatbuffers", "hyperlight-common", "hyperlight-guest", @@ -490,14 +486,15 @@ dependencies = [ "linkme", "log", "spin 0.12.3", + "talc", "tracing", ] [[package]] name = "hyperlight-guest-macro" -version = "0.18.0" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f957baa8d97e2787589bf084a18096c08ab4dd22d4f5a040f153f9f719330ec3" +checksum = "79986640467531455c7a4189f11c6f3a9cb63225bad1525bb5a0c2523390a15a" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -507,9 +504,9 @@ dependencies = [ [[package]] name = "hyperlight-guest-tracing" -version = "0.18.0" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a12c240f87acd92e80ea08e9a405e781e4a15b9f38fee5fe261e9ce69b816d3" +checksum = "386e053d56679610902ef68feb1966e043c67c55006954a6afdca89567a2ce21" dependencies = [ "hyperlight-common", "spin 0.12.3", @@ -550,12 +547,12 @@ dependencies = [ [[package]] name = "hyperlight-libc" -version = "0.18.0" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a74af1c1cc60140c000a6ebd4eba3121bc2b8f56f134163c25eadca1146b73e0" +checksum = "4f0631fc2defbb09008f2dbcff3c995a8378e7e0efa614228cd6250f7b52c3be" dependencies = [ "anyhow", - "bindgen 0.72.1", + "bindgen 0.73.2", "cc", "glob", ] @@ -972,6 +969,16 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "talc" +version = "5.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53f8ad14fa1c1b8635114abf9721076fe1e869572a5f26a52690f407292c80dc" +dependencies = [ + "allocator-api2 0.4.0", + "lock_api", +] + [[package]] name = "thiserror" version = "2.0.18" diff --git a/src/hyperlight-js/examples/runtime_debugging/main.rs b/src/hyperlight-js/examples/runtime_debugging/main.rs index be1c28e9..8495fcb8 100644 --- a/src/hyperlight-js/examples/runtime_debugging/main.rs +++ b/src/hyperlight-js/examples/runtime_debugging/main.rs @@ -20,8 +20,7 @@ fn builder() -> SandboxBuilder { { SandboxBuilder::new() .with_input_transport_pool_pages(512) // 2 MiB - .with_guest_scratch_size(4 * 1024 * 1024) // room for the transport pool - .with_guest_heap_size(10 * 1024 * 1024) // 10 MiB + .with_guest_scratch_size(10 * 1024 * 1024) .with_debugging_enabled(8080) // debugging on port 8080 } #[cfg(not(gdb))] diff --git a/src/hyperlight-js/src/sandbox/sandbox_builder.rs b/src/hyperlight-js/src/sandbox/sandbox_builder.rs index d57ca096..27203fa1 100644 --- a/src/hyperlight-js/src/sandbox/sandbox_builder.rs +++ b/src/hyperlight-js/src/sandbox/sandbox_builder.rs @@ -42,7 +42,7 @@ pub struct NewSandbox; /// /// [`SandboxBuilder::new`] builds a [`ProtoJSSandbox`]. /// [`SandboxBuilder::from_snapshot`] builds the sandbox type the snapshot captured. -/// A snapshot's memory layout overrides heap, scratch, and transport settings. +/// A snapshot's memory layout overrides scratch and transport settings. /// /// Transport setters record choices. [`Self::get_config`] and /// [`SandboxBuilder::build`] resolve buffer sizes, pool budgets, and descriptor @@ -73,24 +73,11 @@ pub struct SandboxBuilder { /// The minimum scratch size for the JS runtime sandbox. /// -/// The scratch region provides writable physical memory for: -/// - Transport queues and buffer pools -/// - Page table copies (proportional to snapshot size — our ~13 MB guest -/// binary + heap produce ~72 KiB of page tables) -/// - Dynamically allocated pages (GDT/IDT, stack growth, Copy-on-Write -/// resolution during QuickJS initialisation) -/// - Exception stack and metadata (2 pages at the top) -/// -/// Hyperlight's default scratch is sized for smaller guests. The JS -/// runtime needs 1 MiB (0x10_0000) to leave room for QuickJS initialisation -/// after the transport and page-table overheads. -const MIN_SCRATCH_SIZE: usize = 0x10_0000; // 1 MiB - -/// The minimum heap size is 4 MiB. The QuickJS engine needs a -/// reasonable amount of heap during initialisation for builtins, -/// global objects, and the bytecode compiler. This lives in the -/// identity-mapped snapshot region (NOT scratch). -const MIN_HEAP_SIZE: u64 = 4096 * 1024; +/// Scratch backs the guest heap and stack, transport queues and buffer pools, +/// page tables, exception handling, and copy-on-write pages. These allocations +/// share one budget; the configured size is not a JavaScript heap allowance. +/// Increase it for allocation-heavy handlers or larger transport pools. +const MIN_SCRATCH_SIZE: usize = 5 * 1024 * 1024; /// The previous 16 KiB message capacity for each direction. const DEFAULT_LEGACY_BUFFER_SIZE: usize = 16 * 1024; @@ -104,7 +91,6 @@ const MIN_LEGACY_BUFFER_SIZE: usize = 8 * 1024; impl SandboxBuilder { fn with_source(source: S) -> Self { let mut config = SandboxConfiguration::default(); - config.set_heap_size(MIN_HEAP_SIZE); config.set_scratch_size(MIN_SCRATCH_SIZE); Self { @@ -259,26 +245,15 @@ impl SandboxBuilder { } /// Set the guest scratch size in bytes. - /// The scratch region provides writable memory for the guest, including the - /// dynamically-sized stack. Increase this if your guest code needs deep - /// recursion or large local variables. + /// Scratch backs the heap, stack, transport pools, page tables, and + /// copy-on-write pages. Increase it for large objects, deep recursion, + /// or larger transport pools. Exhaustion aborts the guest with `MallocFailed` + /// and poisons the sandbox; restore a snapshot before reusing it. /// - /// Values at or below the JS runtime default (1 MiB) are ignored. + /// Defaults to 5 MiB. Smaller values are raised to this minimum. pub fn with_guest_scratch_size(mut self, guest_scratch_size: usize) -> Self { - if guest_scratch_size > MIN_SCRATCH_SIZE { - self.base_config.set_scratch_size(guest_scratch_size); - } - self - } - - /// Set the guest heap size - /// This is the size of the heap that code executing in the guest can use. - /// If this value is too small then the guest will fail, usually with a malloc failed error - /// The default (and minimum) value for this is set to the value of the MIN_HEAP_SIZE const. - pub fn with_guest_heap_size(mut self, guest_heap_size: u64) -> Self { - if guest_heap_size > MIN_HEAP_SIZE { - self.base_config.set_heap_size(guest_heap_size); - } + self.base_config + .set_scratch_size(guest_scratch_size.max(MIN_SCRATCH_SIZE)); self } @@ -422,7 +397,7 @@ impl SandboxBuilder { impl SandboxBuilder> { /// Create a builder that restores `snapshot`. /// - /// The snapshot's memory layout overrides heap, scratch, and transport + /// The snapshot's memory layout overrides scratch and transport /// settings. Supply every host function and module loader the snapshot uses. pub fn from_snapshot(snapshot: Snapshot) -> Self { Self::with_source(snapshot) diff --git a/src/hyperlight-js/tests/host_functions.rs b/src/hyperlight-js/tests/host_functions.rs index b2a71c0a..1b27fc7f 100644 --- a/src/hyperlight-js/tests/host_functions.rs +++ b/src/hyperlight-js/tests/host_functions.rs @@ -294,7 +294,7 @@ fn register_js_chunked_sidecar_round_trip() { let mut proto = SandboxBuilder::new() .with_input_transport_pool_pages(64) .with_output_transport_pool_pages(128) - .with_guest_scratch_size(2 * 1024 * 1024) + .with_guest_scratch_size(5 * 1024 * 1024) .build() .unwrap(); diff --git a/src/hyperlight-js/tests/runtime.rs b/src/hyperlight-js/tests/runtime.rs index 22550377..c2701ca5 100644 --- a/src/hyperlight-js/tests/runtime.rs +++ b/src/hyperlight-js/tests/runtime.rs @@ -15,8 +15,45 @@ limitations under the License. */ //! Test some key aspects of the JavaScript runtime +use hyperlight_common::flatbuffer_wrappers::guest_error::ErrorCode; +use hyperlight_host::HyperlightError; use hyperlight_js::{SandboxBuilder, Script}; +#[test] +fn guest_allocation_respects_scratch_limit() { + const MIB: usize = 1024 * 1024; + const ALLOCATION_BYTES: usize = 8 * MIB; + let handler = Script::from_content( + r#" + function handler(event) { + const bytes = new Uint8Array(event.bytes); + bytes.fill(7); + return bytes[bytes.length - 1]; + } + "#, + ); + + for (scratch_bytes, should_succeed) in [(5 * MIB, false), (32 * MIB, true)] { + let proto = SandboxBuilder::new() + .with_guest_scratch_size(scratch_bytes) + .build() + .unwrap(); + let mut sandbox = proto.load_runtime().unwrap(); + sandbox.add_handler("handler", handler.clone()).unwrap(); + let mut loaded = sandbox.get_loaded_sandbox().unwrap(); + let event = serde_json::json!({ "bytes": ALLOCATION_BYTES }).to_string(); + let result = loaded.handle_event("handler", event, None); + + if should_succeed { + assert_eq!(result.unwrap(), "7"); + } else { + assert!( + matches!(result, Err(HyperlightError::GuestAborted(code, _)) if code == ErrorCode::MallocFailed as u8) + ); + } + } +} + #[test] fn js_date_time_now_is_correct() { use std::time::{SystemTime, UNIX_EPOCH}; diff --git a/src/js-host-api/README.md b/src/js-host-api/README.md index e2532c04..2e0aa944 100644 --- a/src/js-host-api/README.md +++ b/src/js-host-api/README.md @@ -51,8 +51,7 @@ console.log(result); // { name: 'World', message: 'Hello, World!' } Creates and configures a new sandbox. **Methods:** -- `setHeapSize(bytes: number)` → `this` — Set guest heap size (must be > 0, chainable) -- `setScratchSize(bytes: number)` → `this` — Set guest scratch size, includes stack (must be > 0, chainable) +- `setScratchSize(bytes: number)` → `this` — Set the shared guest writable-memory budget (must be > 0, chainable; default and minimum: 5 MiB) - `setInputTransportPoolPages(pages: number)` → `this` — Set the host-to-guest buffer pool size in 4 KiB pages (must be > 0, chainable) - `setOutputTransportPoolPages(pages: number)` → `this` — Set the guest-to-host buffer pool size in 4 KiB pages (must be > 0, chainable) - `setInputTransportBufferSize(bytes: number)` → `this` — Set the capacity of each host-to-guest transport buffer (must be > 0, chainable) @@ -63,12 +62,11 @@ Creates and configures a new sandbox. - `build()` → `Promise` — Builds a proto sandbox ready to load the JavaScript runtime **Static methods:** -- `SandboxBuilder.fromSnapshot(snapshot)` → `SandboxBuilder` — Creates a builder whose `build()` returns the `JSSandbox` or `LoadedJSSandbox` the snapshot captured. The snapshot's memory layout overrides heap, scratch, and transport settings. +- `SandboxBuilder.fromSnapshot(snapshot)` → `SandboxBuilder` — Creates a builder whose `build()` returns the `JSSandbox` or `LoadedJSSandbox` the snapshot captured. The snapshot's memory layout overrides scratch and transport settings. ```javascript const builder = new SandboxBuilder() - .setHeapSize(8 * 1024 * 1024) - .setScratchSize(1024 * 1024) + .setScratchSize(8 * 1024 * 1024) .setInputTransportBufferSize(16 * 1024) .setOutputTransportBufferSize(16 * 1024) .setInputTransportPoolPages(8) diff --git a/src/js-host-api/examples/README.md b/src/js-host-api/examples/README.md index d526357c..fb23443f 100644 --- a/src/js-host-api/examples/README.md +++ b/src/js-host-api/examples/README.md @@ -150,12 +150,12 @@ node persistent-snapshot.js read ../../target/persistent-node-snapshot ### SandboxBuilder ```javascript const { SandboxBuilder } = require('../lib.js'); +const MiB = 1024 * 1024; async function main() { const builder = new SandboxBuilder(); - builder.setHeapSize(8 * 1024 * 1024); // Set heap size - builder.setScratchSize(1024 * 1024); // Set scratch size (includes stack) - const protoSandbox = await builder.build(); // Build sandbox + builder.setScratchSize(8 * MiB); + const protoSandbox = await builder.build(); } main(); ``` diff --git a/src/js-host-api/examples/calculator.js b/src/js-host-api/examples/calculator.js index e5490ff5..19d4c264 100644 --- a/src/js-host-api/examples/calculator.js +++ b/src/js-host-api/examples/calculator.js @@ -7,7 +7,7 @@ async function main() { // Create and configure sandbox const builder = new SandboxBuilder(); - builder.setHeapSize(16 * 1024 * 1024); // 16MB heap + builder.setScratchSize(16 * 1024 * 1024); const protoSandbox = await builder.build(); const jsSandbox = await protoSandbox.loadRuntime(); diff --git a/src/js-host-api/examples/host-functions.js b/src/js-host-api/examples/host-functions.js index eda8dddb..9ee6b690 100644 --- a/src/js-host-api/examples/host-functions.js +++ b/src/js-host-api/examples/host-functions.js @@ -41,8 +41,7 @@ async function main() { // Call hostFunctionModules() again for another builder. Each call creates // fresh module objects, callbacks, and captured state. const proto = await new SandboxBuilder() - .setHeapSize(8 * 1024 * 1024) - .setScratchSize(1024 * 1024) + .setScratchSize(8 * 1024 * 1024) .setHostFunctionModules(hostFunctionModules()) .build(); diff --git a/src/js-host-api/examples/mcp-server/README.md b/src/js-host-api/examples/mcp-server/README.md index d29e2067..0e7c1fa9 100644 --- a/src/js-host-api/examples/mcp-server/README.md +++ b/src/js-host-api/examples/mcp-server/README.md @@ -273,7 +273,7 @@ cd src\js-host-api\examples\mcp-server .\demo-copilot-cli.ps1 -Prompt "Solve 8-queens" -ShowCode -ShowCommand -Model gpt-4o -Mode Headless # Custom sandbox limits -.\demo-copilot-cli.ps1 -CpuTimeout 2000 -HeapSize 32 -Mode Headless +.\demo-copilot-cli.ps1 -CpuTimeout 2000 -ScratchSize 32 -Mode Headless # Install the MCP server permanently .\demo-copilot-cli.ps1 -Mode Install @@ -298,8 +298,7 @@ cd src\js-host-api\examples\mcp-server | `--show-command` | `-ShowCommand` | Display the copilot CLI command line being executed | | `--cpu-timeout `| `-CpuTimeout ` | CPU time limit per execution (default: 1000ms) | | `--wall-timeout `| `-WallTimeout `| Wall-clock backstop per execution (default: 5000ms) | -| `--heap-size ` | `-HeapSize ` | Guest heap size (default: 16MB) | -| `--scratch-size ` | `-ScratchSize ` | Guest scratch size (default: 1MB) | +| `--scratch-size ` | `-ScratchSize ` | Shared guest writable-memory budget (default: 16 MiB, minimum: 5 MiB) | **What the script does:** @@ -457,7 +456,7 @@ Output (when MCP server is not yet installed, with non-default sandbox limits): ⚠ The MCP server must be installed before this command will work. Install it now: - ./demo-copilot-cli.sh --install --cpu-timeout 5000 --heap-size 32 + ./demo-copilot-cli.sh --install --cpu-timeout 5000 --scratch-size 32 To remove it later: @@ -633,7 +632,7 @@ The Hyperlight sandbox provides **hardware-level isolation**: - 🌐 **No network access** — can't make HTTP requests - 🖥️ **No host access** — can't access environment variables, processes, or system calls - ⏱️ **CPU bounded** — configurable limit (default 1000ms), enforced by the hypervisor -- 💾 **Memory bounded** — configurable (default 16MB heap, 1MB scratch) +- 💾 **Guest writable memory bounded** — shared scratch budget (default 16 MiB) - 🔄 **Automatic recovery** — sandbox rebuilds after failures This makes it safe to execute untrusted, AI-generated code. @@ -644,17 +643,22 @@ This makes it safe to execute untrusted, AI-generated code. | ------------------------------ | -------- | ---------------------------------------------------------------------------------- | | `HYPERLIGHT_CPU_TIMEOUT_MS` | `1000` | Maximum CPU time per execution (milliseconds). The hypervisor hard-kills the guest when exceeded. | | `HYPERLIGHT_WALL_TIMEOUT_MS` | `5000` | Maximum wall-clock time per execution (milliseconds). Backstop for edge cases where CPU time alone doesn't catch the issue. | -| `HYPERLIGHT_HEAP_SIZE_MB` | `16` | Guest heap size in megabytes. Increase for memory-heavy computations (large arrays, BigInt work). | -| `HYPERLIGHT_SCRATCH_SIZE_MB` | `1` | Guest scratch size in megabytes. Increase for deeply recursive algorithms. | +| `HYPERLIGHT_SCRATCH_SIZE_MB` | `16` | Shared guest writable-memory budget in MiB (minimum 5). Includes heap, stack, transport pools, page tables, and copy-on-write pages. Increase for large arrays, BigInt work, or deep recursion. | | `HYPERLIGHT_TIMING_LOG` | — | Path to a file. When set, the server appends one JSON line per tool call with a timing breakdown (init, setup, compile, execute, total). Used by the demo script to show model vs. tool time. | | `HYPERLIGHT_CODE_LOG` | — | Path to a file. When set, the server writes the received JavaScript source code on each tool call. Used by the demo script's `--show-code` flag. | Example — tighten limits for a multi-tenant deployment: ```bash -HYPERLIGHT_CPU_TIMEOUT_MS=500 HYPERLIGHT_HEAP_SIZE_MB=8 node server.js +HYPERLIGHT_CPU_TIMEOUT_MS=500 HYPERLIGHT_SCRATCH_SIZE_MB=8 node server.js ``` +The scratch budget is not a JavaScript-only heap allowance or a total +host-process memory limit. Exhausting it aborts and poisons the guest; the server +rebuilds the sandbox for the next request. Separate heap-size options are no +longer supported. Reinstall existing MCP configurations with the updated launcher +to replace their old memory settings. + ## Troubleshooting ### "Cannot find module '../../lib.js'" diff --git a/src/js-host-api/examples/mcp-server/demo-copilot-cli.ps1 b/src/js-host-api/examples/mcp-server/demo-copilot-cli.ps1 index 0d3f0d1e..be0c0e4a 100644 --- a/src/js-host-api/examples/mcp-server/demo-copilot-cli.ps1 +++ b/src/js-host-api/examples/mcp-server/demo-copilot-cli.ps1 @@ -36,11 +36,8 @@ .PARAMETER WallTimeout Wall-clock backstop per execution in milliseconds (default: 5000). -.PARAMETER HeapSize - Guest heap size in megabytes (default: 16). - .PARAMETER ScratchSize - Guest scratch size in megabytes (default: 1). + Shared guest writable-memory budget in MiB (default: 16, minimum: 5). .EXAMPLE .\demo-copilot-cli.ps1 @@ -67,7 +64,7 @@ # Show the copilot CLI command being used .EXAMPLE - .\demo-copilot-cli.ps1 -CpuTimeout 2000 -HeapSize 32 -Mode Headless + .\demo-copilot-cli.ps1 -CpuTimeout 2000 -ScratchSize 32 -Mode Headless # Custom sandbox limits #> @@ -91,10 +88,7 @@ param( [int]$WallTimeout = $(if ($env:HYPERLIGHT_WALL_TIMEOUT_MS) { [int]$env:HYPERLIGHT_WALL_TIMEOUT_MS } else { 5000 }), [ValidateRange(1, [int]::MaxValue)] - [int]$HeapSize = $(if ($env:HYPERLIGHT_HEAP_SIZE_MB) { [int]$env:HYPERLIGHT_HEAP_SIZE_MB } else { 16 }), - - [ValidateRange(1, [int]::MaxValue)] - [int]$ScratchSize = $(if ($env:HYPERLIGHT_SCRATCH_SIZE_MB) { [int]$env:HYPERLIGHT_SCRATCH_SIZE_MB } else { 1 }) + [int]$ScratchSize = $(if ($env:HYPERLIGHT_SCRATCH_SIZE_MB) { [int]$env:HYPERLIGHT_SCRATCH_SIZE_MB } else { 16 }) ) # ── Strict Mode ────────────────────────────────────────────────────── @@ -250,7 +244,6 @@ function Build-McpJson { $env:HYPERLIGHT_CPU_TIMEOUT_MS = $CpuTimeout $env:HYPERLIGHT_WALL_TIMEOUT_MS = $WallTimeout - $env:HYPERLIGHT_HEAP_SIZE_MB = $HeapSize $env:HYPERLIGHT_SCRATCH_SIZE_MB = $ScratchSize $config = [ordered]@{ @@ -276,7 +269,6 @@ function Build-McpServerEntry { $envHash = [ordered]@{ HYPERLIGHT_CPU_TIMEOUT_MS = [string]$CpuTimeout HYPERLIGHT_WALL_TIMEOUT_MS = [string]$WallTimeout - HYPERLIGHT_HEAP_SIZE_MB = [string]$HeapSize HYPERLIGHT_SCRATCH_SIZE_MB = [string]$ScratchSize } @@ -474,8 +466,7 @@ INSTRUCTIONS: You have an MCP tool called 'execute_javascript' from the 'hyperli $installFlags = '' if ($CpuTimeout -ne 1000) { $installFlags += " -CpuTimeout $CpuTimeout" } if ($WallTimeout -ne 5000) { $installFlags += " -WallTimeout $WallTimeout" } - if ($HeapSize -ne 16) { $installFlags += " -HeapSize $HeapSize" } - if ($ScratchSize -ne 1) { $installFlags += " -ScratchSize $ScratchSize" } + if ($ScratchSize -ne 16) { $installFlags += " -ScratchSize $ScratchSize" } Write-Host '' Write-Host '🔧 Copy-pasteable command:' -ForegroundColor Cyan @@ -672,7 +663,7 @@ INSTRUCTIONS: You have an MCP tool called 'execute_javascript' from the 'hyperli Write-Banner Write-Info "Using model: $Model" -Write-Info "Sandbox limits: CPU ${CpuTimeout}ms, wall ${WallTimeout}ms, heap ${HeapSize}MB, scratch ${ScratchSize}MB" +Write-Info "Sandbox limits: CPU ${CpuTimeout}ms, wall ${WallTimeout}ms, requested scratch ${ScratchSize}MiB (minimum 5MiB)" switch ($Mode) { 'Install' { diff --git a/src/js-host-api/examples/mcp-server/demo-copilot-cli.sh b/src/js-host-api/examples/mcp-server/demo-copilot-cli.sh index eba351f7..1b1c9e56 100755 --- a/src/js-host-api/examples/mcp-server/demo-copilot-cli.sh +++ b/src/js-host-api/examples/mcp-server/demo-copilot-cli.sh @@ -17,7 +17,7 @@ # ./demo-copilot-cli.sh --install # Install MCP config permanently # ./demo-copilot-cli.sh --uninstall # Remove MCP config # ./demo-copilot-cli.sh --model gpt-4o # Use a specific model -# ./demo-copilot-cli.sh --cpu-timeout 2000 --heap-size 32 # Custom limits +# ./demo-copilot-cli.sh --cpu-timeout 2000 --scratch-size 32 # Custom limits # # Options: # --model LLM model to use (default: claude-opus-4.6) @@ -26,8 +26,7 @@ # --prompt Run a single custom prompt instead of built-in demos # --cpu-timeout CPU time limit per execution (default: 1000) # --wall-timeout Wall-clock backstop per execution (default: 5000) -# --heap-size Guest heap size in megabytes (default: 16) -# --scratch-size Guest scratch size in megabytes (default: 1) +# --scratch-size Shared guest writable-memory budget (default: 16, minimum: 5) # # ───────────────────────────────────────────────────────────────────── set -euo pipefail @@ -54,8 +53,7 @@ CUSTOM_PROMPT="" # CLI flags take precedence over env vars, env vars over defaults. CPU_TIMEOUT="${HYPERLIGHT_CPU_TIMEOUT_MS:-1000}" WALL_TIMEOUT="${HYPERLIGHT_WALL_TIMEOUT_MS:-5000}" -HEAP_SIZE="${HYPERLIGHT_HEAP_SIZE_MB:-16}" -SCRATCH_SIZE="${HYPERLIGHT_SCRATCH_SIZE_MB:-1}" +SCRATCH_SIZE="${HYPERLIGHT_SCRATCH_SIZE_MB:-16}" # ── Paths ──────────────────────────────────────────────────────────── SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" @@ -186,7 +184,6 @@ build_mcp_json() { export HYPERLIGHT_CPU_TIMEOUT_MS="${CPU_TIMEOUT}" export HYPERLIGHT_WALL_TIMEOUT_MS="${WALL_TIMEOUT}" - export HYPERLIGHT_HEAP_SIZE_MB="${HEAP_SIZE}" export HYPERLIGHT_SCRATCH_SIZE_MB="${SCRATCH_SIZE}" # Pass paths/names to node via the environment rather than string @@ -201,7 +198,6 @@ build_mcp_json() { const env = { HYPERLIGHT_CPU_TIMEOUT_MS: process.env.HYPERLIGHT_CPU_TIMEOUT_MS, HYPERLIGHT_WALL_TIMEOUT_MS: process.env.HYPERLIGHT_WALL_TIMEOUT_MS, - HYPERLIGHT_HEAP_SIZE_MB: process.env.HYPERLIGHT_HEAP_SIZE_MB, HYPERLIGHT_SCRATCH_SIZE_MB: process.env.HYPERLIGHT_SCRATCH_SIZE_MB, }; if (installMode) { @@ -252,7 +248,6 @@ install_mcp_config() { MCP_SERVER_NAME="${MCP_SERVER_NAME}" \ MCP_CPU_TIMEOUT="${CPU_TIMEOUT}" \ MCP_WALL_TIMEOUT="${WALL_TIMEOUT}" \ - MCP_HEAP_SIZE="${HEAP_SIZE}" \ MCP_SCRATCH_SIZE="${SCRATCH_SIZE}" \ node -e " const fs = require('fs'); @@ -265,7 +260,6 @@ install_mcp_config() { env: { HYPERLIGHT_CPU_TIMEOUT_MS: process.env.MCP_CPU_TIMEOUT, HYPERLIGHT_WALL_TIMEOUT_MS: process.env.MCP_WALL_TIMEOUT, - HYPERLIGHT_HEAP_SIZE_MB: process.env.MCP_HEAP_SIZE, HYPERLIGHT_SCRATCH_SIZE_MB: process.env.MCP_SCRATCH_SIZE, HYPERLIGHT_TIMING_LOG: '/tmp/hyperlight-timing.jsonl', }, @@ -475,8 +469,7 @@ Your task is complete the moment you present the result." local install_flags="" [[ "${CPU_TIMEOUT}" != "1000" ]] && install_flags+=" --cpu-timeout ${CPU_TIMEOUT}" [[ "${WALL_TIMEOUT}" != "5000" ]] && install_flags+=" --wall-timeout ${WALL_TIMEOUT}" - [[ "${HEAP_SIZE}" != "16" ]] && install_flags+=" --heap-size ${HEAP_SIZE}" - [[ "${SCRATCH_SIZE}" != "1" ]] && install_flags+=" --scratch-size ${SCRATCH_SIZE}" + [[ "${SCRATCH_SIZE}" != "16" ]] && install_flags+=" --scratch-size ${SCRATCH_SIZE}" echo "" echo -e "${CYAN}${BOLD}🔧 Copy-pasteable command:${RESET}" @@ -629,13 +622,8 @@ main() { WALL_TIMEOUT="$2" shift 2 ;; - --heap-size) - if [[ -z "${2:-}" ]]; then fail "--heap-size requires a value in MB"; fi - HEAP_SIZE="$2" - shift 2 - ;; --scratch-size) - if [[ -z "${2:-}" ]]; then fail "--scratch-size requires a value in MB"; fi + if [[ -z "${2:-}" ]]; then fail "--scratch-size requires a value in MiB"; fi SCRATCH_SIZE="$2" shift 2 ;; @@ -644,14 +632,14 @@ main() { shift ;; *) - fail "Unknown argument: $1\nUsage: $0 [--headless] [--install] [--uninstall] [--model ] [--prompt ] [--show-code] [--show-command] [--cpu-timeout ] [--wall-timeout ] [--heap-size ] [--scratch-size ]" + fail "Unknown argument: $1\nUsage: $0 [--headless] [--install] [--uninstall] [--model ] [--prompt ] [--show-code] [--show-command] [--cpu-timeout ] [--wall-timeout ] [--scratch-size ]" ;; esac done banner info "Using model: ${BOLD}${MODEL}${RESET}" - info "Sandbox limits: CPU ${BOLD}${CPU_TIMEOUT}ms${RESET}, wall ${BOLD}${WALL_TIMEOUT}ms${RESET}, heap ${BOLD}${HEAP_SIZE}MB${RESET}, scratch ${BOLD}${SCRATCH_SIZE}MB${RESET}" + info "Sandbox limits: CPU ${BOLD}${CPU_TIMEOUT}ms${RESET}, wall ${BOLD}${WALL_TIMEOUT}ms${RESET}, requested scratch ${BOLD}${SCRATCH_SIZE}MiB${RESET} (minimum 5MiB)" case "${mode}" in --install) diff --git a/src/js-host-api/examples/mcp-server/server.js b/src/js-host-api/examples/mcp-server/server.js index daa2a1fe..102f82c1 100644 --- a/src/js-host-api/examples/mcp-server/server.js +++ b/src/js-host-api/examples/mcp-server/server.js @@ -41,11 +41,11 @@ const DEFAULT_CPU_TIMEOUT_MS = 1000; /** Default maximum wall-clock time per execution (milliseconds). */ const DEFAULT_WALL_CLOCK_TIMEOUT_MS = 5000; -/** Default guest heap size in megabytes. */ -const DEFAULT_HEAP_SIZE_MB = 16; +/** Default shared guest writable-memory budget in MiB. */ +const DEFAULT_SCRATCH_SIZE_MB = 16; -/** Default guest scratch size in megabytes. */ -const DEFAULT_SCRATCH_SIZE_MB = 1; +/** Matches the JS runtime builder's minimum scratch budget. */ +const MIN_SCRATCH_SIZE_MB = 5; // ── Configuration ──────────────────────────────────────────────────── // @@ -89,27 +89,25 @@ const WALL_CLOCK_TIMEOUT_MS = parsePositiveInt( DEFAULT_WALL_CLOCK_TIMEOUT_MS ); -/** Maximum byte size for heap/scratch to avoid exceeding u32 range at the N-API boundary. */ +/** Maximum scratch byte size at the u32 N-API boundary. */ const MAX_BYTES = 0xffffffff; -/** Maximum heap/scratch size that still fits within MAX_BYTES. +/** Maximum scratch size that still fits within MAX_BYTES. * Clamping at the MiB level (rather than clamping the byte total) keeps the * configured size aligned to a whole MiB, so the value we set and print stays * consistent (e.g. 5000MB clamps to 4095MB, not 4095.999…MB). */ const MAX_MIB = Math.floor(MAX_BYTES / (1024 * 1024)); -/** Guest heap size in bytes. Override with HYPERLIGHT_HEAP_SIZE_MB (megabytes). */ -const HEAP_SIZE_BYTES = - Math.min(parsePositiveInt(process.env.HYPERLIGHT_HEAP_SIZE_MB, DEFAULT_HEAP_SIZE_MB), MAX_MIB) * - 1024 * - 1024; - -/** Guest scratch size in bytes. Override with HYPERLIGHT_SCRATCH_SIZE_MB (megabytes). - * Maps to setScratchSize() on the SandboxBuilder API. */ +/** Shared heap, stack, transport, page-table, and copy-on-write budget. + * Override with HYPERLIGHT_SCRATCH_SIZE_MB (MiB). Match the builder's clamp + * so advertised limits describe the effective configuration. */ const SCRATCH_SIZE_BYTES = - Math.min( - parsePositiveInt(process.env.HYPERLIGHT_SCRATCH_SIZE_MB, DEFAULT_SCRATCH_SIZE_MB), - MAX_MIB + Math.max( + MIN_SCRATCH_SIZE_MB, + Math.min( + parsePositiveInt(process.env.HYPERLIGHT_SCRATCH_SIZE_MB, DEFAULT_SCRATCH_SIZE_MB), + MAX_MIB + ) ) * 1024 * 1024; @@ -164,7 +162,6 @@ let executionLock = Promise.resolve(); */ async function initializeSandbox() { const builder = new SandboxBuilder(); - builder.setHeapSize(HEAP_SIZE_BYTES); builder.setScratchSize(SCRATCH_SIZE_BYTES); const proto = await builder.build(); @@ -322,7 +319,7 @@ mcpServer.registerTool( 'The code runs as the body of a function — use `return` to produce', `a JSON-serializable result. CPU time is hard-limited to ${CPU_TIMEOUT_MS}ms`, `with a ${WALL_CLOCK_TIMEOUT_MS}ms wall-clock backstop.`, - `Memory: ${HEAP_SIZE_BYTES / (1024 * 1024)}MB heap, ${SCRATCH_SIZE_BYTES / (1024 * 1024)}MB scratch.`, + `Memory: ${SCRATCH_SIZE_BYTES / (1024 * 1024)}MiB scratch shared by heap, stack, and runtime overhead.`, '', 'The sandbox has NO access to:', ' - Filesystem, network, or host environment', @@ -427,8 +424,7 @@ await mcpServer.connect(transport); console.error('🔒 Hyperlight JS MCP Server running on stdio'); console.error(` CPU timeout: ${CPU_TIMEOUT_MS}ms`); console.error(` Wall-clock timeout: ${WALL_CLOCK_TIMEOUT_MS}ms`); -console.error(` Heap size: ${HEAP_SIZE_BYTES / (1024 * 1024)}MB`); -console.error(` Scratch size: ${SCRATCH_SIZE_BYTES / (1024 * 1024)}MB`); +console.error(` Scratch budget: ${SCRATCH_SIZE_BYTES / (1024 * 1024)}MiB`); // ── Graceful Shutdown ─────────────────────────────────────────────── diff --git a/src/js-host-api/examples/mcp-server/tests/config.test.js b/src/js-host-api/examples/mcp-server/tests/config.test.js index 85394e5b..d3033e32 100644 --- a/src/js-host-api/examples/mcp-server/tests/config.test.js +++ b/src/js-host-api/examples/mcp-server/tests/config.test.js @@ -1,7 +1,7 @@ // ── Hyperlight JS MCP Server — Configuration Tests ────────────────── // -// Validates that sandbox limits (CPU timeout, wall-clock timeout, heap -// size, scratch size) are configurable via environment variables, that +// Validates that sandbox limits (CPU timeout, wall-clock timeout, scratch +// budget) are configurable via environment variables, that // invalid values fall back to defaults gracefully, and that the tool // description dynamically reflects the configured limits. // @@ -59,7 +59,6 @@ describe('Tool description reflects configured limits', () => { ctx = await spawnServer({ HYPERLIGHT_CPU_TIMEOUT_MS: '2000', HYPERLIGHT_WALL_TIMEOUT_MS: '8000', - HYPERLIGHT_HEAP_SIZE_MB: '32', HYPERLIGHT_SCRATCH_SIZE_MB: '2', }); }); @@ -81,16 +80,10 @@ describe('Tool description reflects configured limits', () => { expect(jsTool.description).toContain('8000ms'); }); - it('should include custom heap size in tool description', async () => { - const response = await ctx.listTools(); - const jsTool = response.result.tools.find((t) => t.name === 'execute_javascript'); - expect(jsTool.description).toContain('32MB'); - }); - it('should include custom scratch size in tool description', async () => { const response = await ctx.listTools(); const jsTool = response.result.tools.find((t) => t.name === 'execute_javascript'); - expect(jsTool.description).toContain('2MB'); + expect(jsTool.description).toContain('5MiB scratch'); }); }); @@ -104,7 +97,6 @@ describe('Invalid env vars fall back to defaults', () => { ctx = await spawnServer({ HYPERLIGHT_CPU_TIMEOUT_MS: 'banana', HYPERLIGHT_WALL_TIMEOUT_MS: '-999', - HYPERLIGHT_HEAP_SIZE_MB: '0', HYPERLIGHT_SCRATCH_SIZE_MB: '3.14', }); }); @@ -147,15 +139,49 @@ describe('Invalid env vars fall back to defaults', () => { // Default values should appear since the invalid ones were rejected expect(jsTool.description).toContain('1000ms'); expect(jsTool.description).toContain('5000ms'); - expect(jsTool.description).toContain('16MB'); - expect(jsTool.description).toContain('1MB'); + expect(jsTool.description).toContain('16MiB scratch'); }); it('should log warnings to stderr about invalid values', () => { const stderr = ctx.stderrChunks.join(''); expect(stderr).toContain('ignoring invalid value "banana"'); expect(stderr).toContain('ignoring invalid value "-999"'); - expect(stderr).toContain('ignoring invalid value "0"'); expect(stderr).toContain('ignoring invalid value "3.14"'); }); }); + +describe('Scratch budget controls guest allocations', () => { + let constrained; + let larger; + const allocation = + 'const bytes = new Uint8Array(8 * 1024 * 1024); bytes.fill(7); return bytes[bytes.length - 1];'; + + beforeAll(async () => { + constrained = await spawnServer({ HYPERLIGHT_SCRATCH_SIZE_MB: '1' }); + larger = await spawnServer({ HYPERLIGHT_SCRATCH_SIZE_MB: '32' }); + }); + + afterAll(() => { + constrained?.server.kill(); + larger?.server.kill(); + }); + + it('advertises the effective minimum rather than the smaller request', async () => { + const response = await constrained.listTools(); + const tool = response.result.tools.find((entry) => entry.name === 'execute_javascript'); + expect(tool.description).toContain('5MiB scratch'); + }); + + it('fails when scratch is exhausted and rebuilds for the next request', async () => { + const failed = await constrained.callTool(allocation); + expect(failed.result.isError).toBe(true); + const recovered = await constrained.callTool('return { ok: true };'); + expect(JSON.parse(recovered.result.content[0].text)).toEqual({ ok: true }); + }); + + it('runs the same allocation with a larger scratch budget', async () => { + const response = await larger.callTool(allocation); + expect(response.result.isError).not.toBe(true); + expect(JSON.parse(response.result.content[0].text)).toBe(7); + }); +}); diff --git a/src/js-host-api/examples/mcp-server/tests/prompt-examples.test.js b/src/js-host-api/examples/mcp-server/tests/prompt-examples.test.js index b54d1e73..7622c54f 100644 --- a/src/js-host-api/examples/mcp-server/tests/prompt-examples.test.js +++ b/src/js-host-api/examples/mcp-server/tests/prompt-examples.test.js @@ -662,10 +662,9 @@ describe('README Example Prompts', () => { // A few prompts exercise deliberately memory-heavy, AI-style // implementations (recursive quicksort that .filter()s at every // level over 5,000 elements, a 1,000-node trie, a recursive- - // backtracking maze) that exhaust the 1MB default guest scratch - // (physical) memory. Give the sandbox extra scratch headroom so - // the suite stays faithful to the README prompts instead of - // trimming the workloads. + // backtracking maze). Use the demo's 16 MiB shared scratch + // budget so the suite stays faithful to the README prompts + // instead of trimming the workloads. HYPERLIGHT_SCRATCH_SIZE_MB: '16', }, }); diff --git a/src/js-host-api/examples/simple.js b/src/js-host-api/examples/simple.js index 4ee80f6b..a8e6d81b 100644 --- a/src/js-host-api/examples/simple.js +++ b/src/js-host-api/examples/simple.js @@ -8,8 +8,7 @@ async function main() { // Step 1: Create and configure the sandbox builder console.log('1. Creating sandbox builder...'); const builder = new SandboxBuilder(); - builder.setHeapSize(8 * 1024 * 1024); // 8MB heap - builder.setScratchSize(1024 * 1024); // 1MB scratch (includes stack) + builder.setScratchSize(8 * 1024 * 1024); console.log(' ✓ Builder configured\n'); // Step 2: Build the proto sandbox (async — returns a Promise) diff --git a/src/js-host-api/examples/unload.js b/src/js-host-api/examples/unload.js index f1c30b5f..bb3a535f 100644 --- a/src/js-host-api/examples/unload.js +++ b/src/js-host-api/examples/unload.js @@ -7,7 +7,7 @@ async function main() { // Create sandbox const builder = new SandboxBuilder(); - builder.setHeapSize(8 * 1024 * 1024); + builder.setScratchSize(8 * 1024 * 1024); const protoSandbox = await builder.build(); let jsSandbox = await protoSandbox.loadRuntime(); diff --git a/src/js-host-api/examples/user-modules.js b/src/js-host-api/examples/user-modules.js index 7fe5e694..ff62a880 100644 --- a/src/js-host-api/examples/user-modules.js +++ b/src/js-host-api/examples/user-modules.js @@ -15,10 +15,7 @@ async function main() { // ── Build sandbox ──────────────────────────────────────────────── console.log('1. Creating sandbox...'); - const proto = await new SandboxBuilder() - .setHeapSize(8 * 1024 * 1024) - .setScratchSize(1024 * 1024) - .build(); + const proto = await new SandboxBuilder().setScratchSize(8 * 1024 * 1024).build(); const sandbox = await proto.loadRuntime(); console.log(' ✓ Sandbox created\n'); @@ -120,10 +117,7 @@ async function main() { // We need a fresh sandbox for the shared-state demo because // getLoadedSandbox() consumes the JSSandbox. console.log('5. Cross-handler shared mutable state...\n'); - const proto2 = await new SandboxBuilder() - .setHeapSize(8 * 1024 * 1024) - .setScratchSize(1024 * 1024) - .build(); + const proto2 = await new SandboxBuilder().setScratchSize(8 * 1024 * 1024).build(); const sandbox2 = await proto2.loadRuntime(); // ── Shared mutable state module ────────────────────────────────── diff --git a/src/js-host-api/lib.js b/src/js-host-api/lib.js index 68da226b..6d99fd4e 100644 --- a/src/js-host-api/lib.js +++ b/src/js-host-api/lib.js @@ -287,7 +287,6 @@ native.Snapshot = withWrappedStatics(Snapshot, { }); for (const method of [ - 'setHeapSize', 'setScratchSize', 'setInputTransportPoolPages', 'setOutputTransportPoolPages', diff --git a/src/js-host-api/src/lib.rs b/src/js-host-api/src/lib.rs index 94203f37..2681f13b 100644 --- a/src/js-host-api/src/lib.rs +++ b/src/js-host-api/src/lib.rs @@ -469,12 +469,11 @@ macro_rules! map_builder { /// /// Use the builder to set memory limits before constructing the sandbox. /// All size setters support method chaining. A snapshot's memory layout -/// overrides heap, scratch, and transport settings. +/// overrides scratch and transport settings. /// /// ```js /// const proto = await new SandboxBuilder() -/// .setHeapSize(8 * 1024 * 1024) -/// .setScratchSize(1024 * 1024) +/// .setScratchSize(8 * 1024 * 1024) /// .build(); /// /// const snapshot = await Snapshot.checkedLoad('./snapshots', 'app'); @@ -598,9 +597,11 @@ impl SandboxBuilderWrapper { /// Set the guest scratch size in bytes. /// - /// Controls how much scratch space (which includes the stack) is available - /// for guest code execution. Deep recursion or large local variables need - /// a bigger scratch region. + /// Shared writable-memory budget for the guest heap, stack, transport pools, + /// page tables, and copy-on-write pages, not a JavaScript heap allowance. + /// Defaults to 5 MiB; smaller positive values are raised to this minimum. + /// Exhaustion aborts and poisons the guest (`ERR_GUEST_ABORT`); restore a + /// snapshot before reusing it. /// /// @param size - Scratch size in bytes (must be > 0) /// @returns this (for chaining) @@ -613,23 +614,6 @@ impl SandboxBuilderWrapper { self.with_inner(|state| map_builder!(state, b => b.with_guest_scratch_size(size as usize))) } - /// Set the guest heap size in bytes. - /// - /// Controls how much heap memory the guest JavaScript engine can - /// allocate. If handlers create many objects or large strings, increase - /// this. Too small will cause `malloc failed` errors in the guest. - /// - /// @param size - Heap size in bytes (must be > 0) - /// @returns this (for chaining) - /// @throws If size is 0 - #[napi] - pub fn set_heap_size(&self, size: u32) -> napi::Result<&Self> { - if size == 0 { - return Err(invalid_arg_error("Heap size must be greater than 0")); - } - self.with_inner(|state| map_builder!(state, b => b.with_guest_heap_size(size as u64))) - } - /// Supply host-function modules for this sandbox. /// /// Each module is consumed and cannot be supplied to another builder. diff --git a/src/js-host-api/test-examples.sh b/src/js-host-api/test-examples.sh index a6b979aa..2efa040e 100755 --- a/src/js-host-api/test-examples.sh +++ b/src/js-host-api/test-examples.sh @@ -3,48 +3,4 @@ set -e -echo "Testing Hyperlight JS Host API Examples" -echo "========================================" -echo "" - -# Check if we're in the right directory -if [ ! -f "package.json" ]; then - echo "Error: Please run this script from the js-host-api directory" - exit 1 -fi - -# Check if the native module is built -if [ ! -f "index.js" ]; then - echo "Native module not found. Building..." - npm install - npm run build - echo "" -fi - -echo "Running simple.js example..." -echo "----------------------------" -node examples/simple.js -echo "" - -echo "Running calculator.js example..." -echo "---------------------------------" -node examples/calculator.js -echo "" - -echo "Running unload.js example..." -echo "----------------------------" -node examples/unload.js -echo "" - -echo "Running interrupt.js example..." -echo "--------------------------------" -node examples/interrupt.js -echo "" - -echo "Running cpu-timeout.js example..." -echo "----------------------------------" -node examples/cpu-timeout.js -echo "" - -echo "========================================" -echo "All examples completed successfully!" +exec just --justfile "$(dirname "${BASH_SOURCE[0]}")/../../Justfile" run-js-host-api-examples "$@" diff --git a/src/js-host-api/tests/persistent-snapshots.test.js b/src/js-host-api/tests/persistent-snapshots.test.js index fb87cf4c..b1643542 100644 --- a/src/js-host-api/tests/persistent-snapshots.test.js +++ b/src/js-host-api/tests/persistent-snapshots.test.js @@ -110,7 +110,7 @@ describe('Persistent snapshots', () => { ); available.register('stillAvailable', () => true); - expect(builder.setHeapSize(8 * 1024 * 1024)).toBe(builder); + expect(builder.setScratchSize(8 * 1024 * 1024)).toBe(builder); }); it('rejects duplicate module objects without consuming them', () => { diff --git a/src/js-host-api/tests/sandbox.test.js b/src/js-host-api/tests/sandbox.test.js index e059eba7..78207594 100644 --- a/src/js-host-api/tests/sandbox.test.js +++ b/src/js-host-api/tests/sandbox.test.js @@ -13,7 +13,7 @@ describe('SandboxBuilder', () => { it('should support method chaining on setters', () => { const builder = new SandboxBuilder(); - const returned = builder.setHeapSize(8 * 1024 * 1024); + const returned = builder.setScratchSize(8 * 1024 * 1024); // Builder setters should return `this` for chaining expect(returned).toBe(builder); }); @@ -21,8 +21,7 @@ describe('SandboxBuilder', () => { it('should chain multiple setters', () => { const builder = new SandboxBuilder(); const result = builder - .setHeapSize(8 * 1024 * 1024) - .setScratchSize(1024 * 1024) + .setScratchSize(8 * 1024 * 1024) .setInputBufferSize(4096) .setOutputBufferSize(4096) .setInputTransportPoolPages(4) @@ -47,7 +46,7 @@ describe('SandboxBuilder', () => { it('should throw CONSUMED on setters after build()', async () => { const builder = new SandboxBuilder(); await builder.build(); - expectThrowsWithCode(() => builder.setHeapSize(1024), 'ERR_CONSUMED'); + expectThrowsWithCode(() => builder.setScratchSize(1024), 'ERR_CONSUMED'); expectThrowsWithCode(() => builder.setInputTransportPoolPages(4), 'ERR_CONSUMED'); expectThrowsWithCode(() => builder.setOutputTransportPoolPages(4), 'ERR_CONSUMED'); expectThrowsWithCode(() => builder.setInputTransportBufferSize(4096), 'ERR_CONSUMED'); @@ -56,11 +55,6 @@ describe('SandboxBuilder', () => { // ── Validation ─────────────────────────────────────────────────── - it('should reject zero heap size', () => { - const builder = new SandboxBuilder(); - expectThrowsWithCode(() => builder.setHeapSize(0), 'ERR_INVALID_ARG'); - }); - it('should reject zero scratch size', () => { const builder = new SandboxBuilder(); expectThrowsWithCode(() => builder.setScratchSize(0), 'ERR_INVALID_ARG'); @@ -252,6 +246,42 @@ describe('LoadedJSSandbox', () => { }); }); +describe('Scratch allocation budget', () => { + it.each([5, 32])('enforces a %i MiB scratch budget and restores safely', async (scratchMiB) => { + const mib = 1024 * 1024; + const proto = await new SandboxBuilder().setScratchSize(scratchMiB * mib).build(); + const sandbox = await trackSandbox(proto.loadRuntime()); + sandbox.addHandler( + 'handler', + ` + function handler(event) { + const bytes = new Uint8Array(event.bytes); + bytes.fill(7); + return bytes[bytes.length - 1]; + } + ` + ); + const loaded = await trackSandbox(sandbox.getLoadedSandbox()); + const snapshot = await loaded.snapshot(); + if (scratchMiB === 5) { + await expectRejectsWithCode( + loaded.callHandler('handler', { bytes: 8 * mib }), + 'ERR_GUEST_ABORT' + ); + expect(loaded.poisoned).toBe(true); + await expectRejectsWithCode( + loaded.callHandler('handler', { bytes: 1 }), + 'ERR_POISONED' + ); + } else { + expect(await loaded.callHandler('handler', { bytes: 8 * mib })).toBe(7); + } + await loaded.restore(snapshot); + expect(loaded.poisoned).toBe(false); + expect(await loaded.callHandler('handler', { bytes: 1 })).toBe(7); + }); +}); + // ── Calculator (functional test) ───────────────────────────────────── describe('Calculator example', () => { From 3abdabffd78e7d7d7aa0dc22d950a6b92254565c Mon Sep 17 00:00:00 2001 From: Simon Davies Date: Thu, 12 Nov 2026 15:12:51 +0000 Subject: [PATCH 3/4] review feedback Signed-off-by: Simon Davies --- src/hyperlight-js/tests/host_functions.rs | 1 - src/hyperlight-js/tests/runtime.rs | 34 +++++++++++------------ 2 files changed, 16 insertions(+), 19 deletions(-) diff --git a/src/hyperlight-js/tests/host_functions.rs b/src/hyperlight-js/tests/host_functions.rs index 1b27fc7f..ab05fb24 100644 --- a/src/hyperlight-js/tests/host_functions.rs +++ b/src/hyperlight-js/tests/host_functions.rs @@ -294,7 +294,6 @@ fn register_js_chunked_sidecar_round_trip() { let mut proto = SandboxBuilder::new() .with_input_transport_pool_pages(64) .with_output_transport_pool_pages(128) - .with_guest_scratch_size(5 * 1024 * 1024) .build() .unwrap(); diff --git a/src/hyperlight-js/tests/runtime.rs b/src/hyperlight-js/tests/runtime.rs index c2701ca5..e2918384 100644 --- a/src/hyperlight-js/tests/runtime.rs +++ b/src/hyperlight-js/tests/runtime.rs @@ -23,35 +23,33 @@ use hyperlight_js::{SandboxBuilder, Script}; fn guest_allocation_respects_scratch_limit() { const MIB: usize = 1024 * 1024; const ALLOCATION_BYTES: usize = 8 * MIB; - let handler = Script::from_content( - r#" + + fn invoke(scratch_bytes: usize) -> hyperlight_js::Result { + let handler = Script::from_content( + r#" function handler(event) { const bytes = new Uint8Array(event.bytes); bytes.fill(7); return bytes[bytes.length - 1]; } "#, - ); + ); - for (scratch_bytes, should_succeed) in [(5 * MIB, false), (32 * MIB, true)] { let proto = SandboxBuilder::new() .with_guest_scratch_size(scratch_bytes) - .build() - .unwrap(); - let mut sandbox = proto.load_runtime().unwrap(); - sandbox.add_handler("handler", handler.clone()).unwrap(); - let mut loaded = sandbox.get_loaded_sandbox().unwrap(); + .build()?; + let mut sandbox = proto.load_runtime()?; + sandbox.add_handler("handler", handler)?; + let mut loaded = sandbox.get_loaded_sandbox()?; let event = serde_json::json!({ "bytes": ALLOCATION_BYTES }).to_string(); - let result = loaded.handle_event("handler", event, None); - - if should_succeed { - assert_eq!(result.unwrap(), "7"); - } else { - assert!( - matches!(result, Err(HyperlightError::GuestAborted(code, _)) if code == ErrorCode::MallocFailed as u8) - ); - } + loaded.handle_event("handler", event, None) } + + std::assert_matches!( + invoke(5 * MIB), + Err(HyperlightError::GuestAborted(code, _)) if code == ErrorCode::MallocFailed as u8 + ); + assert_eq!(invoke(32 * MIB).unwrap(), "7"); } #[test] From 9de0c5c36127d5d83e7a6358c4bec0cd48e3b5f4 Mon Sep 17 00:00:00 2001 From: Simon Davies Date: Thu, 12 Nov 2026 20:14:47 +0000 Subject: [PATCH 4/4] fix merge conflict Signed-off-by: Simon Davies --- .../fixtures/extended_runtime/Cargo.lock | 220 ++++++++---------- 1 file changed, 99 insertions(+), 121 deletions(-) diff --git a/src/hyperlight-js-runtime/tests/fixtures/extended_runtime/Cargo.lock b/src/hyperlight-js-runtime/tests/fixtures/extended_runtime/Cargo.lock index 190fed92..5cebde6c 100644 --- a/src/hyperlight-js-runtime/tests/fixtures/extended_runtime/Cargo.lock +++ b/src/hyperlight-js-runtime/tests/fixtures/extended_runtime/Cargo.lock @@ -4,9 +4,9 @@ version = 4 [[package]] name = "aho-corasick" -version = "1.1.4" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +checksum = "c982642fa9e8606056828ee9a8505737230110bb1099153c79efe865c59d12ba" dependencies = [ "memchr", ] @@ -81,9 +81,9 @@ checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470" [[package]] name = "async-lock" -version = "3.4.2" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311" +checksum = "0876700b0d007ad25d5508865a4259c3af9af6d296bf663a0d586d0bc3369f6f" dependencies = [ "event-listener", "event-listener-strategy", @@ -113,7 +113,7 @@ dependencies = [ "regex", "rustc-hash", "shlex 1.3.0", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -132,14 +132,14 @@ dependencies = [ "regex", "rustc-hash", "shlex 2.0.1", - "syn 3.0.4", + "syn 3.0.10", ] [[package]] name = "bitflags" -version = "2.13.0" +version = "2.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" +checksum = "3ded4057c258ba199e2d26386d3af3780957ecaee6c4ef4041c6b4b8b97c0b06" [[package]] name = "block-buffer" @@ -152,35 +152,35 @@ dependencies = [ [[package]] name = "bytemuck" -version = "1.25.0" +version = "1.25.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" +checksum = "95832e849adfb21180ccb6826a99da14e5d266ae5c2e668e1602cf234f153797" dependencies = [ "bytemuck_derive", ] [[package]] name = "bytemuck_derive" -version = "1.10.2" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff" +checksum = "6a1f896587b6f2c069c73d2f0913e2d590c3990285cd2f0b6aa02b786b4c679c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 3.0.10", ] [[package]] name = "bytes" -version = "1.12.0" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae3f5d315924270530207e2a68396c3cc547f6dca3fbdca317cfb1a51edb593" +checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04" [[package]] name = "cc" -version = "1.2.63" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "556e016178bb5662a08681bbe0f00f8e17631781a4dfc8c45e466e4b185ec27f" +checksum = "4ff2a8126fff0f8c839080d619675a585e61720bb12d2baceae45769fd66f155" dependencies = [ "find-msvc-tools", "shlex 2.0.1", @@ -197,15 +197,15 @@ dependencies = [ [[package]] name = "cfg-if" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +checksum = "4e7648175b45a9a48536d676f68d918270699102aa8dab5496df06904c914600" [[package]] name = "clang-sys" -version = "1.8.1" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +checksum = "157a8ba7b480713b56f4c09fd13fc3e0a22a5dfab8097ba61cbc5feef950788a" dependencies = [ "glob", "libc", @@ -243,14 +243,14 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 3.0.4", + "syn 3.0.10", ] [[package]] name = "clap_lex" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" +checksum = "1c133bc6a41be0d194c306b5506d15e6feeea7b1d6604bd3f8310dfb2ca96486" [[package]] name = "colorchoice" @@ -258,15 +258,6 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" -[[package]] -name = "concurrent-queue" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" -dependencies = [ - "crossbeam-utils", -] - [[package]] name = "convert_case" version = "0.12.0" @@ -285,12 +276,6 @@ dependencies = [ "libc", ] -[[package]] -name = "crossbeam-utils" -version = "0.8.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" - [[package]] name = "crypto-common" version = "0.1.7" @@ -314,23 +299,22 @@ dependencies = [ [[package]] name = "either" -version = "1.16.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" +checksum = "0e9c71c2167ca323c882b99918929403426e2373ea17242ff5653e0d5e1058be" [[package]] name = "equivalent" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" +checksum = "00d174d5400e5e8fd687ad1049e2f578285fa914201b1af7e8b112a4546bd826" [[package]] name = "event-listener" -version = "5.4.1" +version = "5.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" +checksum = "5a23add41df1562121a9393cb065eab5146a1242410f23a644851e90cfd669d2" dependencies = [ - "concurrent-queue", "pin-project-lite", ] @@ -356,15 +340,15 @@ dependencies = [ [[package]] name = "find-msvc-tools" -version = "0.1.9" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" +checksum = "1c66915cc5391097de7a2b0b972e8505aa33c0df401fd3a6811dbdb32881ba48" [[package]] name = "fixedbitset" -version = "0.5.7" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" +checksum = "5905ec755cc8386d7928353e4d356d2be3e89e81e5ca01fe630a1ca4d08dc175" [[package]] name = "flatbuffers" @@ -400,9 +384,9 @@ dependencies = [ [[package]] name = "glob" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" +checksum = "e4eba85ea1d0a966a983acd07deee566e67395d2d96b6fb39e62b5a833f1eb0b" [[package]] name = "hashbrown" @@ -452,7 +436,7 @@ dependencies = [ "flatbuffers", "log", "smallvec", - "spin 0.12.3", + "spin", "thiserror", "tracing-core", ] @@ -485,7 +469,7 @@ dependencies = [ "hyperlight-libc", "linkme", "log", - "spin 0.12.3", + "spin", "talc", "tracing", ] @@ -499,7 +483,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 3.0.4", + "syn 3.0.10", ] [[package]] @@ -509,7 +493,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "386e053d56679610902ef68feb1966e043c67c55006954a6afdca89567a2ce21" dependencies = [ "hyperlight-common", - "spin 0.12.3", + "spin", "tracing", "tracing-core", ] @@ -541,7 +525,7 @@ dependencies = [ "serde", "serde_json", "sha2", - "spin 0.12.3", + "spin", "tracing", ] @@ -559,9 +543,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.14.0" +version = "2.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +checksum = "cc4e190f5d26ca7051642629da2c52fc03bde85a03197c99408dcd291734c855" dependencies = [ "equivalent", "hashbrown", @@ -593,15 +577,15 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.18" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" +checksum = "3bd1574e617a9a6c1f8f8181bd7e841e1f20e4277607ef28945765188d6284d4" [[package]] name = "libc" -version = "0.2.186" +version = "0.2.191" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" +checksum = "65cd87c9a6d60536580b316ad985726104730a5151c99845f2490428ef91240a" [[package]] name = "libloading" @@ -615,22 +599,22 @@ dependencies = [ [[package]] name = "linkme" -version = "0.3.36" +version = "0.3.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e83272d46373fb8decca684579ac3e7c8f3d71d4cc3aa693df8759e260ae41cf" +checksum = "10a4e12fd3f6b22d5bc28beccac0f53bb35db4a897c6ddb709c4a0161f72eb6e" dependencies = [ "linkme-impl", ] [[package]] name = "linkme-impl" -version = "0.3.36" +version = "0.3.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32d59e20403c7d08fe62b4376edfe5c7fb2ef1e6b1465379686d0f21c8df444b" +checksum = "22cd04c256b1cd686a45898c8fd150c79b801d96a3eb7bae1075f9eb3b4b85c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 3.0.10", ] [[package]] @@ -644,15 +628,15 @@ dependencies = [ [[package]] name = "log" -version = "0.4.32" +version = "0.4.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "953f07c43838f8e6f9758cab68bf5bed85465e7587ebe0b823f1bcd81978ad3a" +checksum = "5d15f86d12da44cd8d4ec3c73984537eb7f4cadcb9776a575a0a8a05ca11005c" [[package]] name = "memchr" -version = "2.8.1" +version = "2.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8" +checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" [[package]] name = "minimal-lexical" @@ -696,7 +680,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -706,7 +690,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2bfe0f4c752e450fc2faf62654f1c134747922825d5b04ca717b8874f41a40c0" dependencies = [ "proc-macro2", - "syn 3.0.4", + "syn 3.0.10", ] [[package]] @@ -720,27 +704,27 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.106" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.45" +version = "1.0.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" dependencies = [ "proc-macro2", ] [[package]] name = "regex" -version = "1.12.3" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d" dependencies = [ "aho-corasick", "memchr", @@ -750,9 +734,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.14" +version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2" dependencies = [ "aho-corasick", "memchr", @@ -761,17 +745,17 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.10" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" +checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" [[package]] name = "relative-path" -version = "2.0.1" +version = "2.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bca40a312222d8ba74837cb474edef44b37f561da5f773981007a10bbaa992b0" +checksum = "38540a682c6e9bca04a8bca9e5ec5a6ba168dfbb04c9998fe517a9befb92eb68" dependencies = [ - "serde", + "serde_core", ] [[package]] @@ -808,7 +792,7 @@ dependencies = [ "proc-macro2", "quote", "rquickjs-core", - "syn 3.0.4", + "syn 3.0.10", ] [[package]] @@ -823,9 +807,9 @@ dependencies = [ [[package]] name = "rustc-hash" -version = "2.1.2" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" +checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d" [[package]] name = "rustc_version" @@ -875,7 +859,7 @@ checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" dependencies = [ "proc-macro2", "quote", - "syn 3.0.4", + "syn 3.0.10", ] [[package]] @@ -916,15 +900,9 @@ checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" [[package]] name = "smallvec" -version = "1.15.2" +version = "1.16.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" - -[[package]] -name = "spin" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5fe4ccb98d9c292d56fec89a5e07da7fc4cf0dc11e156b41793132775d3e591" +checksum = "149309498a5f7334b12ddae0cad8aba81ce785fe91755068867e017cf98fee46" [[package]] name = "spin" @@ -949,9 +927,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.117" +version = "2.0.119" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297" dependencies = [ "proc-macro2", "quote", @@ -960,9 +938,9 @@ dependencies = [ [[package]] name = "syn" -version = "3.0.4" +version = "3.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6275cddf4610d1775e6d1fe9469b2e77d0f39fd98fb7450901b821e0c53649f" +checksum = "4ae9ccbea3a7b0b09443036490c8f34a8b1b760fd345e5c0dae0aac718faea79" dependencies = [ "proc-macro2", "quote", @@ -981,38 +959,38 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.18" +version = "2.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +checksum = "09e52cb86a36cede5cb101bf8908837b3e4c6e5e59fe7fd85c23fb56200d189e" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "2.0.18" +version = "2.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +checksum = "fe5197923287db20a58125f0bc85c062f7f2c892de97b18c356f9efb14b28524" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 3.0.10", ] [[package]] name = "toml_datetime" -version = "1.1.1+spec-1.1.0" +version = "1.1.3+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" +checksum = "173f25c883cd2176a2d8212155638c0a60d484a9d84546d270866f40f56b6e49" dependencies = [ "serde_core", ] [[package]] name = "toml_edit" -version = "0.25.12+spec-1.1.0" +version = "0.25.19+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2153edc6955a6c354fad8f5efd38b6a8769bdccf9fe50f8e1329f81b0baa5d7" +checksum = "dd43688c4858d7264ae1cf9a9f874a67c3feb74a883b1cd7bebd6a4ea8307218" dependencies = [ "indexmap", "toml_datetime", @@ -1022,9 +1000,9 @@ dependencies = [ [[package]] name = "toml_parser" -version = "1.1.2+spec-1.1.0" +version = "1.1.5+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" +checksum = "baa693a8032d7e1cada7d0041e96126df243179ff061456783ac7f12bda4744c" dependencies = [ "winnow", ] @@ -1049,7 +1027,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -1066,9 +1044,9 @@ checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" [[package]] name = "unicode-ident" -version = "1.0.24" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" +checksum = "a2c754d6c33795a1c324727428e5a7dedb5b06195f9890bdbcba760d3e246563" [[package]] name = "unicode-segmentation" @@ -1105,15 +1083,15 @@ dependencies = [ [[package]] name = "winnow" -version = "1.0.3" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" +checksum = "8c3b93199df0ab874cdf0c6a94f0a0dc3423fe41f1cafb62b8bd7b1deb277aef" dependencies = [ "memchr", ] [[package]] name = "zmij" -version = "1.0.21" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" +checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b"