Skip to content

Commit 1ea41b7

Browse files
committed
Replace legacy python tools echo with Wasm host function
Remove leftover --enable-tools references from the README and host functions docs. Add a small echo WASIp1 host function example and update the Python tools example plus CI runtime args to register it via --tool echo=... instead of using the legacy built-in echo path. Signed-off-by: akrm al-hakimi <alhakimiakrmj@gmail.com>
1 parent f8835c5 commit 1ea41b7

9 files changed

Lines changed: 275 additions & 21 deletions

File tree

.github/workflows/test-examples.yml

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -183,8 +183,9 @@ jobs:
183183
args: "-- /hello.py"
184184
expect: "Hello from Python on Hyperlight"
185185
- example: python-tools
186-
args: "--enable-tools -- /test_tools.py"
186+
args: "-- /test_tools.py"
187187
expect: "Tool returned"
188+
needs_echo_tool: true
188189
- example: nodejs
189190
args: "-- /app/hello.js"
190191
expect: "Hello from Node.js on Hyperlight"
@@ -270,9 +271,15 @@ jobs:
270271
if: steps.kvm_check.outputs.available == 'true'
271272
run: |
272273
cd host
273-
cargo build --release --bin hyperlight-unikraft --bin multifn-test --bin pydriver-run
274+
cargo build --release --features wasm-host-fns --bin hyperlight-unikraft --bin multifn-test --bin pydriver-run
274275
sudo cp target/release/hyperlight-unikraft /usr/local/bin/
275276
277+
- name: Build echo Wasm host function
278+
if: steps.kvm_check.outputs.available == 'true' && matrix.needs_echo_tool == true
279+
run: |
280+
rustup target add wasm32-wasip1
281+
cargo build --manifest-path examples/echo-wasm-host-fxn/Cargo.toml --release --target wasm32-wasip1
282+
276283
- name: Build local-python-base images (python-agent-driver only)
277284
if: steps.kvm_check.outputs.available == 'true' && matrix.example == 'python-agent-driver'
278285
env:
@@ -338,6 +345,10 @@ jobs:
338345
mkdir -p "$mount_dir"
339346
mount_args="--mount $mount_dir:/host"
340347
fi
348+
tool_args=""
349+
if [ "${{ matrix.needs_echo_tool }}" = "true" ]; then
350+
tool_args="--tool echo=../echo-wasm-host-fxn/target/wasm32-wasip1/release/echo-wasm-host-fxn.wasm"
351+
fi
341352
case "${{ matrix.driver }}" in
342353
multifn-test)
343354
cmd=(timeout 60 /home/runner/work/hyperlight-unikraft/hyperlight-unikraft/host/target/release/multifn-test "$kernel" "$cpio")
@@ -350,7 +361,7 @@ jobs:
350361
if [ -n "$memory" ]; then
351362
mem_args="-m $memory"
352363
fi
353-
cmd=(timeout 120 hyperlight-unikraft -q $mem_args "$kernel" --initrd "$cpio" $mount_args ${{ matrix.args }})
364+
cmd=(timeout 120 hyperlight-unikraft -q $mem_args "$kernel" --initrd "$cpio" $mount_args $tool_args ${{ matrix.args }})
354365
;;
355366
esac
356367
set +e
@@ -514,8 +525,9 @@ jobs:
514525
args: "-- /hello.py"
515526
expect: "Hello from Python on Hyperlight"
516527
- example: python-tools
517-
args: "--enable-tools -- /test_tools.py"
528+
args: "-- /test_tools.py"
518529
expect: "Tool returned"
530+
needs_echo_tool: true
519531
- example: nodejs
520532
args: "-- /app/hello.js"
521533
expect: "Hello from Node.js on Hyperlight"
@@ -575,12 +587,19 @@ jobs:
575587
shell: pwsh
576588
run: |
577589
cd host
578-
cargo build --release --bin hyperlight-unikraft --bin multifn-test --bin pydriver-run --bin pyhl
590+
cargo build --release --features wasm-host-fns --bin hyperlight-unikraft --bin multifn-test --bin pydriver-run --bin pyhl
579591
Copy-Item target\release\hyperlight-unikraft.exe $env:USERPROFILE\.cargo\bin\ -Force
580592
Copy-Item target\release\multifn-test.exe $env:USERPROFILE\.cargo\bin\ -Force
581593
Copy-Item target\release\pydriver-run.exe $env:USERPROFILE\.cargo\bin\ -Force
582594
Copy-Item target\release\pyhl.exe $env:USERPROFILE\.cargo\bin\ -Force
583595
596+
- name: Build echo Wasm host function
597+
if: matrix.needs_echo_tool == true
598+
shell: pwsh
599+
run: |
600+
rustup target add wasm32-wasip1
601+
cargo build --manifest-path examples/echo-wasm-host-fxn/Cargo.toml --release --target wasm32-wasip1
602+
584603
- name: Download prebuilt image
585604
uses: actions/download-artifact@v4
586605
with:
@@ -596,6 +615,7 @@ jobs:
596615
$driver = '${{ matrix.driver }}'
597616
$runArgs = '${{ matrix.args }}'
598617
$needsMount = '${{ matrix.needs_mount }}'
618+
$needsEchoTool = '${{ matrix.needs_echo_tool }}'
599619
600620
# Read memory from the example's Justfile (single source of truth).
601621
$memory = ''
@@ -617,6 +637,11 @@ jobs:
617637
$mountArgs = @('--mount', ($mountDir + ':/host'))
618638
}
619639
640+
$toolArgs = @()
641+
if ($needsEchoTool -eq 'true') {
642+
$toolArgs = @('--tool', 'echo=examples/echo-wasm-host-fxn/target/wasm32-wasip1/release/echo-wasm-host-fxn.wasm')
643+
}
644+
620645
# Strict mode: expected output must appear AND the driver
621646
# must exit 0. Matches Linux semantics.
622647
$PSNativeCommandUseErrorActionPreference = $false
@@ -643,7 +668,7 @@ jobs:
643668
$memArgs = @('-m', $memory)
644669
}
645670
$out = & hyperlight-unikraft -q @memArgs `
646-
$kernel --initrd $cpio @mountArgs @argList 2>&1
671+
$kernel --initrd $cpio @mountArgs @toolArgs @argList 2>&1
647672
$rc = $LASTEXITCODE
648673
}
649674
}

README.md

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ This project enables running Linux applications (Python, Node.js, Go, Rust, C/C+
4949

5050
### Key Features
5151

52-
- **Thin, opt-in host surface** — by default, the guest has no access to the host filesystem, network, or any host functions. When you enable features like `--mount`, `--net`, `--enable-tools`, or `--tool`, a single `__dispatch` JSON-RPC bridge is registered as the only guest→host channel. See [host_functions.md](docs/host_functions.md) for the full list of dispatchable operations
52+
- **Thin, opt-in host surface** — by default, the guest has no access to the host filesystem, network, or custom host tools. When you enable capabilities like `--mount`, `--net`, or `--tool`, a single `__dispatch` JSON-RPC bridge is registered as the only guest→host channel. See [host_functions.md](docs/host_functions.md) for the full list of dispatchable operations
5353
- **Identity-mapped memory** - Simplified memory layout (vaddr == paddr)
5454
- **Generic cmdline mechanism** - Pass arguments to any application via `-- arg1 arg2 ...`
5555
- **Fast cold start** - Hyperlight's lightweight design enables millisecond startup times
@@ -182,6 +182,7 @@ just run
182182
| `helloworld-c` | Static PIE C binary | Compiled with `musl-gcc` |
183183
| `rust` | Static PIE Rust binary | Compiled with `rustc --target x86_64-unknown-linux-musl` |
184184
| `python` | CPython 3.12 | Rootfs from Docker, script passed via cmdline |
185+
| `python-tools` | CPython 3.12 + host function call | Calls an echo Wasm host function registered with `--tool echo=...` |
185186
| `go` | Static PIE Go binary | Compiled with musl via Docker for CGO support |
186187
| `nodejs` | Node.js 21 | Rootfs from Alpine, script passed via cmdline |
187188
| `hostfs-posix-c` | C + unmodified POSIX | `open`/`read`/`write`/`mkdir` against `/host`, forwarded by `lib/hostfs` |
@@ -281,7 +282,6 @@ Options:
281282
-m, --memory <MEMORY> Memory allocation (e.g., 256Mi, 512Mi, 1Gi) [default: 512Mi]
282283
--stack <STACK> Stack size (e.g., 8Mi) [default: 8Mi]
283284
-q, --quiet Quiet mode — suppress host-side status messages
284-
--enable-tools Register the built-in echo tool
285285
--tool <NAME=WASM> Register a WASIp1 module as a host tool (requires wasm-host-fns)
286286
--tool-wasi-dir <HOST[:GUEST]>
287287
Preopen a read-write host directory for Wasm tools
@@ -315,10 +315,13 @@ Build the CLI with the optional `wasm-host-fns` feature to register host-side cu
315315

316316
```bash
317317
cargo build --manifest-path host/Cargo.toml --release --features wasm-host-fns --bin hyperlight-unikraft
318-
hyperlight-unikraft kernel --initrd app.cpio --tool greet=./greet.wasm
318+
rustup target add wasm32-wasip1
319+
cargo build --manifest-path examples/echo-wasm-host-fxn/Cargo.toml --release --target wasm32-wasip1
320+
hyperlight-unikraft kernel --initrd app.cpio \
321+
--tool echo=examples/echo-wasm-host-fxn/target/wasm32-wasip1/release/echo-wasm-host-fxn.wasm
319322
```
320323

321-
The guest still calls the existing `__dispatch` envelope, for example `{"name":"greet","args":{"who":"Ada"}}`. The Wasm handler runs on the host in Wasmtime, receives that request JSON on stdin, and writes either a raw JSON result or `{"result": ...}` / `{"error": "..."}` to stdout. WASI filesystem and environment access are off unless granted with `--tool-wasi-dir`, `--tool-wasi-dir-ro`, `--tool-wasi-env`, or `--tool-wasi-env-inherit`.
324+
The guest still calls the existing `__dispatch` envelope, for example `{"name":"echo","args":{"message":"hello"}}`. The Wasm handler runs on the host in Wasmtime, receives that request JSON on stdin, and writes either a raw JSON result or `{"result": ...}` / `{"error": "..."}` to stdout. WASI filesystem and environment access are off unless granted with `--tool-wasi-dir`, `--tool-wasi-dir-ro`, `--tool-wasi-env`, or `--tool-wasi-env-inherit`.
322325

323326
## Project Structure
324327

docs/host_functions.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ Implementation lives in [`host/src/lib.rs`](../host/src/lib.rs). Guest-side call
1515
| `fs_*` tools | **Off** | `--mount HOST[:GUEST]` (repeatable) |
1616
| `net_*` tools | **Off** | `--net`, `--net-allow`, or `--net-block` |
1717
| Inbound listen | **Off** | `--port PORT` (requires network enabled) |
18-
| Custom tools | **Off** | `--tool NAME=WASM` with `wasm-host-fns`, `SandboxBuilder::tool()`, or legacy/demo `--enable-tools` echo |
18+
| Custom tools | **Off** | `--tool NAME=WASM` with `wasm-host-fns` or `SandboxBuilder::tool()` |
1919

2020
With **no flags**, the guest cannot reach the host filesystem or network through dispatch. Only internal plumbing (`__hl_exit`, `__hl_sleep`) is wired.
2121

@@ -85,7 +85,6 @@ hyperlight-unikraft KERNEL [--initrd CPIO] [options] [-- APP_ARGS...]
8585
| `--net-allow HOST_OR_IP` | Allow-list outbound destinations (implies `--net`). Repeatable. |
8686
| `--net-block HOST_OR_IP` | Block-list; all other destinations allowed (implies `--net`). Mutually exclusive with `--net-allow`. |
8787
| `--port PORT` | Allow `net_bind` / listen on `PORT` (implies `--net`). Without `--port`, outbound-only: bind is rejected. |
88-
| `--enable-tools` | Registers only the built-in `echo` demo tool. It does not load user code; prefer `--tool NAME=WASM` for CLI custom tools or `SandboxBuilder::tool()` for library users. |
8988
| `--tool NAME=WASM` | With the Cargo feature `wasm-host-fns`, registers `WASM` as a host-side WASIp1 custom tool named `NAME`. Repeatable. |
9089
| `--tool-wasi-dir HOST[:GUEST]` | Preopens a read-write host directory for every CLI Wasm tool. Default guest path is `/host`. Repeatable. |
9190
| `--tool-wasi-dir-ro HOST[:GUEST]` | Preopens a read-only host directory for every CLI Wasm tool. Default guest path is `/host`. Repeatable. |
@@ -200,19 +199,20 @@ Sockets are host-side (`socket2`); the guest sees opaque numeric **`fd`** handle
200199

201200
## Custom tools
202201

203-
**CLI demo tool:** `--enable-tools` registers only a built-in `echo` tool that returns `args` unchanged. It is useful as a smoke test and compatibility path, but it is not the CLI extension mechanism for user-provided host functions. CLI examples should prefer a Wasm `echo.wasm` registered with `--tool echo=...`; library examples should register an echo handler with `SandboxBuilder::tool()`.
204-
205-
**CLI Wasm tools:** build with the optional feature and pass one or more `--tool` flags:
202+
**CLI Wasm tools:** build with the optional feature and pass one or more `--tool` flags. The `examples/echo-wasm-host-fxn` module is a minimal echo handler used by `examples/python-tools`:
206203

207204
```bash
208205
cargo build --manifest-path host/Cargo.toml --features wasm-host-fns --bin hyperlight-unikraft
209-
hyperlight-unikraft kernel --initrd app.cpio --tool greet=./greet.wasm
206+
rustup target add wasm32-wasip1
207+
cargo build --manifest-path examples/echo-wasm-host-fxn/Cargo.toml --release --target wasm32-wasip1
208+
hyperlight-unikraft kernel --initrd app.cpio \
209+
--tool echo=examples/echo-wasm-host-fxn/target/wasm32-wasip1/release/echo-wasm-host-fxn.wasm
210210
```
211211

212212
Each `--tool NAME=WASM` module is compiled and linked before VM boot, then invoked as a fresh WASIp1 command for every matching guest `__dispatch` call. The handler receives the existing dispatch request on stdin:
213213

214214
```json
215-
{"name":"NAME","args":<json_value>}
215+
{"name":"echo","args":<json_value>}
216216
```
217217

218218
The handler writes JSON to stdout. It may write either a raw JSON result value or the normal dispatch envelope:

examples/echo-wasm-host-fxn/Cargo.lock

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
[package]
2+
name = "echo-wasm-host-fxn"
3+
version = "0.1.0"
4+
edition = "2021"
5+
publish = false
6+
7+
[[bin]]
8+
name = "echo-wasm-host-fxn"
9+
path = "src/main.rs"
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# echo-wasm-host-fxn
2+
#
3+
# Builds a tiny WASIp1 command module that can be registered with:
4+
# --tool echo=target/wasm32-wasip1/release/echo-wasm-host-fxn.wasm
5+
6+
set windows-shell := ["powershell.exe", "-NoLogo", "-Command"]
7+
8+
target := "wasm32-wasip1"
9+
wasm := "target/wasm32-wasip1/release/echo-wasm-host-fxn.wasm"
10+
11+
build:
12+
rustup target add {{target}}
13+
cargo build --release --target {{target}}
14+
15+
path:
16+
@echo {{wasm}}
17+
18+
clean:
19+
cargo clean
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# echo-wasm-host-fxn
2+
3+
A tiny WASIp1 command module used by the Python host-tools example as a custom host function.
4+
5+
The Hyperlight host runs the module through `--tool echo=...`. The module receives the `__dispatch` request JSON on stdin:
6+
7+
```json
8+
{"name":"echo","args":{"message":"hello"}}
9+
```
10+
11+
It writes `{"result": args}` to stdout, so the outer host dispatch returns the original `args` value as the tool result.
12+
13+
```bash
14+
just build
15+
hyperlight-unikraft kernel --initrd app.cpio \
16+
--tool echo=target/wasm32-wasip1/release/echo-wasm-host-fxn.wasm \
17+
-- /test_tools.py
18+
```

0 commit comments

Comments
 (0)