From 93a33ea14ebec1d1526e0506603ebdbf06c5e6ed Mon Sep 17 00:00:00 2001 From: Roman Volosatovs Date: Wed, 18 Feb 2026 11:56:46 +0100 Subject: [PATCH 1/7] test `cli-p3` example Signed-off-by: Roman Volosatovs --- tests/bindings.rs | 21 +++++++++++++++++++++ tests/componentize.rs | 17 +++++++++++++---- 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/tests/bindings.rs b/tests/bindings.rs index 2b583d8..51e92db 100644 --- a/tests/bindings.rs +++ b/tests/bindings.rs @@ -29,6 +29,27 @@ fn lint_cli_bindings() -> anyhow::Result<()> { Ok(()) } +#[test] +fn lint_cli_p3_bindings() -> anyhow::Result<()> { + let dir = tempfile::tempdir()?; + fs_extra::copy_items( + &["./examples/cli-p3", "./wit"], + dir.path(), + &CopyOptions::new(), + )?; + let path = dir.path().join("cli-p3"); + + generate_bindings(&path, "wasi:cli/command@0.3.0-rc-2026-01-06")?; + + assert!(predicate::path::is_dir().eval(&path.join("wit_world"))); + + _ = dir.keep(); + + mypy_check(&path, ["--strict", "-m", "app"]); + + Ok(()) +} + #[test] fn lint_http_bindings() -> anyhow::Result<()> { let dir = tempfile::tempdir()?; diff --git a/tests/componentize.rs b/tests/componentize.rs index e1fa63c..383b405 100644 --- a/tests/componentize.rs +++ b/tests/componentize.rs @@ -14,13 +14,22 @@ use tar::Archive; #[test] fn cli_example() -> anyhow::Result<()> { + test_cli_example("cli", "wasi:cli/command@0.2.0") +} + +#[test] +fn cli_p3_example() -> anyhow::Result<()> { + test_cli_example("cli-p3", "wasi:cli/command@0.3.0-rc-2026-01-06") +} + +fn test_cli_example(name: &str, world: &str) -> anyhow::Result<()> { let dir = tempfile::tempdir()?; fs_extra::copy_items( - &["./examples/cli", "./wit"], + &[format!("./examples/{name}").as_str(), "./wit"], dir.path(), &CopyOptions::new(), )?; - let path = dir.path().join("cli"); + let path = dir.path().join(name); cargo::cargo_bin_cmd!("componentize-py") .current_dir(&path) @@ -28,7 +37,7 @@ fn cli_example() -> anyhow::Result<()> { "-d", "../wit", "-w", - "wasi:cli/command@0.2.0", + world, "componentize", "app", "-o", @@ -40,7 +49,7 @@ fn cli_example() -> anyhow::Result<()> { Command::new("wasmtime") .current_dir(&path) - .args(["run", "cli.wasm"]) + .args(["run", "-Sp3", "-Wcomponent-model-async", "cli.wasm"]) .assert() .success() .stdout("Hello, world!\n"); From 23b6c6b13d88f6dd9fc5ac1162f8456361182d25 Mon Sep 17 00:00:00 2001 From: Roman Volosatovs Date: Wed, 18 Feb 2026 11:58:57 +0100 Subject: [PATCH 2/7] add `cli-p3` Wasm to gitignore Signed-off-by: Roman Volosatovs --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index d59c72e..5acc1bb 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,7 @@ examples/http/proxy examples/http/poll_loop.py examples/tcp/tcp.wasm examples/tcp/command +examples/cli-p3/cli.wasm examples/cli/cli.wasm examples/cli/command examples/sandbox/sandbox From 115b3a51c0fd663e2fef6105b280f28d50483a60 Mon Sep 17 00:00:00 2001 From: Roman Volosatovs Date: Wed, 18 Feb 2026 11:59:55 +0100 Subject: [PATCH 3/7] update `Cargo.lock` version Signed-off-by: Roman Volosatovs --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 8bfb5fe..fe89d03 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -477,7 +477,7 @@ dependencies = [ [[package]] name = "componentize-py" -version = "0.20.0" +version = "0.21.0" dependencies = [ "anyhow", "assert_cmd", From 0ef43b379b9982c341f15936e46bf1b983035956 Mon Sep 17 00:00:00 2001 From: Roman Volosatovs Date: Wed, 18 Feb 2026 12:07:04 +0100 Subject: [PATCH 4/7] cli-p3: add `-> None` Signed-off-by: Roman Volosatovs --- examples/cli-p3/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cli-p3/app.py b/examples/cli-p3/app.py index fd4184e..caf22c7 100644 --- a/examples/cli-p3/app.py +++ b/examples/cli-p3/app.py @@ -1,5 +1,5 @@ from wit_world import exports class Run(exports.Run): - async def run(self): + async def run(self) -> None: print("Hello, world!") From bceb5777b97a7be2e3f3d6098258fe2f2e2e3f71 Mon Sep 17 00:00:00 2001 From: Roman Volosatovs Date: Wed, 18 Feb 2026 14:23:33 +0100 Subject: [PATCH 5/7] add `tcp-p3` example and test Signed-off-by: Roman Volosatovs --- .gitignore | 1 + examples/tcp-p3/README.md | 45 ++++++++++++++++++++++ examples/tcp-p3/app.py | 81 +++++++++++++++++++++++++++++++++++++++ tests/bindings.rs | 19 +++++++++ tests/componentize.rs | 25 ++++++++---- 5 files changed, 164 insertions(+), 7 deletions(-) create mode 100644 examples/tcp-p3/README.md create mode 100644 examples/tcp-p3/app.py diff --git a/.gitignore b/.gitignore index 5acc1bb..430b6e3 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,7 @@ examples/http/.spin examples/http/http.wasm examples/http/proxy examples/http/poll_loop.py +examples/tcp-p3/tcp.wasm examples/tcp/tcp.wasm examples/tcp/command examples/cli-p3/cli.wasm diff --git a/examples/tcp-p3/README.md b/examples/tcp-p3/README.md new file mode 100644 index 0000000..3e744b3 --- /dev/null +++ b/examples/tcp-p3/README.md @@ -0,0 +1,45 @@ +# Example: `tcp-p3` + +This is an example of how to use [componentize-py] and [Wasmtime] to build and +run a Python-based component targetting version `0.3.0-rc-2026-01-06` of the +[wasi-cli] `command` world and making an outbound TCP request using [wasi-sockets]. + +[componentize-py]: https://github.com/bytecodealliance/componentize-py +[Wasmtime]: https://github.com/bytecodealliance/wasmtime +[wasi-cli]: https://github.com/WebAssembly/WASI/tree/v0.3.0-rc-2026-01-06/proposals/cli/wit-0.3.0-draft +[wasi-sockets]: https://github.com/WebAssembly/WASI/tree/v0.3.0-rc-2026-01-06/proposals/sockets/wit-0.3.0-draft + +## Prerequisites + +* `Wasmtime` 41.0.3 +* `componentize-py` 0.21.0 + +Below, we use [Rust](https://rustup.rs/)'s `cargo` to install `Wasmtime`. If +you don't have `cargo`, you can download and install from +https://github.com/bytecodealliance/wasmtime/releases/tag/v41.0.3. + +``` +cargo install --version 41.0.3 wasmtime-cli +pip install componentize-py==0.21.0 +``` + +## Running the demo + +First, in a separate terminal, run `netcat`, telling it to listen for incoming +TCP connections. You can choose any port you like. + +``` +nc -l 127.0.0.1 3456 +``` + +Now, build and run the example, using the same port you gave to `netcat`. + +``` +componentize-py -d ../../wit -w wasi:cli/command@0.3.0-rc-2026-01-06 componentize app -o tcp.wasm +wasmtime run -Sp3 -Sinherit-network -Wcomponent-model-async tcp.wasm 127.0.0.1:3456 +``` + +The program will open a TCP connection, send a message, and wait to receive a +response before exiting. You can give it a response by typing anything you like +into the terminal where `netcat` is running and then pressing the `Enter` key on +your keyboard. diff --git a/examples/tcp-p3/app.py b/examples/tcp-p3/app.py new file mode 100644 index 0000000..33e74c9 --- /dev/null +++ b/examples/tcp-p3/app.py @@ -0,0 +1,81 @@ +import sys +import asyncio +import ipaddress +from ipaddress import IPv4Address, IPv6Address +import wit_world +from wit_world import exports +from wit_world.imports.wasi_sockets_types import ( + TcpSocket, + IpSocketAddress_Ipv4, + IpSocketAddress_Ipv6, + Ipv4SocketAddress, + Ipv6SocketAddress, + IpAddressFamily, +) +from typing import Tuple + + +IPAddress = IPv4Address | IPv6Address + +class Run(exports.Run): + async def run(self) -> None: + args = sys.argv[1:] + if len(args) != 1: + print("usage: tcp-p3
:", file=sys.stderr) + exit(-1) + + address, port = parse_address_and_port(args[0]) + await send_and_receive(address, port) + + +def parse_address_and_port(address_and_port: str) -> Tuple[IPAddress, int]: + ip, separator, port = address_and_port.rpartition(":") + assert separator + return (ipaddress.ip_address(ip.strip("[]")), int(port)) + + +def make_socket_address(address: IPAddress, port: int) -> IpSocketAddress_Ipv4 | IpSocketAddress_Ipv6: + if isinstance(address, IPv4Address): + octets = address.packed + return IpSocketAddress_Ipv4(Ipv4SocketAddress( + port=port, + address=(octets[0], octets[1], octets[2], octets[3]), + )) + else: + b = address.packed + return IpSocketAddress_Ipv6(Ipv6SocketAddress( + port=port, + flow_info=0, + address=( + (b[0] << 8) | b[1], + (b[2] << 8) | b[3], + (b[4] << 8) | b[5], + (b[6] << 8) | b[7], + (b[8] << 8) | b[9], + (b[10] << 8) | b[11], + (b[12] << 8) | b[13], + (b[14] << 8) | b[15], + ), + scope_id=0, + )) + + +async def send_and_receive(address: IPAddress, port: int) -> None: + family = IpAddressFamily.IPV4 if isinstance(address, IPv4Address) else IpAddressFamily.IPV6 + + sock = TcpSocket.create(family) + + await sock.connect(make_socket_address(address, port)) + + send_tx, send_rx = wit_world.byte_stream() + async def write() -> None: + with send_tx: + await send_tx.write_all(b"hello, world!") + await asyncio.gather(sock.send(send_rx), write()) + + recv_rx, recv_fut = sock.receive() + async def read() -> None: + with recv_rx: + data = await recv_rx.read(1024) + print(f"received: {str(data)}") + await asyncio.gather(recv_fut.read(), read()) diff --git a/tests/bindings.rs b/tests/bindings.rs index 51e92db..f40b482 100644 --- a/tests/bindings.rs +++ b/tests/bindings.rs @@ -171,6 +171,25 @@ fn lint_tcp_bindings() -> anyhow::Result<()> { Ok(()) } +#[test] +fn lint_tcp_p3_bindings() -> anyhow::Result<()> { + let dir = tempfile::tempdir()?; + fs_extra::copy_items( + &["./examples/tcp-p3", "./wit"], + dir.path(), + &CopyOptions::new(), + )?; + let path = dir.path().join("tcp-p3"); + + generate_bindings(&path, "wasi:cli/command@0.3.0-rc-2026-01-06")?; + + assert!(predicate::path::is_dir().eval(&path.join("wit_world"))); + + mypy_check(&path, ["--strict", "-m", "app"]); + + Ok(()) +} + fn generate_bindings(path: &Path, world: &str) -> Result { Ok(cargo::cargo_bin_cmd!("componentize-py") .current_dir(path) diff --git a/tests/componentize.rs b/tests/componentize.rs index 383b405..215f354 100644 --- a/tests/componentize.rs +++ b/tests/componentize.rs @@ -1,3 +1,4 @@ +use core::net::Ipv4Addr; use std::{ io::Write, path::{Path, PathBuf}, @@ -241,13 +242,22 @@ fn sandbox_example() -> anyhow::Result<()> { #[test] fn tcp_example() -> anyhow::Result<()> { + test_tcp_example("tcp", "wasi:cli/command@0.2.0") +} + +#[test] +fn tcp_p3_example() -> anyhow::Result<()> { + test_tcp_example("tcp-p3", "wasi:cli/command@0.3.0-rc-2026-01-06") +} + +fn test_tcp_example(name: &str, world: &str) -> anyhow::Result<()> { let dir = tempfile::tempdir()?; fs_extra::copy_items( - &["./examples/tcp", "./wit"], + &[format!("./examples/{name}").as_str(), "./wit"], dir.path(), &CopyOptions::new(), )?; - let path = dir.path().join("tcp"); + let path = dir.path().join(name); cargo::cargo_bin_cmd!("componentize-py") .current_dir(&path) @@ -255,7 +265,7 @@ fn tcp_example() -> anyhow::Result<()> { "-d", "../wit", "-w", - "wasi:cli/command@0.2.0", + world, "componentize", "app", "-o", @@ -265,16 +275,17 @@ fn tcp_example() -> anyhow::Result<()> { .success() .stdout("Component built successfully\n"); - let listener = std::net::TcpListener::bind("127.0.0.1:3456")?; + let listener = std::net::TcpListener::bind((Ipv4Addr::LOCALHOST, 0))?; + let port = listener.local_addr()?.port(); let tcp_handle = std::process::Command::new("wasmtime") .current_dir(&path) .args([ "run", - "--wasi", - "inherit-network", + "-Sp3,inherit-network", + "-Wcomponent-model-async", "tcp.wasm", - "127.0.0.1:3456", + &format!("127.0.0.1:{port}"), ]) .stdout(Stdio::piped()) .spawn()?; From ae8553d90f19bf5759fb20272942e7b6ae35709d Mon Sep 17 00:00:00 2001 From: Roman Volosatovs Date: Thu, 19 Feb 2026 12:36:36 +0100 Subject: [PATCH 6/7] update p3 WIT Signed-off-by: Roman Volosatovs --- .../{cli-0.3.0-rc-2026-01-06.wit => cli.wit} | 158 +++++++++--------- ...cks-0.3.0-rc-2026-01-06.wit => clocks.wit} | 34 ++-- ...0.3.0-rc-2026-01-06.wit => filesystem.wit} | 125 +++++++------- ...{http-0.3.0-rc-2026-01-06.wit => http.wit} | 70 +++++--- ...dom-0.3.0-rc-2026-01-06.wit => random.wit} | 26 +-- ...ts-0.3.0-rc-2026-01-06.wit => sockets.wit} | 134 ++++++++------- 6 files changed, 294 insertions(+), 253 deletions(-) rename wit/deps/{cli-0.3.0-rc-2026-01-06.wit => cli.wit} (60%) rename wit/deps/{clocks-0.3.0-rc-2026-01-06.wit => clocks.wit} (89%) rename wit/deps/{filesystem-0.3.0-rc-2026-01-06.wit => filesystem.wit} (87%) rename wit/deps/{http-0.3.0-rc-2026-01-06.wit => http.wit} (90%) rename wit/deps/{random-0.3.0-rc-2026-01-06.wit => random.wit} (85%) rename wit/deps/{sockets-0.3.0-rc-2026-01-06.wit => sockets.wit} (92%) diff --git a/wit/deps/cli-0.3.0-rc-2026-01-06.wit b/wit/deps/cli.wit similarity index 60% rename from wit/deps/cli-0.3.0-rc-2026-01-06.wit rename to wit/deps/cli.wit index 0f987bf..4f8bbf8 100644 --- a/wit/deps/cli-0.3.0-rc-2026-01-06.wit +++ b/wit/deps/cli.wit @@ -1,6 +1,6 @@ -package wasi:cli@0.3.0-rc-2026-01-06; +package wasi:cli@0.3.0-rc-2026-02-09; -@since(version = 0.3.0-rc-2026-01-06) +@since(version = 0.3.0-rc-2026-02-09) interface environment { /// Get the POSIX-style environment variables. /// @@ -10,23 +10,23 @@ interface environment { /// Morally, these are a value import, but until value imports are available /// in the component model, this import function should return the same /// values each time it is called. - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) get-environment: func() -> list>; /// Get the POSIX-style arguments to the program. - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) get-arguments: func() -> list; /// Return a path that programs should use as their initial current working /// directory, interpreting `.` as shorthand for this. - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) get-initial-cwd: func() -> option; } -@since(version = 0.3.0-rc-2026-01-06) +@since(version = 0.3.0-rc-2026-02-09) interface exit { /// Exit the current instance and any linked instances. - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) exit: func(status: result); /// Exit the current instance and any linked instances, reporting the @@ -41,16 +41,16 @@ interface exit { exit-with-code: func(status-code: u8); } -@since(version = 0.3.0-rc-2026-01-06) +@since(version = 0.3.0-rc-2026-02-09) interface run { /// Run the program. - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) run: async func() -> result; } -@since(version = 0.3.0-rc-2026-01-06) +@since(version = 0.3.0-rc-2026-02-09) interface types { - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) enum error-code { /// Input/output error io, @@ -61,7 +61,7 @@ interface types { } } -@since(version = 0.3.0-rc-2026-01-06) +@since(version = 0.3.0-rc-2026-02-09) interface stdin { use types.{error-code}; @@ -78,11 +78,11 @@ interface stdin { /// /// Multiple streams may be active at the same time. The behavior of concurrent /// reads is implementation-specific. - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) read-via-stream: func() -> tuple, future>>; } -@since(version = 0.3.0-rc-2026-01-06) +@since(version = 0.3.0-rc-2026-02-09) interface stdout { use types.{error-code}; @@ -94,11 +94,11 @@ interface stdout { /// /// Otherwise if there is an error the readable end of the stream will be /// dropped and this function will return an error-code. - @since(version = 0.3.0-rc-2026-01-06) - write-via-stream: async func(data: stream) -> result<_, error-code>; + @since(version = 0.3.0-rc-2026-02-09) + write-via-stream: func(data: stream) -> future>; } -@since(version = 0.3.0-rc-2026-01-06) +@since(version = 0.3.0-rc-2026-02-09) interface stderr { use types.{error-code}; @@ -110,8 +110,8 @@ interface stderr { /// /// Otherwise if there is an error the readable end of the stream will be /// dropped and this function will return an error-code. - @since(version = 0.3.0-rc-2026-01-06) - write-via-stream: async func(data: stream) -> result<_, error-code>; + @since(version = 0.3.0-rc-2026-02-09) + write-via-stream: func(data: stream) -> future>; } /// Terminal input. @@ -119,10 +119,10 @@ interface stderr { /// In the future, this may include functions for disabling echoing, /// disabling input buffering so that keyboard events are sent through /// immediately, querying supported features, and so on. -@since(version = 0.3.0-rc-2026-01-06) +@since(version = 0.3.0-rc-2026-02-09) interface terminal-input { /// The input side of a terminal. - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) resource terminal-input; } @@ -131,126 +131,126 @@ interface terminal-input { /// In the future, this may include functions for querying the terminal /// size, being notified of terminal size changes, querying supported /// features, and so on. -@since(version = 0.3.0-rc-2026-01-06) +@since(version = 0.3.0-rc-2026-02-09) interface terminal-output { /// The output side of a terminal. - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) resource terminal-output; } /// An interface providing an optional `terminal-input` for stdin as a /// link-time authority. -@since(version = 0.3.0-rc-2026-01-06) +@since(version = 0.3.0-rc-2026-02-09) interface terminal-stdin { - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) use terminal-input.{terminal-input}; /// If stdin is connected to a terminal, return a `terminal-input` handle /// allowing further interaction with it. - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) get-terminal-stdin: func() -> option; } /// An interface providing an optional `terminal-output` for stdout as a /// link-time authority. -@since(version = 0.3.0-rc-2026-01-06) +@since(version = 0.3.0-rc-2026-02-09) interface terminal-stdout { - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) use terminal-output.{terminal-output}; /// If stdout is connected to a terminal, return a `terminal-output` handle /// allowing further interaction with it. - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) get-terminal-stdout: func() -> option; } /// An interface providing an optional `terminal-output` for stderr as a /// link-time authority. -@since(version = 0.3.0-rc-2026-01-06) +@since(version = 0.3.0-rc-2026-02-09) interface terminal-stderr { - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) use terminal-output.{terminal-output}; /// If stderr is connected to a terminal, return a `terminal-output` handle /// allowing further interaction with it. - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) get-terminal-stderr: func() -> option; } -@since(version = 0.3.0-rc-2026-01-06) +@since(version = 0.3.0-rc-2026-02-09) world imports { - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) import environment; - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) import exit; - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) import types; - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) import stdin; - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) import stdout; - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) import stderr; - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) import terminal-input; - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) import terminal-output; - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) import terminal-stdin; - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) import terminal-stdout; - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) import terminal-stderr; - import wasi:clocks/types@0.3.0-rc-2026-01-06; - import wasi:clocks/monotonic-clock@0.3.0-rc-2026-01-06; - import wasi:clocks/system-clock@0.3.0-rc-2026-01-06; + import wasi:clocks/types@0.3.0-rc-2026-02-09; + import wasi:clocks/monotonic-clock@0.3.0-rc-2026-02-09; + import wasi:clocks/system-clock@0.3.0-rc-2026-02-09; @unstable(feature = clocks-timezone) - import wasi:clocks/timezone@0.3.0-rc-2026-01-06; - import wasi:filesystem/types@0.3.0-rc-2026-01-06; - import wasi:filesystem/preopens@0.3.0-rc-2026-01-06; - import wasi:sockets/types@0.3.0-rc-2026-01-06; - import wasi:sockets/ip-name-lookup@0.3.0-rc-2026-01-06; - import wasi:random/random@0.3.0-rc-2026-01-06; - import wasi:random/insecure@0.3.0-rc-2026-01-06; - import wasi:random/insecure-seed@0.3.0-rc-2026-01-06; + import wasi:clocks/timezone@0.3.0-rc-2026-02-09; + import wasi:filesystem/types@0.3.0-rc-2026-02-09; + import wasi:filesystem/preopens@0.3.0-rc-2026-02-09; + import wasi:sockets/types@0.3.0-rc-2026-02-09; + import wasi:sockets/ip-name-lookup@0.3.0-rc-2026-02-09; + import wasi:random/random@0.3.0-rc-2026-02-09; + import wasi:random/insecure@0.3.0-rc-2026-02-09; + import wasi:random/insecure-seed@0.3.0-rc-2026-02-09; } -@since(version = 0.3.0-rc-2026-01-06) +@since(version = 0.3.0-rc-2026-02-09) world command { - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) import environment; - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) import exit; - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) import types; - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) import stdin; - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) import stdout; - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) import stderr; - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) import terminal-input; - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) import terminal-output; - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) import terminal-stdin; - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) import terminal-stdout; - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) import terminal-stderr; - import wasi:clocks/types@0.3.0-rc-2026-01-06; - import wasi:clocks/monotonic-clock@0.3.0-rc-2026-01-06; - import wasi:clocks/system-clock@0.3.0-rc-2026-01-06; + import wasi:clocks/types@0.3.0-rc-2026-02-09; + import wasi:clocks/monotonic-clock@0.3.0-rc-2026-02-09; + import wasi:clocks/system-clock@0.3.0-rc-2026-02-09; @unstable(feature = clocks-timezone) - import wasi:clocks/timezone@0.3.0-rc-2026-01-06; - import wasi:filesystem/types@0.3.0-rc-2026-01-06; - import wasi:filesystem/preopens@0.3.0-rc-2026-01-06; - import wasi:sockets/types@0.3.0-rc-2026-01-06; - import wasi:sockets/ip-name-lookup@0.3.0-rc-2026-01-06; - import wasi:random/random@0.3.0-rc-2026-01-06; - import wasi:random/insecure@0.3.0-rc-2026-01-06; - import wasi:random/insecure-seed@0.3.0-rc-2026-01-06; + import wasi:clocks/timezone@0.3.0-rc-2026-02-09; + import wasi:filesystem/types@0.3.0-rc-2026-02-09; + import wasi:filesystem/preopens@0.3.0-rc-2026-02-09; + import wasi:sockets/types@0.3.0-rc-2026-02-09; + import wasi:sockets/ip-name-lookup@0.3.0-rc-2026-02-09; + import wasi:random/random@0.3.0-rc-2026-02-09; + import wasi:random/insecure@0.3.0-rc-2026-02-09; + import wasi:random/insecure-seed@0.3.0-rc-2026-02-09; - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) export run; } diff --git a/wit/deps/clocks-0.3.0-rc-2026-01-06.wit b/wit/deps/clocks.wit similarity index 89% rename from wit/deps/clocks-0.3.0-rc-2026-01-06.wit rename to wit/deps/clocks.wit index fc59f5a..71986ba 100644 --- a/wit/deps/clocks-0.3.0-rc-2026-01-06.wit +++ b/wit/deps/clocks.wit @@ -1,10 +1,10 @@ -package wasi:clocks@0.3.0-rc-2026-01-06; +package wasi:clocks@0.3.0-rc-2026-02-09; /// This interface common types used throughout wasi:clocks. -@since(version = 0.3.0-rc-2026-01-06) +@since(version = 0.3.0-rc-2026-02-09) interface types { /// A duration of time, in nanoseconds. - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) type duration = u64; } @@ -16,14 +16,14 @@ interface types { /// /// A monotonic clock is a clock which has an unspecified initial value, and /// successive reads of the clock will produce non-decreasing values. -@since(version = 0.3.0-rc-2026-01-06) +@since(version = 0.3.0-rc-2026-02-09) interface monotonic-clock { use types.{duration}; /// A mark on a monotonic clock is a number of nanoseconds since an /// unspecified initial value, and can only be compared to instances from /// the same monotonic-clock. - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) type mark = u64; /// Read the current value of the clock. @@ -35,20 +35,20 @@ interface monotonic-clock { /// the value of the clock in a `mark`. Consequently, implementations /// should ensure that the starting time is low enough to avoid the /// possibility of overflow in practice. - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) now: func() -> mark; /// Query the resolution of the clock. Returns the duration of time /// corresponding to a clock tick. - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) get-resolution: func() -> duration; /// Wait until the specified mark has occurred. - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) wait-until: async func(when: mark); /// Wait for the specified duration to elapse. - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) wait-for: async func(how-long: duration); } @@ -62,7 +62,7 @@ interface monotonic-clock { /// monotonic, making it unsuitable for measuring elapsed time. /// /// It is intended for reporting the current date and time for humans. -@since(version = 0.3.0-rc-2026-01-06) +@since(version = 0.3.0-rc-2026-02-09) interface system-clock { use types.{duration}; @@ -82,7 +82,7 @@ interface system-clock { /// /// [POSIX's Seconds Since the Epoch]: https://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xbd_chap04.html#tag_21_04_16 /// [Unix Time]: https://en.wikipedia.org/wiki/Unix_time - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) record instant { seconds: s64, nanoseconds: u32, @@ -94,12 +94,12 @@ interface system-clock { /// will not necessarily produce a sequence of non-decreasing values. /// /// The nanoseconds field of the output is always less than 1000000000. - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) now: func() -> instant; /// Query the resolution of the clock. Returns the smallest duration of time /// that the implementation permits distinguishing. - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) get-resolution: func() -> duration; } @@ -148,13 +148,13 @@ interface timezone { to-debug-string: func() -> string; } -@since(version = 0.3.0-rc-2026-01-06) +@since(version = 0.3.0-rc-2026-02-09) world imports { - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) import types; - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) import monotonic-clock; - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) import system-clock; @unstable(feature = clocks-timezone) import timezone; diff --git a/wit/deps/filesystem-0.3.0-rc-2026-01-06.wit b/wit/deps/filesystem.wit similarity index 87% rename from wit/deps/filesystem-0.3.0-rc-2026-01-06.wit rename to wit/deps/filesystem.wit index 0ac149c..8fb8304 100644 --- a/wit/deps/filesystem-0.3.0-rc-2026-01-06.wit +++ b/wit/deps/filesystem.wit @@ -1,12 +1,9 @@ -package wasi:filesystem@0.3.0-rc-2026-01-06; +package wasi:filesystem@0.3.0-rc-2026-02-09; /// WASI filesystem is a filesystem API primarily intended to let users run WASI /// programs that access their files on their existing filesystems, without /// significant overhead. /// -/// It is intended to be roughly portable between Unix-family platforms and -/// Windows, though it does not hide many of the major differences. -/// /// Paths are passed as interface-type `string`s, meaning they must consist of /// a sequence of Unicode Scalar Values (USVs). Some filesystems may contain /// paths which are not accessible by this API. @@ -23,20 +20,34 @@ package wasi:filesystem@0.3.0-rc-2026-01-06; /// For more information about WASI path resolution and sandboxing, see /// [WASI filesystem path resolution]. /// +/// Though this package presents a portable interface modelled on POSIX, it +/// prioritizes compatibility over portability: allowing users to access their +/// files on their machine is more important than exposing a single semantics +/// across all platforms. Notably, depending on the underlying operating system +/// and file system: +/// * Paths may be case-folded or not. +/// * Deleting (unlinking) a file may fail if there are other file descriptors +/// open. +/// * Durability and atomicity of changes to underlying files when there are +/// concurrent writers. +/// +/// Users that need well-defined, portable semantics should use a key-value +/// store or a database instead. +/// /// [WASI filesystem path resolution]: https://github.com/WebAssembly/wasi-filesystem/blob/main/path-resolution.md -@since(version = 0.3.0-rc-2026-01-06) +@since(version = 0.3.0-rc-2026-02-09) interface types { - @since(version = 0.3.0-rc-2026-01-06) - use wasi:clocks/system-clock@0.3.0-rc-2026-01-06.{instant}; + @since(version = 0.3.0-rc-2026-02-09) + use wasi:clocks/system-clock@0.3.0-rc-2026-02-09.{instant}; /// File size or length of a region within a file. - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) type filesize = u64; /// The type of a filesystem object referenced by a descriptor. /// /// Note: This was called `filetype` in earlier versions of WASI. - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) enum descriptor-type { /// The type of the descriptor or file is unknown or is different from /// any of the other types specified. @@ -60,7 +71,7 @@ interface types { /// Descriptor flags. /// /// Note: This was called `fdflags` in earlier versions of WASI. - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) flags descriptor-flags { /// Read mode: Data can be read. read, @@ -102,7 +113,7 @@ interface types { } /// Flags determining the method of how paths are resolved. - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) flags path-flags { /// As long as the resolved path corresponds to a symbolic link, it is /// expanded. @@ -110,7 +121,7 @@ interface types { } /// Open flags used by `open-at`. - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) flags open-flags { /// Create file if it does not exist, similar to `O_CREAT` in POSIX. create, @@ -123,13 +134,13 @@ interface types { } /// Number of hard links to an inode. - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) type link-count = u64; /// File attributes. /// /// Note: This was called `filestat` in earlier versions of WASI. - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) record descriptor-stat { /// File type. %type: descriptor-type, @@ -156,7 +167,7 @@ interface types { } /// When setting a timestamp, this gives the value to set it to. - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) variant new-timestamp { /// Leave the timestamp set to its previous value. no-change, @@ -168,6 +179,7 @@ interface types { } /// A directory entry. + @since(version = 0.3.0-rc-2026-02-09) record directory-entry { /// The type of the file referred to by this directory entry. %type: descriptor-type, @@ -179,6 +191,7 @@ interface types { /// Not all of these error codes are returned by the functions provided by this /// API; some are used in higher-level library layers, and others are provided /// merely for alignment with POSIX. + @since(version = 0.3.0-rc-2026-02-09) enum error-code { /// Permission denied, similar to `EACCES` in POSIX. access, @@ -255,7 +268,7 @@ interface types { } /// File or memory access pattern advisory information. - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) enum advice { /// The application has no advice to give on its behavior with respect /// to the specified data. @@ -279,7 +292,7 @@ interface types { /// A 128-bit hash value, split into parts because wasm doesn't have a /// 128-bit integer type. - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) record metadata-hash-value { /// 64 bits of a 128-bit hash value. lower: u64, @@ -290,7 +303,7 @@ interface types { /// A descriptor is a reference to a filesystem object, which may be a file, /// directory, named pipe, special file, or other object on which filesystem /// calls may be made. - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) resource descriptor { /// Return a stream for reading from a file. /// @@ -308,7 +321,7 @@ interface types { /// resolves to `err` with an `error-code`. /// /// Note: This is similar to `pread` in POSIX. - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) read-via-stream: func(offset: filesize) -> tuple, future>>; /// Return a stream for writing to a file, if available. /// @@ -322,8 +335,8 @@ interface types { /// written or an error is encountered. /// /// Note: This is similar to `pwrite` in POSIX. - @since(version = 0.3.0-rc-2026-01-06) - write-via-stream: async func(data: stream, offset: filesize) -> result<_, error-code>; + @since(version = 0.3.0-rc-2026-02-09) + write-via-stream: func(data: stream, offset: filesize) -> future>; /// Return a stream for appending to a file, if available. /// /// May fail with an error-code describing why the file cannot be appended. @@ -332,12 +345,12 @@ interface types { /// written or an error is encountered. /// /// Note: This is similar to `write` with `O_APPEND` in POSIX. - @since(version = 0.3.0-rc-2026-01-06) - append-via-stream: async func(data: stream) -> result<_, error-code>; + @since(version = 0.3.0-rc-2026-02-09) + append-via-stream: func(data: stream) -> future>; /// Provide file advisory information on a descriptor. /// /// This is similar to `posix_fadvise` in POSIX. - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) advise: async func(offset: filesize, length: filesize, advice: advice) -> result<_, error-code>; /// Synchronize the data of a file to disk. /// @@ -345,7 +358,7 @@ interface types { /// opened for writing. /// /// Note: This is similar to `fdatasync` in POSIX. - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) sync-data: async func() -> result<_, error-code>; /// Get flags associated with a descriptor. /// @@ -353,7 +366,7 @@ interface types { /// /// Note: This returns the value that was the `fs_flags` value returned /// from `fdstat_get` in earlier versions of WASI. - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) get-flags: async func() -> result; /// Get the dynamic type of a descriptor. /// @@ -365,20 +378,20 @@ interface types { /// /// Note: This returns the value that was the `fs_filetype` value returned /// from `fdstat_get` in earlier versions of WASI. - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) get-type: async func() -> result; /// Adjust the size of an open file. If this increases the file's size, the /// extra bytes are filled with zeros. /// /// Note: This was called `fd_filestat_set_size` in earlier versions of WASI. - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) set-size: async func(size: filesize) -> result<_, error-code>; /// Adjust the timestamps of an open file or directory. /// /// Note: This is similar to `futimens` in POSIX. /// /// Note: This was called `fd_filestat_set_times` in earlier versions of WASI. - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) set-times: async func(data-access-timestamp: new-timestamp, data-modification-timestamp: new-timestamp) -> result<_, error-code>; /// Read directory entries from a directory. /// @@ -392,20 +405,20 @@ interface types { /// /// This function returns a future, which will resolve to an error code if /// reading full contents of the directory fails. - @since(version = 0.3.0-rc-2026-01-06) - read-directory: async func() -> tuple, future>>; + @since(version = 0.3.0-rc-2026-02-09) + read-directory: func() -> tuple, future>>; /// Synchronize the data and metadata of a file to disk. /// /// This function succeeds with no effect if the file descriptor is not /// opened for writing. /// /// Note: This is similar to `fsync` in POSIX. - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) sync: async func() -> result<_, error-code>; /// Create a directory. /// /// Note: This is similar to `mkdirat` in POSIX. - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) create-directory-at: async func(path: string) -> result<_, error-code>; /// Return the attributes of an open file or directory. /// @@ -416,7 +429,7 @@ interface types { /// modified, use `metadata-hash`. /// /// Note: This was called `fd_filestat_get` in earlier versions of WASI. - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) stat: async func() -> result; /// Return the attributes of a file or directory. /// @@ -425,7 +438,7 @@ interface types { /// discussion of alternatives. /// /// Note: This was called `path_filestat_get` in earlier versions of WASI. - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) stat-at: async func(path-flags: path-flags, path: string) -> result; /// Adjust the timestamps of a file or directory. /// @@ -433,7 +446,7 @@ interface types { /// /// Note: This was called `path_filestat_set_times` in earlier versions of /// WASI. - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) set-times-at: async func(path-flags: path-flags, path: string, data-access-timestamp: new-timestamp, data-modification-timestamp: new-timestamp) -> result<_, error-code>; /// Create a hard link. /// @@ -442,7 +455,7 @@ interface types { /// `error-code::not-permitted` if the old path is not a file. /// /// Note: This is similar to `linkat` in POSIX. - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) link-at: async func(old-path-flags: path-flags, old-path: string, new-descriptor: borrow, new-path: string) -> result<_, error-code>; /// Open a file or directory. /// @@ -456,7 +469,7 @@ interface types { /// `error-code::read-only`. /// /// Note: This is similar to `openat` in POSIX. - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) open-at: async func(path-flags: path-flags, path: string, open-flags: open-flags, %flags: descriptor-flags) -> result; /// Read the contents of a symbolic link. /// @@ -464,19 +477,19 @@ interface types { /// filesystem, this function fails with `error-code::not-permitted`. /// /// Note: This is similar to `readlinkat` in POSIX. - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) readlink-at: async func(path: string) -> result; /// Remove a directory. /// /// Return `error-code::not-empty` if the directory is not empty. /// /// Note: This is similar to `unlinkat(fd, path, AT_REMOVEDIR)` in POSIX. - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) remove-directory-at: async func(path: string) -> result<_, error-code>; /// Rename a filesystem object. /// /// Note: This is similar to `renameat` in POSIX. - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) rename-at: async func(old-path: string, new-descriptor: borrow, new-path: string) -> result<_, error-code>; /// Create a symbolic link (also known as a "symlink"). /// @@ -484,13 +497,13 @@ interface types { /// `error-code::not-permitted`. /// /// Note: This is similar to `symlinkat` in POSIX. - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) symlink-at: async func(old-path: string, new-path: string) -> result<_, error-code>; /// Unlink a filesystem object that is not a directory. /// /// Return `error-code::is-directory` if the path refers to a directory. /// Note: This is similar to `unlinkat(fd, path, 0)` in POSIX. - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) unlink-file-at: async func(path: string) -> result<_, error-code>; /// Test whether two descriptors refer to the same filesystem object. /// @@ -498,7 +511,7 @@ interface types { /// same device (`st_dev`) and inode (`st_ino` or `d_ino`) numbers. /// wasi-filesystem does not expose device and inode numbers, so this function /// may be used instead. - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) is-same-object: async func(other: borrow) -> bool; /// Return a hash of the metadata associated with a filesystem object referred /// to by a descriptor. @@ -519,35 +532,35 @@ interface types { /// computed hash. /// /// However, none of these is required. - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) metadata-hash: async func() -> result; /// Return a hash of the metadata associated with a filesystem object referred /// to by a directory descriptor and a relative path. /// /// This performs the same hash computation as `metadata-hash`. - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) metadata-hash-at: async func(path-flags: path-flags, path: string) -> result; } } -@since(version = 0.3.0-rc-2026-01-06) +@since(version = 0.3.0-rc-2026-02-09) interface preopens { - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) use types.{descriptor}; /// Return the set of preopened directories, and their paths. - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) get-directories: func() -> list>; } -@since(version = 0.3.0-rc-2026-01-06) +@since(version = 0.3.0-rc-2026-02-09) world imports { - @since(version = 0.3.0-rc-2026-01-06) - import wasi:clocks/types@0.3.0-rc-2026-01-06; - @since(version = 0.3.0-rc-2026-01-06) - import wasi:clocks/system-clock@0.3.0-rc-2026-01-06; - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) + import wasi:clocks/types@0.3.0-rc-2026-02-09; + @since(version = 0.3.0-rc-2026-02-09) + import wasi:clocks/system-clock@0.3.0-rc-2026-02-09; + @since(version = 0.3.0-rc-2026-02-09) import types; - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) import preopens; } diff --git a/wit/deps/http-0.3.0-rc-2026-01-06.wit b/wit/deps/http.wit similarity index 90% rename from wit/deps/http-0.3.0-rc-2026-01-06.wit rename to wit/deps/http.wit index 3860af7..442b18e 100644 --- a/wit/deps/http-0.3.0-rc-2026-01-06.wit +++ b/wit/deps/http.wit @@ -1,11 +1,13 @@ -package wasi:http@0.3.0-rc-2026-01-06; +package wasi:http@0.3.0-rc-2026-02-09; /// This interface defines all of the types and methods for implementing HTTP /// Requests and Responses, as well as their headers, trailers, and bodies. +@since(version = 0.3.0-rc-2026-02-09) interface types { - use wasi:clocks/types@0.3.0-rc-2026-01-06.{duration}; + use wasi:clocks/types@0.3.0-rc-2026-02-09.{duration}; /// This type corresponds to HTTP standard Methods. + @since(version = 0.3.0-rc-2026-02-09) variant method { get, head, @@ -20,6 +22,7 @@ interface types { } /// This type corresponds to HTTP standard Related Schemes. + @since(version = 0.3.0-rc-2026-02-09) variant scheme { HTTP, HTTPS, @@ -27,18 +30,21 @@ interface types { } /// Defines the case payload type for `DNS-error` above: + @since(version = 0.3.0-rc-2026-02-09) record DNS-error-payload { rcode: option, info-code: option, } /// Defines the case payload type for `TLS-alert-received` above: + @since(version = 0.3.0-rc-2026-02-09) record TLS-alert-received-payload { alert-id: option, alert-message: option, } /// Defines the case payload type for `HTTP-response-{header,trailer}-size` above: + @since(version = 0.3.0-rc-2026-02-09) record field-size-payload { field-name: option, field-size: option, @@ -46,6 +52,7 @@ interface types { /// These cases are inspired by the IANA HTTP Proxy Error Types: /// + @since(version = 0.3.0-rc-2026-02-09) variant error-code { DNS-timeout, DNS-error(DNS-error-payload), @@ -95,6 +102,7 @@ interface types { /// This type enumerates the different kinds of errors that may occur when /// setting or appending to a `fields` resource. + @since(version = 0.3.0-rc-2026-02-09) variant header-error { /// This error indicates that a `field-name` or `field-value` was /// syntactically invalid when used with an operation that sets headers in a @@ -110,6 +118,7 @@ interface types { /// This type enumerates the different kinds of errors that may occur when /// setting fields of a `request-options` resource. + @since(version = 0.3.0-rc-2026-02-09) variant request-options-error { /// Indicates the specified field is not supported by this implementation. not-supported, @@ -122,11 +131,13 @@ interface types { /// /// Field names should always be treated as case insensitive by the `fields` /// resource for the purposes of equality checking. + @since(version = 0.3.0-rc-2026-02-09) type field-name = string; /// Field values should always be ASCII strings. However, in /// reality, HTTP implementations often have to interpret malformed values, /// so they are provided as a list of bytes. + @since(version = 0.3.0-rc-2026-02-09) type field-value = list; /// This following block defines the `fields` resource which corresponds to @@ -144,6 +155,7 @@ interface types { /// original casing used to construct or mutate the `fields` resource. The `fields` /// resource should use that original casing when serializing the fields for /// transport or when returning them from a method. + @since(version = 0.3.0-rc-2026-02-09) resource fields { /// Construct an empty HTTP Fields. /// @@ -212,12 +224,15 @@ interface types { } /// Headers is an alias for Fields. + @since(version = 0.3.0-rc-2026-02-09) type headers = fields; /// Trailers is an alias for Fields. + @since(version = 0.3.0-rc-2026-02-09) type trailers = fields; /// Represents an HTTP Request. + @since(version = 0.3.0-rc-2026-02-09) resource request { /// Construct a new `request` with a default `method` of `GET`, and /// `none` values for `path-with-query`, `scheme`, and `authority`. @@ -304,6 +319,7 @@ interface types { /// /// These timeouts are separate from any the user may use to bound an /// asynchronous call. + @since(version = 0.3.0-rc-2026-02-09) resource request-options { /// Construct a default `request-options` value. constructor(); @@ -332,9 +348,11 @@ interface types { } /// This type corresponds to the HTTP standard Status Code. + @since(version = 0.3.0-rc-2026-02-09) type status-code = u16; /// Represents an HTTP Response. + @since(version = 0.3.0-rc-2026-02-09) resource response { /// Construct a new `response`, with a default `status-code` of `200`. /// If a different `status-code` is needed, it must be set via the @@ -383,6 +401,7 @@ interface types { /// /// In `wasi:http/middleware` this interface is both exported and imported as /// the "downstream" and "upstream" directions of the middleware chain. +@since(version = 0.3.0-rc-2026-02-09) interface handler { use types.{request, response, error-code}; @@ -401,6 +420,7 @@ interface handler { /// (including WIT itself) is unable to represent a component importing two /// instances of the same interface. A `client.send` import may be linked /// directly to a `handler.handle` export to bypass the network. +@since(version = 0.3.0-rc-2026-02-09) interface client { use types.{request, response, error-code}; @@ -412,21 +432,22 @@ interface client { /// The `wasi:http/service` world captures a broad category of HTTP services /// including web applications, API servers, and proxies. It may be `include`d /// in more specific worlds such as `wasi:http/middleware`. +@since(version = 0.3.0-rc-2026-02-09) world service { - import wasi:cli/types@0.3.0-rc-2026-01-06; - import wasi:cli/stdout@0.3.0-rc-2026-01-06; - import wasi:cli/stderr@0.3.0-rc-2026-01-06; - import wasi:cli/stdin@0.3.0-rc-2026-01-06; - import wasi:clocks/types@0.3.0-rc-2026-01-06; + import wasi:cli/types@0.3.0-rc-2026-02-09; + import wasi:cli/stdout@0.3.0-rc-2026-02-09; + import wasi:cli/stderr@0.3.0-rc-2026-02-09; + import wasi:cli/stdin@0.3.0-rc-2026-02-09; + import wasi:clocks/types@0.3.0-rc-2026-02-09; import types; import client; - import wasi:clocks/monotonic-clock@0.3.0-rc-2026-01-06; - import wasi:clocks/system-clock@0.3.0-rc-2026-01-06; + import wasi:clocks/monotonic-clock@0.3.0-rc-2026-02-09; + import wasi:clocks/system-clock@0.3.0-rc-2026-02-09; @unstable(feature = clocks-timezone) - import wasi:clocks/timezone@0.3.0-rc-2026-01-06; - import wasi:random/random@0.3.0-rc-2026-01-06; - import wasi:random/insecure@0.3.0-rc-2026-01-06; - import wasi:random/insecure-seed@0.3.0-rc-2026-01-06; + import wasi:clocks/timezone@0.3.0-rc-2026-02-09; + import wasi:random/random@0.3.0-rc-2026-02-09; + import wasi:random/insecure@0.3.0-rc-2026-02-09; + import wasi:random/insecure-seed@0.3.0-rc-2026-02-09; export handler; } @@ -436,22 +457,23 @@ world service { /// Components may implement this world to allow them to participate in handler /// "chains" where a `request` flows through handlers on its way to some terminal /// `service` and corresponding `response` flows in the opposite direction. +@since(version = 0.3.0-rc-2026-02-09) world middleware { - import wasi:clocks/types@0.3.0-rc-2026-01-06; + import wasi:clocks/types@0.3.0-rc-2026-02-09; import types; import handler; - import wasi:cli/types@0.3.0-rc-2026-01-06; - import wasi:cli/stdout@0.3.0-rc-2026-01-06; - import wasi:cli/stderr@0.3.0-rc-2026-01-06; - import wasi:cli/stdin@0.3.0-rc-2026-01-06; + import wasi:cli/types@0.3.0-rc-2026-02-09; + import wasi:cli/stdout@0.3.0-rc-2026-02-09; + import wasi:cli/stderr@0.3.0-rc-2026-02-09; + import wasi:cli/stdin@0.3.0-rc-2026-02-09; import client; - import wasi:clocks/monotonic-clock@0.3.0-rc-2026-01-06; - import wasi:clocks/system-clock@0.3.0-rc-2026-01-06; + import wasi:clocks/monotonic-clock@0.3.0-rc-2026-02-09; + import wasi:clocks/system-clock@0.3.0-rc-2026-02-09; @unstable(feature = clocks-timezone) - import wasi:clocks/timezone@0.3.0-rc-2026-01-06; - import wasi:random/random@0.3.0-rc-2026-01-06; - import wasi:random/insecure@0.3.0-rc-2026-01-06; - import wasi:random/insecure-seed@0.3.0-rc-2026-01-06; + import wasi:clocks/timezone@0.3.0-rc-2026-02-09; + import wasi:random/random@0.3.0-rc-2026-02-09; + import wasi:random/insecure@0.3.0-rc-2026-02-09; + import wasi:random/insecure-seed@0.3.0-rc-2026-02-09; export handler; } diff --git a/wit/deps/random-0.3.0-rc-2026-01-06.wit b/wit/deps/random.wit similarity index 85% rename from wit/deps/random-0.3.0-rc-2026-01-06.wit rename to wit/deps/random.wit index 8bb8731..521df6e 100644 --- a/wit/deps/random-0.3.0-rc-2026-01-06.wit +++ b/wit/deps/random.wit @@ -1,10 +1,10 @@ -package wasi:random@0.3.0-rc-2026-01-06; +package wasi:random@0.3.0-rc-2026-02-09; /// The insecure-seed interface for seeding hash-map DoS resistance. /// /// It is intended to be portable at least between Unix-family platforms and /// Windows. -@since(version = 0.3.0-rc-2026-01-06) +@since(version = 0.3.0-rc-2026-02-09) interface insecure-seed { /// Return a 128-bit value that may contain a pseudo-random value. /// @@ -23,7 +23,7 @@ interface insecure-seed { /// This will likely be changed to a value import, to prevent it from being /// called multiple times and potentially used for purposes other than DoS /// protection. - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) get-insecure-seed: func() -> tuple; } @@ -31,7 +31,7 @@ interface insecure-seed { /// /// It is intended to be portable at least between Unix-family platforms and /// Windows. -@since(version = 0.3.0-rc-2026-01-06) +@since(version = 0.3.0-rc-2026-02-09) interface insecure { /// Return `len` insecure pseudo-random bytes. /// @@ -41,14 +41,14 @@ interface insecure { /// There are no requirements on the values of the returned bytes, however /// implementations are encouraged to return evenly distributed values with /// a long period. - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) get-insecure-random-bytes: func(len: u64) -> list; /// Return an insecure pseudo-random `u64` value. /// /// This function returns the same type of pseudo-random data as /// `get-insecure-random-bytes`, represented as a `u64`. - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) get-insecure-random-u64: func() -> u64; } @@ -56,7 +56,7 @@ interface insecure { /// /// It is intended to be portable at least between Unix-family platforms and /// Windows. -@since(version = 0.3.0-rc-2026-01-06) +@since(version = 0.3.0-rc-2026-02-09) interface random { /// Return `len` cryptographically-secure random or pseudo-random bytes. /// @@ -70,23 +70,23 @@ interface random { /// This function must always return fresh data. Deterministic environments /// must omit this function, rather than implementing it with deterministic /// data. - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) get-random-bytes: func(len: u64) -> list; /// Return a cryptographically-secure random or pseudo-random `u64` value. /// /// This function returns the same type of data as `get-random-bytes`, /// represented as a `u64`. - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) get-random-u64: func() -> u64; } -@since(version = 0.3.0-rc-2026-01-06) +@since(version = 0.3.0-rc-2026-02-09) world imports { - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) import random; - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) import insecure; - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) import insecure-seed; } diff --git a/wit/deps/sockets-0.3.0-rc-2026-01-06.wit b/wit/deps/sockets.wit similarity index 92% rename from wit/deps/sockets-0.3.0-rc-2026-01-06.wit rename to wit/deps/sockets.wit index d7ebe15..aa9d4d7 100644 --- a/wit/deps/sockets-0.3.0-rc-2026-01-06.wit +++ b/wit/deps/sockets.wit @@ -1,9 +1,9 @@ -package wasi:sockets@0.3.0-rc-2026-01-06; +package wasi:sockets@0.3.0-rc-2026-02-09; -@since(version = 0.3.0-rc-2026-01-06) +@since(version = 0.3.0-rc-2026-02-09) interface types { - @since(version = 0.3.0-rc-2026-01-06) - use wasi:clocks/types@0.3.0-rc-2026-01-06.{duration}; + @since(version = 0.3.0-rc-2026-02-09) + use wasi:clocks/types@0.3.0-rc-2026-02-09.{duration}; /// Error codes. /// @@ -16,7 +16,7 @@ interface types { /// - `out-of-memory` /// /// See each individual API for what the POSIX equivalents are. They sometimes differ per API. - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) enum error-code { /// Unknown error unknown, @@ -57,7 +57,7 @@ interface types { datagram-too-large, } - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) enum ip-address-family { /// Similar to `AF_INET` in POSIX. ipv4, @@ -65,19 +65,19 @@ interface types { ipv6, } - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) type ipv4-address = tuple; - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) type ipv6-address = tuple; - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) variant ip-address { ipv4(ipv4-address), ipv6(ipv6-address), } - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) record ipv4-socket-address { /// sin_port port: u16, @@ -85,7 +85,7 @@ interface types { address: ipv4-address, } - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) record ipv6-socket-address { /// sin6_port port: u16, @@ -97,7 +97,7 @@ interface types { scope-id: u32, } - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) variant ip-socket-address { ipv4(ipv4-socket-address), ipv6(ipv6-socket-address), @@ -122,7 +122,7 @@ interface types { /// In addition to the general error codes documented on the /// `types::error-code` type, TCP socket methods may always return /// `error(invalid-state)` when in the `closed` state. - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) resource tcp-socket { /// Create a new TCP socket. /// @@ -138,7 +138,7 @@ interface types { /// - /// - /// - - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) create: static func(address-family: ip-address-family) -> result; /// Bind the socket to the provided IP address and port. /// @@ -171,7 +171,7 @@ interface types { /// - /// - /// - - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) bind: func(local-address: ip-socket-address) -> result<_, error-code>; /// Connect to a remote endpoint. /// @@ -202,7 +202,7 @@ interface types { /// - /// - /// - - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) connect: async func(remote-address: ip-socket-address) -> result<_, error-code>; /// Start listening and return a stream of new inbound connections. /// @@ -259,6 +259,12 @@ interface types { /// In either case, the stream returned by this `listen` method remains /// operational. /// + /// WASI requires `listen` to perform an implicit bind if the socket + /// has not already been bound. Not all platforms (notably Windows) + /// exhibit this behavior out of the box. On platforms that require it, + /// the WASI implementation can emulate this behavior by performing + /// the bind itself if the guest hasn't already done so. + /// /// # References /// - /// - @@ -268,7 +274,7 @@ interface types { /// - /// - /// - - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) listen: func() -> result, error-code>; /// Transmit data to peer. /// @@ -290,8 +296,8 @@ interface types { /// - /// - /// - - @since(version = 0.3.0-rc-2026-01-06) - send: async func(data: stream) -> result<_, error-code>; + @since(version = 0.3.0-rc-2026-02-09) + send: func(data: stream) -> future>; /// Read data from peer. /// /// This function returns a `stream` which provides the data received from the @@ -323,7 +329,7 @@ interface types { /// - /// - /// - - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) receive: func() -> tuple, future>>; /// Get the bound local address. /// @@ -341,7 +347,7 @@ interface types { /// - /// - /// - - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) get-local-address: func() -> result; /// Get the remote address. /// @@ -353,19 +359,19 @@ interface types { /// - /// - /// - - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) get-remote-address: func() -> result; /// Whether the socket is in the `listening` state. /// /// Equivalent to the SO_ACCEPTCONN socket option. - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) get-is-listening: func() -> bool; /// Whether this is a IPv4 or IPv6 socket. /// /// This is the value passed to the constructor. /// /// Equivalent to the SO_DOMAIN socket option. - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) get-address-family: func() -> ip-address-family; /// Hints the desired listen queue size. Implementations are free to ignore this. /// @@ -376,7 +382,7 @@ interface types { /// - `not-supported`: (set) The platform does not support changing the backlog size after the initial listen. /// - `invalid-argument`: (set) The provided value was 0. /// - `invalid-state`: (set) The socket is in the `connecting` or `connected` state. - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) set-listen-backlog-size: func(value: u64) -> result<_, error-code>; /// Enables or disables keepalive. /// @@ -387,9 +393,9 @@ interface types { /// These properties can be configured while `keep-alive-enabled` is false, but only come into effect when `keep-alive-enabled` is true. /// /// Equivalent to the SO_KEEPALIVE socket option. - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) get-keep-alive-enabled: func() -> result; - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) set-keep-alive-enabled: func(value: bool) -> result<_, error-code>; /// Amount of time the connection has to be idle before TCP starts sending keepalive packets. /// @@ -401,9 +407,9 @@ interface types { /// /// # Typical errors /// - `invalid-argument`: (set) The provided value was 0. - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) get-keep-alive-idle-time: func() -> result; - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) set-keep-alive-idle-time: func(value: duration) -> result<_, error-code>; /// The time between keepalive packets. /// @@ -415,9 +421,9 @@ interface types { /// /// # Typical errors /// - `invalid-argument`: (set) The provided value was 0. - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) get-keep-alive-interval: func() -> result; - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) set-keep-alive-interval: func(value: duration) -> result<_, error-code>; /// The maximum amount of keepalive packets TCP should send before aborting the connection. /// @@ -429,9 +435,9 @@ interface types { /// /// # Typical errors /// - `invalid-argument`: (set) The provided value was 0. - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) get-keep-alive-count: func() -> result; - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) set-keep-alive-count: func(value: u32) -> result<_, error-code>; /// Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options. /// @@ -439,9 +445,9 @@ interface types { /// /// # Typical errors /// - `invalid-argument`: (set) The TTL value must be 1 or higher. - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) get-hop-limit: func() -> result; - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) set-hop-limit: func(value: u8) -> result<_, error-code>; /// The kernel buffer space reserved for sends/receives on this socket. /// @@ -453,18 +459,18 @@ interface types { /// /// # Typical errors /// - `invalid-argument`: (set) The provided value was 0. - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) get-receive-buffer-size: func() -> result; - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) set-receive-buffer-size: func(value: u64) -> result<_, error-code>; - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) get-send-buffer-size: func() -> result; - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) set-send-buffer-size: func(value: u64) -> result<_, error-code>; } /// A UDP socket handle. - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) resource udp-socket { /// Create a new UDP socket. /// @@ -480,7 +486,7 @@ interface types { /// - /// - /// - - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) create: static func(address-family: ip-address-family) -> result; /// Bind the socket to the provided IP address and port. /// @@ -500,7 +506,7 @@ interface types { /// - /// - /// - - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) bind: func(local-address: ip-socket-address) -> result<_, error-code>; /// Associate this socket with a specific peer address. /// @@ -538,7 +544,7 @@ interface types { /// - /// - /// - - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) connect: func(remote-address: ip-socket-address) -> result<_, error-code>; /// Dissociate this socket from its peer address. /// @@ -555,7 +561,7 @@ interface types { /// - /// - /// - - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) disconnect: func() -> result<_, error-code>; /// Send a message on the socket to a particular peer. /// @@ -588,7 +594,7 @@ interface types { /// - /// - /// - - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) send: async func(data: list, remote-address: option) -> result<_, error-code>; /// Receive a message on the socket. /// @@ -613,7 +619,7 @@ interface types { /// - /// - /// - - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) receive: async func() -> result, ip-socket-address>, error-code>; /// Get the current bound address. /// @@ -631,7 +637,7 @@ interface types { /// - /// - /// - - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) get-local-address: func() -> result; /// Get the address the socket is currently "connected" to. /// @@ -643,14 +649,14 @@ interface types { /// - /// - /// - - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) get-remote-address: func() -> result; /// Whether this is a IPv4 or IPv6 socket. /// /// This is the value passed to the constructor. /// /// Equivalent to the SO_DOMAIN socket option. - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) get-address-family: func() -> ip-address-family; /// Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options. /// @@ -658,9 +664,9 @@ interface types { /// /// # Typical errors /// - `invalid-argument`: (set) The TTL value must be 1 or higher. - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) get-unicast-hop-limit: func() -> result; - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) set-unicast-hop-limit: func(value: u8) -> result<_, error-code>; /// The kernel buffer space reserved for sends/receives on this socket. /// @@ -672,24 +678,24 @@ interface types { /// /// # Typical errors /// - `invalid-argument`: (set) The provided value was 0. - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) get-receive-buffer-size: func() -> result; - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) set-receive-buffer-size: func(value: u64) -> result<_, error-code>; - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) get-send-buffer-size: func() -> result; - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) set-send-buffer-size: func(value: u64) -> result<_, error-code>; } } -@since(version = 0.3.0-rc-2026-01-06) +@since(version = 0.3.0-rc-2026-02-09) interface ip-name-lookup { - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) use types.{ip-address}; /// Lookup error codes. - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) enum error-code { /// Unknown error unknown, @@ -737,16 +743,16 @@ interface ip-name-lookup { /// - /// - /// - - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) resolve-addresses: async func(name: string) -> result, error-code>; } -@since(version = 0.3.0-rc-2026-01-06) +@since(version = 0.3.0-rc-2026-02-09) world imports { - @since(version = 0.3.0-rc-2026-01-06) - import wasi:clocks/types@0.3.0-rc-2026-01-06; - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) + import wasi:clocks/types@0.3.0-rc-2026-02-09; + @since(version = 0.3.0-rc-2026-02-09) import types; - @since(version = 0.3.0-rc-2026-01-06) + @since(version = 0.3.0-rc-2026-02-09) import ip-name-lookup; } From 59db3f0d37f980af64824b5274bd9047c99101c5 Mon Sep 17 00:00:00 2001 From: Roman Volosatovs Date: Thu, 19 Feb 2026 12:39:02 +0100 Subject: [PATCH 7/7] add `tls-p3` example and test Signed-off-by: Roman Volosatovs --- .gitignore | 1 + examples/tls-p3/README.md | 38 +++++++++++++++ examples/tls-p3/app.py | 76 +++++++++++++++++++++++++++++ tests/bindings.rs | 19 ++++++++ tests/componentize.rs | 42 ++++++++++++++++ wit/deps/tls-0.3.0-draft/client.wit | 22 +++++++++ wit/deps/tls-0.3.0-draft/types.wit | 5 ++ wit/deps/tls-0.3.0-draft/world.wit | 6 +++ wit/tls.wit | 4 ++ 9 files changed, 213 insertions(+) create mode 100644 examples/tls-p3/README.md create mode 100644 examples/tls-p3/app.py create mode 100644 wit/deps/tls-0.3.0-draft/client.wit create mode 100644 wit/deps/tls-0.3.0-draft/types.wit create mode 100644 wit/deps/tls-0.3.0-draft/world.wit create mode 100644 wit/tls.wit diff --git a/.gitignore b/.gitignore index 430b6e3..af2286b 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,7 @@ examples/http/poll_loop.py examples/tcp-p3/tcp.wasm examples/tcp/tcp.wasm examples/tcp/command +examples/tls-p3/tls.wasm examples/cli-p3/cli.wasm examples/cli/cli.wasm examples/cli/command diff --git a/examples/tls-p3/README.md b/examples/tls-p3/README.md new file mode 100644 index 0000000..09f9122 --- /dev/null +++ b/examples/tls-p3/README.md @@ -0,0 +1,38 @@ +# Example: `tls-p3` + +This is an example of how to use [componentize-py] and [Wasmtime] to build and +run a Python-based TLS client component targetting version `0.3.0-rc-2026-02-09` +of the [wasi-cli] `command` world with [wasi-tls] and [wasi-sockets] support. + +[componentize-py]: https://github.com/bytecodealliance/componentize-py +[Wasmtime]: https://github.com/bytecodealliance/wasmtime +[wasi-cli]: https://github.com/WebAssembly/WASI/tree/v0.3.0-rc-2026-02-09/proposals/cli/wit-0.3.0-draft +[wasi-sockets]: https://github.com/WebAssembly/WASI/tree/v0.3.0-rc-2026-02-09/proposals/sockets/wit-0.3.0-draft +[wasi-tls]: https://github.com/WebAssembly/wasi-tls + +## Prerequisites + +* `Wasmtime` 43.0.0 +* `componentize-py` 0.21.0 + +Below, we use [Rust](https://rustup.rs/)'s `cargo` to install `Wasmtime`. If +you don't have `cargo`, you can download and install from +https://github.com/bytecodealliance/wasmtime/releases/tag/v43.0.0. + +``` +cargo install --version 43.0.0 wasmtime-cli +pip install componentize-py==0.21.0 +``` + +## Running the demo + +``` +componentize-py -d ../../wit -w tls-p3 componentize app -o tls.wasm +wasmtime run -Sp3,inherit-network,tls,allow-ip-name-lookup -Wcomponent-model-async tls.wasm +``` + +For example, to connect to `api.github.com` over TLS: + +``` +wasmtime run -Sp3,inherit-network,tls,allow-ip-name-lookup -Wcomponent-model-async tls.wasm api.github.com +``` diff --git a/examples/tls-p3/app.py b/examples/tls-p3/app.py new file mode 100644 index 0000000..ca6aead --- /dev/null +++ b/examples/tls-p3/app.py @@ -0,0 +1,76 @@ +import sys +import asyncio +import wit_world +from wit_world import exports +from wit_world.imports.wasi_sockets_types import ( + TcpSocket, + IpSocketAddress_Ipv4, + IpSocketAddress_Ipv6, + Ipv4SocketAddress, + Ipv6SocketAddress, + IpAddressFamily, + IpAddress_Ipv4, + IpAddress_Ipv6, +) +from wit_world.imports.ip_name_lookup import resolve_addresses +from wit_world.imports.client import Connector + + +class Run(exports.Run): + async def run(self) -> None: + args = sys.argv[1:] + if len(args) != 1: + print("usage: tls-p3 ", file=sys.stderr) + exit(-1) + + server_name = args[0] + await send_and_receive(server_name) + + +async def send_and_receive(server_name: str) -> None: + port = 443 + addresses = await resolve_addresses(server_name) + address = addresses[0] + + if isinstance(address, IpAddress_Ipv4): + family = IpAddressFamily.IPV4 + sock_addr: IpSocketAddress_Ipv4 | IpSocketAddress_Ipv6 = IpSocketAddress_Ipv4( + Ipv4SocketAddress(port=port, address=address.value) + ) + else: + family = IpAddressFamily.IPV6 + sock_addr = IpSocketAddress_Ipv6( + Ipv6SocketAddress(port=port, flow_info=0, address=address.value, scope_id=0) + ) + + sock = TcpSocket.create(family) + await sock.connect(sock_addr) + + tls = Connector() + + data_send_tx, data_send_rx = wit_world.byte_stream() + tls_send_rx, tls_send_fut = tls.send(data_send_rx) + sock_send_fut = sock.send(tls_send_rx) + + tls_recv_rx, sock_recv_fut = sock.receive() + data_recv_rx, tls_recv_fut = tls.receive(tls_recv_rx) + + async def write() -> None: + with data_send_tx: + await data_send_tx.write_all(f"GET / HTTP/1.1\r\nHost: {server_name}\r\nUser-Agent: wasmtime-wasi-rust\r\nConnection: close\r\n\r\n".encode()) + + async def read() -> None: + with data_recv_rx: + while not data_recv_rx.writer_dropped: + buf = await data_recv_rx.read(1024) + sys.stdout.buffer.write(buf) + + await asyncio.gather( + Connector.connect(tls, server_name), + write(), + read(), + sock_send_fut.read(), + sock_recv_fut.read(), + tls_send_fut.read(), + tls_recv_fut.read(), + ) diff --git a/tests/bindings.rs b/tests/bindings.rs index f40b482..fadcf85 100644 --- a/tests/bindings.rs +++ b/tests/bindings.rs @@ -190,6 +190,25 @@ fn lint_tcp_p3_bindings() -> anyhow::Result<()> { Ok(()) } +#[test] +fn lint_tls_p3_bindings() -> anyhow::Result<()> { + let dir = tempfile::tempdir()?; + fs_extra::copy_items( + &["./examples/tls-p3", "./wit"], + dir.path(), + &CopyOptions::new(), + )?; + let path = dir.path().join("tls-p3"); + + generate_bindings(&path, "tls-p3")?; + + assert!(predicate::path::is_dir().eval(&path.join("wit_world"))); + + mypy_check(&path, ["--strict", "-m", "app"]); + + Ok(()) +} + fn generate_bindings(path: &Path, world: &str) -> Result { Ok(cargo::cargo_bin_cmd!("componentize-py") .current_dir(path) diff --git a/tests/componentize.rs b/tests/componentize.rs index 215f354..bcf2bf4 100644 --- a/tests/componentize.rs +++ b/tests/componentize.rs @@ -303,6 +303,48 @@ fn test_tcp_example(name: &str, world: &str) -> anyhow::Result<()> { Ok(()) } +#[test] +fn tls_p3_example() -> anyhow::Result<()> { + let dir = tempfile::tempdir()?; + fs_extra::copy_items( + &["./examples/tls-p3", "./wit"], + dir.path(), + &CopyOptions::new(), + )?; + let path = dir.path().join("tls-p3"); + + cargo::cargo_bin_cmd!("componentize-py") + .current_dir(&path) + .args([ + "-d", + "../wit", + "-w", + "tls-p3", + "componentize", + "app", + "-o", + "tls.wasm", + ]) + .assert() + .success() + .stdout("Component built successfully\n"); + + Command::new("wasmtime") + .current_dir(&path) + .args([ + "run", + "-Sp3,inherit-network,tls,allow-ip-name-lookup", + "-Wcomponent-model-async", + "tls.wasm", + "api.github.com", + ]) + .assert() + .success() + .stdout(predicate::str::starts_with("HTTP/1.1 200 OK")); + + Ok(()) +} + fn retry(mut func: impl FnMut() -> anyhow::Result) -> anyhow::Result { let times = 10; for i in 0..times { diff --git a/wit/deps/tls-0.3.0-draft/client.wit b/wit/deps/tls-0.3.0-draft/client.wit new file mode 100644 index 0000000..8f282db --- /dev/null +++ b/wit/deps/tls-0.3.0-draft/client.wit @@ -0,0 +1,22 @@ +interface client { + use types.{error}; + + resource connector { + constructor(); + + /// Set up the encryption stream transform. + /// This takes an unprotected `cleartext` application data stream and + /// returns an encrypted data stream, ready to be sent out over the network. + /// Closing the `cleartext` stream will cause a `close_notify` packet to be emitted on the returned output stream. + send: func(cleartext: stream) -> tuple, future>>; + + /// Set up the decryption stream transform. + /// This takes an encrypted data stream, as received via e.g. the network, + /// and returns a decrypted application data stream. + receive: func(ciphertext: stream) -> tuple, future>>; + + /// Perform the handshake. + /// The `send` & `receive` streams must be set up before calling this method. + connect: static async func(this: connector, server-name: string) -> result<_, error>; + } +} diff --git a/wit/deps/tls-0.3.0-draft/types.wit b/wit/deps/tls-0.3.0-draft/types.wit new file mode 100644 index 0000000..fc6c4b1 --- /dev/null +++ b/wit/deps/tls-0.3.0-draft/types.wit @@ -0,0 +1,5 @@ +interface types { + resource error { + to-debug-string: func() -> string; + } +} diff --git a/wit/deps/tls-0.3.0-draft/world.wit b/wit/deps/tls-0.3.0-draft/world.wit new file mode 100644 index 0000000..599d496 --- /dev/null +++ b/wit/deps/tls-0.3.0-draft/world.wit @@ -0,0 +1,6 @@ +package wasi:tls@0.3.0-draft; + +world imports { + import client; + import types; +} diff --git a/wit/tls.wit b/wit/tls.wit new file mode 100644 index 0000000..7a85dd1 --- /dev/null +++ b/wit/tls.wit @@ -0,0 +1,4 @@ +world tls-p3 { + include wasi:cli/command@0.3.0-rc-2026-02-09; + include wasi:tls/imports@0.3.0-draft; +}