diff --git a/.github/scripts/validate-proposals.js b/.github/scripts/validate-proposals.js
index a6a85b59..baf486ba 100644
--- a/.github/scripts/validate-proposals.js
+++ b/.github/scripts/validate-proposals.js
@@ -71,6 +71,14 @@ for (const { proposal, version } of toValidate) {
try {
console.log(` Path: ${witDir}`);
+ // Skip when the wit directory no longer exists. A PR that removes a wit
+ // tree (e.g. dropping the 0.3 drafts on this branch) still surfaces its
+ // deleted files in the changed-file list, but there is nothing to validate.
+ if (!fs.existsSync(witDir)) {
+ console.log(` Skipping ${proposal} v${version}: ${witDir} no longer exists (removed)`);
+ continue;
+ }
+
// Check wit-deps lock if deps.toml exists
if (fs.existsSync(`${witDir}/deps.toml`)) {
console.log(' Checking dependencies...');
diff --git a/README.md b/README.md
index 27365e25..eeb28828 100644
--- a/README.md
+++ b/README.md
@@ -1,41 +1,25 @@
-
+# WASI 0.2 docs
-# WebAssembly System Interface
+This branch documents the WASI 0.2 iteration of WASI, also known as Preview 2.
-[](https://doi.org/10.5281/zenodo.4323447)
-
+WASI 0.2 is a modular collection of APIs defined using the [Wit IDL]. It
+incorporates many of the lessons learned from [Preview 1], including support
+for a wider range of source languages, modularity, a more expressive type
+system, virtualizability, and more. It covers the `wasi:cli`, `wasi:clocks`,
+`wasi:filesystem`, `wasi:http`, `wasi:io`, `wasi:random`, and `wasi:sockets`
+interfaces at their 0.2.x versions.
-The WebAssembly System Interface (WASI) is a set of APIs for WASI being
-developed for eventual standardization by the WASI Subgroup, which is a
-subgroup of the WebAssembly Community Group.
+Development of WASI now continues in [WASI 0.3] (Preview 3) on the `main`
+branch, which supplants Preview 2 by providing integrated and composable
+`async` functionality. Implementations may continue to support WASI 0.2,
+either by implementing WASI 0.3 alongside it, or by virtualizing
+(polyfilling) 0.2 in terms of 0.3.
-WASI started with launching what is now called [Preview 1], an API using
-the witx IDL, and it is now widely used. Its major influences are POSIX and
-CloudABI.
-
-[WASI Preview 2] is now stable, and is a modular collection of
-APIs defined with the [Wit IDL], and it incorporates many of the lessons
-learned from Preview 1, including adding support for a wider range of
-source languages, modularity, a more expressive type system,
-virtualizability, and more.
+The proposals included in WASI 0.2 are documented under [`proposals/`](proposals),
+and the tagged interface snapshots are under [`specifications/`](specifications).
+See [Preview2.md](docs/Preview2.md) for the Preview 2 inclusion criteria and
+overview.
[Preview 1]: https://github.com/WebAssembly/WASI/tree/wasi-0.1
-[WASI Preview 2]: https://github.com/WebAssembly/WASI/blob/main/docs/Preview2.md
+[WASI 0.3]: https://github.com/WebAssembly/WASI/tree/main
[Wit IDL]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/WIT.md
-
-## Find the APIs
-
-Development of each API happens in its own repo, which you can access
-from the [proposals list](docs/Proposals.md).
-
-This repo is for general discussion, as well as documenting how we work
-and high-level goals.
-
-## Propose a new API
-
-If you would like to create a new proposal, get started with our
-[Contributing guide](CONTRIBUTING.md).
-
-All new API proposals should use the new format and the new repo structure that is shown in the [proposal template](https://github.com/WebAssembly/wasi-proposal-template).
-
-See the [Wit in WASI](docs/WitInWasi.md) document for more information about using Wit for WASI proposals.
diff --git a/proposals/cli/wit-0.3.0-draft/command.wit b/proposals/cli/wit-0.3.0-draft/command.wit
deleted file mode 100644
index 12cdb104..00000000
--- a/proposals/cli/wit-0.3.0-draft/command.wit
+++ /dev/null
@@ -1,10 +0,0 @@
-package wasi:cli@0.3.0-rc-2026-03-15;
-
-@since(version = 0.3.0-rc-2026-03-15)
-world command {
- @since(version = 0.3.0-rc-2026-03-15)
- include imports;
-
- @since(version = 0.3.0-rc-2026-03-15)
- export run;
-}
diff --git a/proposals/cli/wit-0.3.0-draft/deps.lock b/proposals/cli/wit-0.3.0-draft/deps.lock
deleted file mode 100644
index c3eb59cc..00000000
--- a/proposals/cli/wit-0.3.0-draft/deps.lock
+++ /dev/null
@@ -1,19 +0,0 @@
-[clocks]
-path = "../../clocks/wit-0.3.0-draft"
-sha256 = "a8702c42e23c22a458207d0efa38cc1b64c8c6299b0e3152aa84327a7c1c952b"
-sha512 = "da69a5d498b6afe0facf77ec3d21f6e8a2a4d4661ebfec310a2ce6f943cb01f92131a746765648febf96563ddac788e9f218941d3db2b905f3746dc8e05cf54d"
-
-[filesystem]
-path = "../../filesystem/wit-0.3.0-draft"
-sha256 = "d9db43c43b4e09899a9e54c76544eef17439fb42e2f730ed96d40eac994b0dfd"
-sha512 = "3b7a1bef322ba36b1e9df768d740e4238f58d3c15d3e4c51fe899b93cf1e6641a79b5a8665ae71d38a80e1e0f852b30b023ecf318f4cbeacb36dd1ae0ac7e37b"
-
-[random]
-path = "../../random/wit-0.3.0-draft"
-sha256 = "d44de4e427505fdfd584a23479dba5899ad80aa8e174dc0528df840db8ae9a43"
-sha512 = "6b08b32a197aee74076d0cdca6a09f78da9040eedd9ab3f64e5f14901ad5a0c5bbc592ad46a0d575dc6705249b3e10a413e09835616f753788aa598af605c776"
-
-[sockets]
-path = "../../sockets/wit-0.3.0-draft"
-sha256 = "ee64b45d3826b6ff2cd27f6ac24fe23c41e65ba4d155294671dc7d0a0b47ddfe"
-sha512 = "dd797ab47b899aee52799f797497fa286f5e217307e8a451a7ba89bd05b7b9bfe2a6e0cf39c0e609ed4c76ed102487adff6e73156340f14baf70a9cc9e48ecbc"
diff --git a/proposals/cli/wit-0.3.0-draft/deps.toml b/proposals/cli/wit-0.3.0-draft/deps.toml
deleted file mode 100644
index e66b8755..00000000
--- a/proposals/cli/wit-0.3.0-draft/deps.toml
+++ /dev/null
@@ -1,4 +0,0 @@
-clocks = "../../clocks/wit-0.3.0-draft"
-sockets = "../../sockets/wit-0.3.0-draft"
-random = "../../random/wit-0.3.0-draft"
-filesystem = "../../filesystem/wit-0.3.0-draft"
diff --git a/proposals/cli/wit-0.3.0-draft/environment.wit b/proposals/cli/wit-0.3.0-draft/environment.wit
deleted file mode 100644
index 7abda6b6..00000000
--- a/proposals/cli/wit-0.3.0-draft/environment.wit
+++ /dev/null
@@ -1,22 +0,0 @@
-@since(version = 0.3.0-rc-2026-03-15)
-interface environment {
- /// Get the POSIX-style environment variables.
- ///
- /// Each environment variable is provided as a pair of string variable names
- /// and string value.
- ///
- /// 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-03-15)
- get-environment: func() -> list>;
-
- /// Get the POSIX-style arguments to the program.
- @since(version = 0.3.0-rc-2026-03-15)
- 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-03-15)
- get-initial-cwd: func() -> option;
-}
diff --git a/proposals/cli/wit-0.3.0-draft/exit.wit b/proposals/cli/wit-0.3.0-draft/exit.wit
deleted file mode 100644
index 24e337d5..00000000
--- a/proposals/cli/wit-0.3.0-draft/exit.wit
+++ /dev/null
@@ -1,17 +0,0 @@
-@since(version = 0.3.0-rc-2026-03-15)
-interface exit {
- /// Exit the current instance and any linked instances.
- @since(version = 0.3.0-rc-2026-03-15)
- exit: func(status: result);
-
- /// Exit the current instance and any linked instances, reporting the
- /// specified status code to the host.
- ///
- /// The meaning of the code depends on the context, with 0 usually meaning
- /// "success", and other values indicating various types of failure.
- ///
- /// This function does not return; the effect is analogous to a trap, but
- /// without the connotation that something bad has happened.
- @since(version = 0.3.0-rc-2026-03-15)
- exit-with-code: func(status-code: u8);
-}
diff --git a/proposals/cli/wit-0.3.0-draft/imports.wit b/proposals/cli/wit-0.3.0-draft/imports.wit
deleted file mode 100644
index 398b632d..00000000
--- a/proposals/cli/wit-0.3.0-draft/imports.wit
+++ /dev/null
@@ -1,34 +0,0 @@
-package wasi:cli@0.3.0-rc-2026-03-15;
-
-@since(version = 0.3.0-rc-2026-03-15)
-world imports {
- @since(version = 0.3.0-rc-2026-03-15)
- include wasi:clocks/imports@0.3.0-rc-2026-03-15;
- @since(version = 0.3.0-rc-2026-03-15)
- include wasi:filesystem/imports@0.3.0-rc-2026-03-15;
- @since(version = 0.3.0-rc-2026-03-15)
- include wasi:sockets/imports@0.3.0-rc-2026-03-15;
- @since(version = 0.3.0-rc-2026-03-15)
- include wasi:random/imports@0.3.0-rc-2026-03-15;
-
- @since(version = 0.3.0-rc-2026-03-15)
- import environment;
- @since(version = 0.3.0-rc-2026-03-15)
- import exit;
- @since(version = 0.3.0-rc-2026-03-15)
- import stdin;
- @since(version = 0.3.0-rc-2026-03-15)
- import stdout;
- @since(version = 0.3.0-rc-2026-03-15)
- import stderr;
- @since(version = 0.3.0-rc-2026-03-15)
- import terminal-input;
- @since(version = 0.3.0-rc-2026-03-15)
- import terminal-output;
- @since(version = 0.3.0-rc-2026-03-15)
- import terminal-stdin;
- @since(version = 0.3.0-rc-2026-03-15)
- import terminal-stdout;
- @since(version = 0.3.0-rc-2026-03-15)
- import terminal-stderr;
-}
diff --git a/proposals/cli/wit-0.3.0-draft/run.wit b/proposals/cli/wit-0.3.0-draft/run.wit
deleted file mode 100644
index d6583109..00000000
--- a/proposals/cli/wit-0.3.0-draft/run.wit
+++ /dev/null
@@ -1,6 +0,0 @@
-@since(version = 0.3.0-rc-2026-03-15)
-interface run {
- /// Run the program.
- @since(version = 0.3.0-rc-2026-03-15)
- run: async func() -> result;
-}
diff --git a/proposals/cli/wit-0.3.0-draft/stdio.wit b/proposals/cli/wit-0.3.0-draft/stdio.wit
deleted file mode 100644
index a46ee6da..00000000
--- a/proposals/cli/wit-0.3.0-draft/stdio.wit
+++ /dev/null
@@ -1,65 +0,0 @@
-@since(version = 0.3.0-rc-2026-03-15)
-interface types {
- @since(version = 0.3.0-rc-2026-03-15)
- enum error-code {
- /// Input/output error
- io,
- /// Invalid or incomplete multibyte or wide character
- illegal-byte-sequence,
- /// Broken pipe
- pipe,
- }
-}
-
-@since(version = 0.3.0-rc-2026-03-15)
-interface stdin {
- use types.{error-code};
-
- /// Return a stream for reading from stdin.
- ///
- /// This function returns a stream which provides data read from stdin,
- /// and a future to signal read results.
- ///
- /// If the stream's readable end is dropped the future will resolve to success.
- ///
- /// If the stream's writable end is dropped the future will either resolve to
- /// success if stdin was closed by the writer or to an error-code if reading
- /// failed for some other reason.
- ///
- /// Multiple streams may be active at the same time. The behavior of concurrent
- /// reads is implementation-specific.
- @since(version = 0.3.0-rc-2026-03-15)
- read-via-stream: func() -> tuple, future>>;
-}
-
-@since(version = 0.3.0-rc-2026-03-15)
-interface stdout {
- use types.{error-code};
-
- /// Write the given stream to stdout.
- ///
- /// If the stream's writable end is dropped this function will either return
- /// success once the entire contents of the stream have been written or an
- /// error-code representing a failure.
- ///
- /// 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-03-15)
- write-via-stream: func(data: stream) -> future>;
-}
-
-@since(version = 0.3.0-rc-2026-03-15)
-interface stderr {
- use types.{error-code};
-
- /// Write the given stream to stderr.
- ///
- /// If the stream's writable end is dropped this function will either return
- /// success once the entire contents of the stream have been written or an
- /// error-code representing a failure.
- ///
- /// 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-03-15)
- write-via-stream: func(data: stream) -> future>;
-}
diff --git a/proposals/cli/wit-0.3.0-draft/terminal.wit b/proposals/cli/wit-0.3.0-draft/terminal.wit
deleted file mode 100644
index 3ae61915..00000000
--- a/proposals/cli/wit-0.3.0-draft/terminal.wit
+++ /dev/null
@@ -1,62 +0,0 @@
-/// Terminal input.
-///
-/// 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-03-15)
-interface terminal-input {
- /// The input side of a terminal.
- @since(version = 0.3.0-rc-2026-03-15)
- resource terminal-input;
-}
-
-/// Terminal output.
-///
-/// 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-03-15)
-interface terminal-output {
- /// The output side of a terminal.
- @since(version = 0.3.0-rc-2026-03-15)
- resource terminal-output;
-}
-
-/// An interface providing an optional `terminal-input` for stdin as a
-/// link-time authority.
-@since(version = 0.3.0-rc-2026-03-15)
-interface terminal-stdin {
- @since(version = 0.3.0-rc-2026-03-15)
- 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-03-15)
- 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-03-15)
-interface terminal-stdout {
- @since(version = 0.3.0-rc-2026-03-15)
- 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-03-15)
- 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-03-15)
-interface terminal-stderr {
- @since(version = 0.3.0-rc-2026-03-15)
- 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-03-15)
- get-terminal-stderr: func() -> option;
-}
diff --git a/proposals/clocks/wit-0.3.0-draft/monotonic-clock.wit b/proposals/clocks/wit-0.3.0-draft/monotonic-clock.wit
deleted file mode 100644
index 786ae103..00000000
--- a/proposals/clocks/wit-0.3.0-draft/monotonic-clock.wit
+++ /dev/null
@@ -1,48 +0,0 @@
-package wasi:clocks@0.3.0-rc-2026-03-15;
-/// WASI Monotonic Clock is a clock API intended to let users measure elapsed
-/// time.
-///
-/// It is intended to be portable at least between Unix-family platforms and
-/// Windows.
-///
-/// 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-03-15)
-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-03-15)
- type mark = u64;
-
- /// Read the current value of the clock.
- ///
- /// The clock is monotonic, therefore calling this function repeatedly will
- /// produce a sequence of non-decreasing values.
- ///
- /// For completeness, this function traps if it's not possible to represent
- /// 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-03-15)
- 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-03-15)
- get-resolution: func() -> duration;
-
- /// Wait until the specified mark has occurred.
- @since(version = 0.3.0-rc-2026-03-15)
- wait-until: async func(
- when: mark,
- );
-
- /// Wait for the specified duration to elapse.
- @since(version = 0.3.0-rc-2026-03-15)
- wait-for: async func(
- how-long: duration,
- );
-}
diff --git a/proposals/clocks/wit-0.3.0-draft/system-clock.wit b/proposals/clocks/wit-0.3.0-draft/system-clock.wit
deleted file mode 100644
index f8c16611..00000000
--- a/proposals/clocks/wit-0.3.0-draft/system-clock.wit
+++ /dev/null
@@ -1,51 +0,0 @@
-package wasi:clocks@0.3.0-rc-2026-03-15;
-/// WASI System Clock is a clock API intended to let users query the current
-/// time. The clock is not necessarily monotonic as it may be reset.
-///
-/// It is intended to be portable at least between Unix-family platforms and
-/// Windows.
-///
-/// External references may be reset, so this clock is not necessarily
-/// 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-03-15)
-interface system-clock {
- use types.{duration};
-
- /// An "instant", or "exact time", is a point in time without regard to any
- /// time zone: just the time since a particular external reference point,
- /// often called an "epoch".
- ///
- /// Here, the epoch is 1970-01-01T00:00:00Z, also known as
- /// [POSIX's Seconds Since the Epoch], also known as [Unix Time].
- ///
- /// Note that even if the seconds field is negative, incrementing
- /// nanoseconds always represents moving forwards in time.
- /// For example, `{ -1 seconds, 999999999 nanoseconds }` represents the
- /// instant one nanosecond before the epoch.
- /// For more on various different ways to represent time, see
- /// https://tc39.es/proposal-temporal/docs/timezone.html
- ///
- /// [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-03-15)
- record instant {
- seconds: s64,
- nanoseconds: u32,
- }
-
- /// Read the current value of the clock.
- ///
- /// This clock is not monotonic, therefore calling this function repeatedly
- /// 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-03-15)
- 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-03-15)
- get-resolution: func() -> duration;
-}
diff --git a/proposals/clocks/wit-0.3.0-draft/timezone.wit b/proposals/clocks/wit-0.3.0-draft/timezone.wit
deleted file mode 100644
index 8b2e3e83..00000000
--- a/proposals/clocks/wit-0.3.0-draft/timezone.wit
+++ /dev/null
@@ -1,46 +0,0 @@
-package wasi:clocks@0.3.0-rc-2026-03-15;
-
-@unstable(feature = clocks-timezone)
-interface timezone {
- @unstable(feature = clocks-timezone)
- use system-clock.{instant};
-
- /// Return the IANA identifier of the currently configured timezone. This
- /// should be an identifier from the IANA Time Zone Database.
- ///
- /// For displaying to a user, the identifier should be converted into a
- /// localized name by means of an internationalization API.
- ///
- /// If the implementation does not expose an actual timezone, or is unable
- /// to provide mappings from times to deltas between the configured timezone
- /// and UTC, or determining the current timezone fails, or the timezone does
- /// not have an IANA identifier, this returns nothing.
- @unstable(feature = clocks-timezone)
- iana-id: func() -> option;
-
- /// The number of nanoseconds difference between UTC time and the local
- /// time of the currently configured timezone, at the exact time of
- /// `instant`.
- ///
- /// The magnitude of the returned value will always be less than
- /// 86,400,000,000,000 which is the number of nanoseconds in a day
- /// (24*60*60*1e9).
- ///
- /// If the implementation does not expose an actual timezone, or is unable
- /// to provide mappings from times to deltas between the configured timezone
- /// and UTC, or determining the current timezone fails, this returns
- /// nothing.
- @unstable(feature = clocks-timezone)
- utc-offset: func(when: instant) -> option;
-
- /// Returns a string that is suitable to assist humans in debugging whether
- /// any timezone is available, and if so, which. This may be the same string
- /// as `iana-id`, or a formatted representation of the UTC offset such as
- /// `-04:00`, or something else.
- ///
- /// WARNING: The returned string should not be consumed mechanically! It may
- /// change across platforms, hosts, or other implementation details. Parsing
- /// this string is a major platform-compatibility hazard.
- @unstable(feature = clocks-timezone)
- to-debug-string: func() -> string;
-}
diff --git a/proposals/clocks/wit-0.3.0-draft/types.wit b/proposals/clocks/wit-0.3.0-draft/types.wit
deleted file mode 100644
index 0da1a6cb..00000000
--- a/proposals/clocks/wit-0.3.0-draft/types.wit
+++ /dev/null
@@ -1,8 +0,0 @@
-package wasi:clocks@0.3.0-rc-2026-03-15;
-/// This interface common types used throughout wasi:clocks.
-@since(version = 0.3.0-rc-2026-03-15)
-interface types {
- /// A duration of time, in nanoseconds.
- @since(version = 0.3.0-rc-2026-03-15)
- type duration = u64;
-}
diff --git a/proposals/clocks/wit-0.3.0-draft/world.wit b/proposals/clocks/wit-0.3.0-draft/world.wit
deleted file mode 100644
index ac51f7fc..00000000
--- a/proposals/clocks/wit-0.3.0-draft/world.wit
+++ /dev/null
@@ -1,11 +0,0 @@
-package wasi:clocks@0.3.0-rc-2026-03-15;
-
-@since(version = 0.3.0-rc-2026-03-15)
-world imports {
- @since(version = 0.3.0-rc-2026-03-15)
- import monotonic-clock;
- @since(version = 0.3.0-rc-2026-03-15)
- import system-clock;
- @unstable(feature = clocks-timezone)
- import timezone;
-}
diff --git a/proposals/filesystem/wit-0.3.0-draft/deps.lock b/proposals/filesystem/wit-0.3.0-draft/deps.lock
deleted file mode 100644
index a780d3df..00000000
--- a/proposals/filesystem/wit-0.3.0-draft/deps.lock
+++ /dev/null
@@ -1,4 +0,0 @@
-[clocks]
-path = "../../clocks/wit-0.3.0-draft"
-sha256 = "a8702c42e23c22a458207d0efa38cc1b64c8c6299b0e3152aa84327a7c1c952b"
-sha512 = "da69a5d498b6afe0facf77ec3d21f6e8a2a4d4661ebfec310a2ce6f943cb01f92131a746765648febf96563ddac788e9f218941d3db2b905f3746dc8e05cf54d"
diff --git a/proposals/filesystem/wit-0.3.0-draft/deps.toml b/proposals/filesystem/wit-0.3.0-draft/deps.toml
deleted file mode 100644
index c54398c1..00000000
--- a/proposals/filesystem/wit-0.3.0-draft/deps.toml
+++ /dev/null
@@ -1 +0,0 @@
-clocks = "../../clocks/wit-0.3.0-draft"
diff --git a/proposals/filesystem/wit-0.3.0-draft/preopens.wit b/proposals/filesystem/wit-0.3.0-draft/preopens.wit
deleted file mode 100644
index 2c4560af..00000000
--- a/proposals/filesystem/wit-0.3.0-draft/preopens.wit
+++ /dev/null
@@ -1,11 +0,0 @@
-package wasi:filesystem@0.3.0-rc-2026-03-15;
-
-@since(version = 0.3.0-rc-2026-03-15)
-interface preopens {
- @since(version = 0.3.0-rc-2026-03-15)
- use types.{descriptor};
-
- /// Return the set of preopened directories, and their paths.
- @since(version = 0.3.0-rc-2026-03-15)
- get-directories: func() -> list>;
-}
diff --git a/proposals/filesystem/wit-0.3.0-draft/types.wit b/proposals/filesystem/wit-0.3.0-draft/types.wit
deleted file mode 100644
index d02ce735..00000000
--- a/proposals/filesystem/wit-0.3.0-draft/types.wit
+++ /dev/null
@@ -1,658 +0,0 @@
-package wasi:filesystem@0.3.0-rc-2026-03-15;
-/// 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.
-///
-/// 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.
-///
-/// The directory separator in WASI is always the forward-slash (`/`).
-///
-/// All paths in WASI are relative paths, and are interpreted relative to a
-/// `descriptor` referring to a base directory. If a `path` argument to any WASI
-/// function starts with `/`, or if any step of resolving a `path`, including
-/// `..` and symbolic link steps, reaches a directory outside of the base
-/// directory, or reaches a symlink to an absolute or rooted path in the
-/// underlying filesystem, the function fails with `error-code::not-permitted`.
-///
-/// 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-03-15)
-interface types {
- @since(version = 0.3.0-rc-2026-03-15)
- use wasi:clocks/system-clock@0.3.0-rc-2026-03-15.{instant};
-
- /// File size or length of a region within a file.
- @since(version = 0.3.0-rc-2026-03-15)
- 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-03-15)
- variant descriptor-type {
- /// The descriptor refers to a block device inode.
- block-device,
- /// The descriptor refers to a character device inode.
- character-device,
- /// The descriptor refers to a directory inode.
- directory,
- /// The descriptor refers to a named pipe.
- fifo,
- /// The file refers to a symbolic link inode.
- symbolic-link,
- /// The descriptor refers to a regular file inode.
- regular-file,
- /// The descriptor refers to a socket.
- socket,
- /// The type of the descriptor or file is different from any of the
- /// other types specified.
- other(option),
- }
-
- /// Descriptor flags.
- ///
- /// Note: This was called `fdflags` in earlier versions of WASI.
- @since(version = 0.3.0-rc-2026-03-15)
- flags descriptor-flags {
- /// Read mode: Data can be read.
- read,
- /// Write mode: Data can be written to.
- write,
- /// Request that writes be performed according to synchronized I/O file
- /// integrity completion. The data stored in the file and the file's
- /// metadata are synchronized. This is similar to `O_SYNC` in POSIX.
- ///
- /// The precise semantics of this operation have not yet been defined for
- /// WASI. At this time, it should be interpreted as a request, and not a
- /// requirement.
- file-integrity-sync,
- /// Request that writes be performed according to synchronized I/O data
- /// integrity completion. Only the data stored in the file is
- /// synchronized. This is similar to `O_DSYNC` in POSIX.
- ///
- /// The precise semantics of this operation have not yet been defined for
- /// WASI. At this time, it should be interpreted as a request, and not a
- /// requirement.
- data-integrity-sync,
- /// Requests that reads be performed at the same level of integrity
- /// requested for writes. This is similar to `O_RSYNC` in POSIX.
- ///
- /// The precise semantics of this operation have not yet been defined for
- /// WASI. At this time, it should be interpreted as a request, and not a
- /// requirement.
- requested-write-sync,
- /// Mutating directories mode: Directory contents may be mutated.
- ///
- /// When this flag is unset on a descriptor, operations using the
- /// descriptor which would create, rename, delete, modify the data or
- /// metadata of filesystem objects, or obtain another handle which
- /// would permit any of those, shall fail with `error-code::read-only` if
- /// they would otherwise succeed.
- ///
- /// This may only be set on directories.
- mutate-directory,
- }
-
- /// File attributes.
- ///
- /// Note: This was called `filestat` in earlier versions of WASI.
- @since(version = 0.3.0-rc-2026-03-15)
- record descriptor-stat {
- /// File type.
- %type: descriptor-type,
- /// Number of hard links to the file.
- link-count: link-count,
- /// For regular files, the file size in bytes. For symbolic links, the
- /// length in bytes of the pathname contained in the symbolic link.
- size: filesize,
- /// Last data access timestamp.
- ///
- /// If the `option` is none, the platform doesn't maintain an access
- /// timestamp for this file.
- data-access-timestamp: option,
- /// Last data modification timestamp.
- ///
- /// If the `option` is none, the platform doesn't maintain a
- /// modification timestamp for this file.
- data-modification-timestamp: option,
- /// Last file status-change timestamp.
- ///
- /// If the `option` is none, the platform doesn't maintain a
- /// status-change timestamp for this file.
- status-change-timestamp: option,
- }
-
- /// Flags determining the method of how paths are resolved.
- @since(version = 0.3.0-rc-2026-03-15)
- flags path-flags {
- /// As long as the resolved path corresponds to a symbolic link, it is
- /// expanded.
- symlink-follow,
- }
-
- /// Open flags used by `open-at`.
- @since(version = 0.3.0-rc-2026-03-15)
- flags open-flags {
- /// Create file if it does not exist, similar to `O_CREAT` in POSIX.
- create,
- /// Fail if not a directory, similar to `O_DIRECTORY` in POSIX.
- directory,
- /// Fail if file already exists, similar to `O_EXCL` in POSIX.
- exclusive,
- /// Truncate file to size 0, similar to `O_TRUNC` in POSIX.
- truncate,
- }
-
- /// Number of hard links to an inode.
- @since(version = 0.3.0-rc-2026-03-15)
- type link-count = u64;
-
- /// When setting a timestamp, this gives the value to set it to.
- @since(version = 0.3.0-rc-2026-03-15)
- variant new-timestamp {
- /// Leave the timestamp set to its previous value.
- no-change,
- /// Set the timestamp to the current time of the system clock associated
- /// with the filesystem.
- now,
- /// Set the timestamp to the given value.
- timestamp(instant),
- }
-
- /// A directory entry.
- @since(version = 0.3.0-rc-2026-03-15)
- record directory-entry {
- /// The type of the file referred to by this directory entry.
- %type: descriptor-type,
-
- /// The name of the object.
- name: string,
- }
-
- /// Error codes returned by functions, similar to `errno` in POSIX.
- /// 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-03-15)
- variant error-code {
- /// Permission denied, similar to `EACCES` in POSIX.
- access,
- /// Connection already in progress, similar to `EALREADY` in POSIX.
- already,
- /// Bad descriptor, similar to `EBADF` in POSIX.
- bad-descriptor,
- /// Device or resource busy, similar to `EBUSY` in POSIX.
- busy,
- /// Resource deadlock would occur, similar to `EDEADLK` in POSIX.
- deadlock,
- /// Storage quota exceeded, similar to `EDQUOT` in POSIX.
- quota,
- /// File exists, similar to `EEXIST` in POSIX.
- exist,
- /// File too large, similar to `EFBIG` in POSIX.
- file-too-large,
- /// Illegal byte sequence, similar to `EILSEQ` in POSIX.
- illegal-byte-sequence,
- /// Operation in progress, similar to `EINPROGRESS` in POSIX.
- in-progress,
- /// Interrupted function, similar to `EINTR` in POSIX.
- interrupted,
- /// Invalid argument, similar to `EINVAL` in POSIX.
- invalid,
- /// I/O error, similar to `EIO` in POSIX.
- io,
- /// Is a directory, similar to `EISDIR` in POSIX.
- is-directory,
- /// Too many levels of symbolic links, similar to `ELOOP` in POSIX.
- loop,
- /// Too many links, similar to `EMLINK` in POSIX.
- too-many-links,
- /// Message too large, similar to `EMSGSIZE` in POSIX.
- message-size,
- /// Filename too long, similar to `ENAMETOOLONG` in POSIX.
- name-too-long,
- /// No such device, similar to `ENODEV` in POSIX.
- no-device,
- /// No such file or directory, similar to `ENOENT` in POSIX.
- no-entry,
- /// No locks available, similar to `ENOLCK` in POSIX.
- no-lock,
- /// Not enough space, similar to `ENOMEM` in POSIX.
- insufficient-memory,
- /// No space left on device, similar to `ENOSPC` in POSIX.
- insufficient-space,
- /// Not a directory or a symbolic link to a directory, similar to `ENOTDIR` in POSIX.
- not-directory,
- /// Directory not empty, similar to `ENOTEMPTY` in POSIX.
- not-empty,
- /// State not recoverable, similar to `ENOTRECOVERABLE` in POSIX.
- not-recoverable,
- /// Not supported, similar to `ENOTSUP` and `ENOSYS` in POSIX.
- unsupported,
- /// Inappropriate I/O control operation, similar to `ENOTTY` in POSIX.
- no-tty,
- /// No such device or address, similar to `ENXIO` in POSIX.
- no-such-device,
- /// Value too large to be stored in data type, similar to `EOVERFLOW` in POSIX.
- overflow,
- /// Operation not permitted, similar to `EPERM` in POSIX.
- not-permitted,
- /// Broken pipe, similar to `EPIPE` in POSIX.
- pipe,
- /// Read-only file system, similar to `EROFS` in POSIX.
- read-only,
- /// Invalid seek, similar to `ESPIPE` in POSIX.
- invalid-seek,
- /// Text file busy, similar to `ETXTBSY` in POSIX.
- text-file-busy,
- /// Cross-device link, similar to `EXDEV` in POSIX.
- cross-device,
- /// A catch-all for errors not captured by the existing variants.
- /// Implementations can use this to extend the error type without
- /// breaking existing code.
- other(option),
- }
-
- /// File or memory access pattern advisory information.
- @since(version = 0.3.0-rc-2026-03-15)
- enum advice {
- /// The application has no advice to give on its behavior with respect
- /// to the specified data.
- normal,
- /// The application expects to access the specified data sequentially
- /// from lower offsets to higher offsets.
- sequential,
- /// The application expects to access the specified data in a random
- /// order.
- random,
- /// The application expects to access the specified data in the near
- /// future.
- will-need,
- /// The application expects that it will not access the specified data
- /// in the near future.
- dont-need,
- /// The application expects to access the specified data once and then
- /// not reuse it thereafter.
- no-reuse,
- }
-
- /// 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-03-15)
- record metadata-hash-value {
- /// 64 bits of a 128-bit hash value.
- lower: u64,
- /// Another 64 bits of a 128-bit hash value.
- upper: u64,
- }
-
- /// 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-03-15)
- resource descriptor {
- /// Return a stream for reading from a file.
- ///
- /// Multiple read, write, and append streams may be active on the same open
- /// file and they do not interfere with each other.
- ///
- /// This function returns a `stream` which provides the data received from the
- /// file, and a `future` providing additional error information in case an
- /// error is encountered.
- ///
- /// If no error is encountered, `stream.read` on the `stream` will return
- /// `read-status::closed` with no `error-context` and the future resolves to
- /// the value `ok`. If an error is encountered, `stream.read` on the
- /// `stream` returns `read-status::closed` with an `error-context` and the future
- /// resolves to `err` with an `error-code`.
- ///
- /// Note: This is similar to `pread` in POSIX.
- @since(version = 0.3.0-rc-2026-03-15)
- read-via-stream: func(
- /// The offset within the file at which to start reading.
- offset: filesize,
- ) -> tuple, future>>;
-
- /// Return a stream for writing to a file, if available.
- ///
- /// May fail with an error-code describing why the file cannot be written.
- ///
- /// It is valid to write past the end of a file; the file is extended to the
- /// extent of the write, with bytes between the previous end and the start of
- /// the write set to zero.
- ///
- /// This function returns once either full contents of the stream are
- /// written or an error is encountered.
- ///
- /// Note: This is similar to `pwrite` in POSIX.
- @since(version = 0.3.0-rc-2026-03-15)
- write-via-stream: func(
- /// Data to write
- data: stream,
- /// The offset within the file at which to start writing.
- 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.
- ///
- /// This function returns once either full contents of the stream are
- /// written or an error is encountered.
- ///
- /// Note: This is similar to `write` with `O_APPEND` in POSIX.
- @since(version = 0.3.0-rc-2026-03-15)
- 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-03-15)
- advise: async func(
- /// The offset within the file to which the advisory applies.
- offset: filesize,
- /// The length of the region to which the advisory applies.
- length: filesize,
- /// The advice.
- advice: advice
- ) -> result<_, error-code>;
-
- /// Synchronize the data 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 `fdatasync` in POSIX.
- @since(version = 0.3.0-rc-2026-03-15)
- sync-data: async func() -> result<_, error-code>;
-
- /// Get flags associated with a descriptor.
- ///
- /// Note: This returns similar flags to `fcntl(fd, F_GETFL)` in POSIX.
- ///
- /// 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-03-15)
- get-flags: async func() -> result;
-
- /// Get the dynamic type of a descriptor.
- ///
- /// Note: This returns the same value as the `type` field of the `fd-stat`
- /// returned by `stat`, `stat-at` and similar.
- ///
- /// Note: This returns similar flags to the `st_mode & S_IFMT` value provided
- /// by `fstat` in POSIX.
- ///
- /// 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-03-15)
- 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-03-15)
- 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-03-15)
- set-times: async func(
- /// The desired values of the data access timestamp.
- data-access-timestamp: new-timestamp,
- /// The desired values of the data modification timestamp.
- data-modification-timestamp: new-timestamp,
- ) -> result<_, error-code>;
-
- /// Read directory entries from a directory.
- ///
- /// On filesystems where directories contain entries referring to themselves
- /// and their parents, often named `.` and `..` respectively, these entries
- /// are omitted.
- ///
- /// This always returns a new stream which starts at the beginning of the
- /// directory. Multiple streams may be active on the same directory, and they
- /// do not interfere with each other.
- ///
- /// 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-03-15)
- 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-03-15)
- sync: async func() -> result<_, error-code>;
-
- /// Create a directory.
- ///
- /// Note: This is similar to `mkdirat` in POSIX.
- @since(version = 0.3.0-rc-2026-03-15)
- create-directory-at: async func(
- /// The relative path at which to create the directory.
- path: string,
- ) -> result<_, error-code>;
-
- /// Return the attributes of an open file or directory.
- ///
- /// Note: This is similar to `fstat` in POSIX, except that it does not return
- /// device and inode information. For testing whether two descriptors refer to
- /// the same underlying filesystem object, use `is-same-object`. To obtain
- /// additional data that can be used do determine whether a file has been
- /// modified, use `metadata-hash`.
- ///
- /// Note: This was called `fd_filestat_get` in earlier versions of WASI.
- @since(version = 0.3.0-rc-2026-03-15)
- stat: async func() -> result;
-
- /// Return the attributes of a file or directory.
- ///
- /// Note: This is similar to `fstatat` in POSIX, except that it does not
- /// return device and inode information. See the `stat` description for a
- /// discussion of alternatives.
- ///
- /// Note: This was called `path_filestat_get` in earlier versions of WASI.
- @since(version = 0.3.0-rc-2026-03-15)
- stat-at: async func(
- /// Flags determining the method of how the path is resolved.
- path-flags: path-flags,
- /// The relative path of the file or directory to inspect.
- path: string,
- ) -> result;
-
- /// Adjust the timestamps of a file or directory.
- ///
- /// Note: This is similar to `utimensat` in POSIX.
- ///
- /// Note: This was called `path_filestat_set_times` in earlier versions of
- /// WASI.
- @since(version = 0.3.0-rc-2026-03-15)
- set-times-at: async func(
- /// Flags determining the method of how the path is resolved.
- path-flags: path-flags,
- /// The relative path of the file or directory to operate on.
- path: string,
- /// The desired values of the data access timestamp.
- data-access-timestamp: new-timestamp,
- /// The desired values of the data modification timestamp.
- data-modification-timestamp: new-timestamp,
- ) -> result<_, error-code>;
-
- /// Create a hard link.
- ///
- /// Fails with `error-code::no-entry` if the old path does not exist,
- /// with `error-code::exist` if the new path already exists, and
- /// `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-03-15)
- link-at: async func(
- /// Flags determining the method of how the path is resolved.
- old-path-flags: path-flags,
- /// The relative source path from which to link.
- old-path: string,
- /// The base directory for `new-path`.
- new-descriptor: borrow,
- /// The relative destination path at which to create the hard link.
- new-path: string,
- ) -> result<_, error-code>;
-
- /// Open a file or directory.
- ///
- /// If `flags` contains `descriptor-flags::mutate-directory`, and the base
- /// descriptor doesn't have `descriptor-flags::mutate-directory` set,
- /// `open-at` fails with `error-code::read-only`.
- ///
- /// If `flags` contains `write` or `mutate-directory`, or `open-flags`
- /// contains `truncate` or `create`, and the base descriptor doesn't have
- /// `descriptor-flags::mutate-directory` set, `open-at` fails with
- /// `error-code::read-only`.
- ///
- /// Note: This is similar to `openat` in POSIX.
- @since(version = 0.3.0-rc-2026-03-15)
- open-at: async func(
- /// Flags determining the method of how the path is resolved.
- path-flags: path-flags,
- /// The relative path of the object to open.
- path: string,
- /// The method by which to open the file.
- open-flags: open-flags,
- /// Flags to use for the resulting descriptor.
- %flags: descriptor-flags,
- ) -> result;
-
- /// Read the contents of a symbolic link.
- ///
- /// If the contents contain an absolute or rooted path in the underlying
- /// filesystem, this function fails with `error-code::not-permitted`.
- ///
- /// Note: This is similar to `readlinkat` in POSIX.
- @since(version = 0.3.0-rc-2026-03-15)
- readlink-at: async func(
- /// The relative path of the symbolic link from which to read.
- 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-03-15)
- remove-directory-at: async func(
- /// The relative path to a directory to remove.
- path: string,
- ) -> result<_, error-code>;
-
- /// Rename a filesystem object.
- ///
- /// Note: This is similar to `renameat` in POSIX.
- @since(version = 0.3.0-rc-2026-03-15)
- rename-at: async func(
- /// The relative source path of the file or directory to rename.
- old-path: string,
- /// The base directory for `new-path`.
- new-descriptor: borrow,
- /// The relative destination path to which to rename the file or directory.
- new-path: string,
- ) -> result<_, error-code>;
-
- /// Create a symbolic link (also known as a "symlink").
- ///
- /// If `old-path` starts with `/`, the function fails with
- /// `error-code::not-permitted`.
- ///
- /// Note: This is similar to `symlinkat` in POSIX.
- @since(version = 0.3.0-rc-2026-03-15)
- symlink-at: async func(
- /// The contents of the symbolic link.
- old-path: string,
- /// The relative destination path at which to create the symbolic link.
- new-path: string,
- ) -> result<_, error-code>;
-
- /// Unlink a filesystem object that is not a directory.
- ///
- /// This is similar to `unlinkat(fd, path, 0)` in POSIX.
- ///
- /// Error returns are as specified by POSIX.
- ///
- /// If the filesystem object is a directory, `error-code::access` or
- /// `error-code::is-directory` may be returned instead of the
- /// POSIX-specified `error-code::not-permitted`.
- @since(version = 0.3.0-rc-2026-03-15)
- unlink-file-at: async func(
- /// The relative path to a file to unlink.
- path: string,
- ) -> result<_, error-code>;
-
- /// Test whether two descriptors refer to the same filesystem object.
- ///
- /// In POSIX, this corresponds to testing whether the two descriptors have the
- /// 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-03-15)
- is-same-object: async func(other: borrow) -> bool;
-
- /// Return a hash of the metadata associated with a filesystem object referred
- /// to by a descriptor.
- ///
- /// This returns a hash of the last-modification timestamp and file size, and
- /// may also include the inode number, device number, birth timestamp, and
- /// other metadata fields that may change when the file is modified or
- /// replaced. It may also include a secret value chosen by the
- /// implementation and not otherwise exposed.
- ///
- /// Implementations are encouraged to provide the following properties:
- ///
- /// - If the file is not modified or replaced, the computed hash value should
- /// usually not change.
- /// - If the object is modified or replaced, the computed hash value should
- /// usually change.
- /// - The inputs to the hash should not be easily computable from the
- /// computed hash.
- ///
- /// However, none of these is required.
- @since(version = 0.3.0-rc-2026-03-15)
- 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-03-15)
- metadata-hash-at: async func(
- /// Flags determining the method of how the path is resolved.
- path-flags: path-flags,
- /// The relative path of the file or directory to inspect.
- path: string,
- ) -> result;
- }
-}
diff --git a/proposals/filesystem/wit-0.3.0-draft/world.wit b/proposals/filesystem/wit-0.3.0-draft/world.wit
deleted file mode 100644
index b4fef32c..00000000
--- a/proposals/filesystem/wit-0.3.0-draft/world.wit
+++ /dev/null
@@ -1,9 +0,0 @@
-package wasi:filesystem@0.3.0-rc-2026-03-15;
-
-@since(version = 0.3.0-rc-2026-03-15)
-world imports {
- @since(version = 0.3.0-rc-2026-03-15)
- import types;
- @since(version = 0.3.0-rc-2026-03-15)
- import preopens;
-}
diff --git a/proposals/http/wit-0.3.0-draft/deps.lock b/proposals/http/wit-0.3.0-draft/deps.lock
deleted file mode 100644
index 6c3947e6..00000000
--- a/proposals/http/wit-0.3.0-draft/deps.lock
+++ /dev/null
@@ -1,22 +0,0 @@
-[cli]
-path = "../../cli/wit-0.3.0-draft"
-sha256 = "5be8d35b6397517777497279f1978c71aea0d91c7d63d4f4e0b54d0c1d33efd3"
-sha512 = "faf89924d5d5210d3b4df52d634deb8507c44c632bc0fd1a6fa4351a15ac36e92a039daf945c59c862ea002a748ac213b27c51affa68a90dbe529551f3d63a9b"
-deps = ["filesystem", "random", "sockets"]
-
-[clocks]
-path = "../../clocks/wit-0.3.0-draft"
-sha256 = "a8702c42e23c22a458207d0efa38cc1b64c8c6299b0e3152aa84327a7c1c952b"
-sha512 = "da69a5d498b6afe0facf77ec3d21f6e8a2a4d4661ebfec310a2ce6f943cb01f92131a746765648febf96563ddac788e9f218941d3db2b905f3746dc8e05cf54d"
-
-[filesystem]
-sha256 = "d9db43c43b4e09899a9e54c76544eef17439fb42e2f730ed96d40eac994b0dfd"
-sha512 = "3b7a1bef322ba36b1e9df768d740e4238f58d3c15d3e4c51fe899b93cf1e6641a79b5a8665ae71d38a80e1e0f852b30b023ecf318f4cbeacb36dd1ae0ac7e37b"
-
-[random]
-sha256 = "d44de4e427505fdfd584a23479dba5899ad80aa8e174dc0528df840db8ae9a43"
-sha512 = "6b08b32a197aee74076d0cdca6a09f78da9040eedd9ab3f64e5f14901ad5a0c5bbc592ad46a0d575dc6705249b3e10a413e09835616f753788aa598af605c776"
-
-[sockets]
-sha256 = "ee64b45d3826b6ff2cd27f6ac24fe23c41e65ba4d155294671dc7d0a0b47ddfe"
-sha512 = "dd797ab47b899aee52799f797497fa286f5e217307e8a451a7ba89bd05b7b9bfe2a6e0cf39c0e609ed4c76ed102487adff6e73156340f14baf70a9cc9e48ecbc"
diff --git a/proposals/http/wit-0.3.0-draft/deps.toml b/proposals/http/wit-0.3.0-draft/deps.toml
deleted file mode 100644
index 46265d48..00000000
--- a/proposals/http/wit-0.3.0-draft/deps.toml
+++ /dev/null
@@ -1,2 +0,0 @@
-cli = "../../cli/wit-0.3.0-draft"
-clocks = "../../clocks/wit-0.3.0-draft"
diff --git a/proposals/http/wit-0.3.0-draft/types.wit b/proposals/http/wit-0.3.0-draft/types.wit
deleted file mode 100644
index f6d9dd99..00000000
--- a/proposals/http/wit-0.3.0-draft/types.wit
+++ /dev/null
@@ -1,472 +0,0 @@
-package wasi:http@0.3.0-rc-2026-03-15;
-
-/// 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-03-15)
-interface types {
- use wasi:clocks/types@0.3.0-rc-2026-03-15.{duration};
-
- /// This type corresponds to HTTP standard Methods.
- @since(version = 0.3.0-rc-2026-03-15)
- variant method {
- get,
- head,
- post,
- put,
- delete,
- connect,
- options,
- trace,
- patch,
- other(string)
- }
-
- /// This type corresponds to HTTP standard Related Schemes.
- @since(version = 0.3.0-rc-2026-03-15)
- variant scheme {
- HTTP,
- HTTPS,
- other(string)
- }
-
- /// These cases are inspired by the IANA HTTP Proxy Error Types:
- ///
- @since(version = 0.3.0-rc-2026-03-15)
- variant error-code {
- DNS-timeout,
- DNS-error(DNS-error-payload),
- destination-not-found,
- destination-unavailable,
- destination-IP-prohibited,
- destination-IP-unroutable,
- connection-refused,
- connection-terminated,
- connection-timeout,
- connection-read-timeout,
- connection-write-timeout,
- connection-limit-reached,
- TLS-protocol-error,
- TLS-certificate-error,
- TLS-alert-received(TLS-alert-received-payload),
- HTTP-request-denied,
- HTTP-request-length-required,
- HTTP-request-body-size(option),
- HTTP-request-method-invalid,
- HTTP-request-URI-invalid,
- HTTP-request-URI-too-long,
- HTTP-request-header-section-size(option),
- HTTP-request-header-size(option),
- HTTP-request-trailer-section-size(option),
- HTTP-request-trailer-size(field-size-payload),
- HTTP-response-incomplete,
- HTTP-response-header-section-size(option),
- HTTP-response-header-size(field-size-payload),
- HTTP-response-body-size(option),
- HTTP-response-trailer-section-size(option),
- HTTP-response-trailer-size(field-size-payload),
- HTTP-response-transfer-coding(option),
- HTTP-response-content-coding(option),
- HTTP-response-timeout,
- HTTP-upgrade-failed,
- HTTP-protocol-error,
- loop-detected,
- configuration-error,
- /// This is a catch-all error for anything that doesn't fit cleanly into a
- /// more specific case. It also includes an optional string for an
- /// unstructured description of the error. Users should not depend on the
- /// string for diagnosing errors, as it's not required to be consistent
- /// between implementations.
- internal-error(option)
- }
-
- /// Defines the case payload type for `DNS-error` above:
- @since(version = 0.3.0-rc-2026-03-15)
- 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-03-15)
- 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-03-15)
- record field-size-payload {
- field-name: option,
- field-size: option
- }
-
- /// 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-03-15)
- 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
- /// `fields`.
- invalid-syntax,
-
- /// This error indicates that a forbidden `field-name` was used when trying
- /// to set a header in a `fields`.
- forbidden,
-
- /// This error indicates that the operation on the `fields` was not
- /// permitted because the fields are immutable.
- immutable,
-
- /// This error indicates that the operation would exceed an
- /// implementation-defined limit on field sizes. This may apply to
- /// an individual `field-value`, a single `field-name` plus all its
- /// values, or the total aggregate size of all fields.
- size-exceeded,
-
- /// This is a catch-all error for anything that doesn't fit cleanly into a
- /// more specific case. Implementations can use this to extend the error
- /// type without breaking existing code. It also includes an optional
- /// string for an unstructured description of the error. Users should not
- /// depend on the string for diagnosing errors, as it's not required to be
- /// consistent between implementations.
- other(option),
- }
-
- /// 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-03-15)
- variant request-options-error {
- /// Indicates the specified field is not supported by this implementation.
- not-supported,
-
- /// Indicates that the operation on the `request-options` was not permitted
- /// because it is immutable.
- immutable,
-
- /// This is a catch-all error for anything that doesn't fit cleanly into a
- /// more specific case. Implementations can use this to extend the error
- /// type without breaking existing code. It also includes an optional
- /// string for an unstructured description of the error. Users should not
- /// depend on the string for diagnosing errors, as it's not required to be
- /// consistent between implementations.
- other(option),
- }
-
- /// Field names are always strings.
- ///
- /// 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-03-15)
- 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-03-15)
- type field-value = list;
-
- /// This following block defines the `fields` resource which corresponds to
- /// HTTP standard Fields. Fields are a common representation used for both
- /// Headers and Trailers.
- ///
- /// A `fields` may be mutable or immutable. A `fields` created using the
- /// constructor, `from-list`, or `clone` will be mutable, but a `fields`
- /// resource given by other means (including, but not limited to,
- /// `request.headers`) might be be immutable. In an immutable fields, the
- /// `set`, `append`, and `delete` operations will fail with
- /// `header-error.immutable`.
- ///
- /// A `fields` resource should store `field-name`s and `field-value`s in their
- /// 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.
- ///
- /// Implementations may impose limits on individual field values and on total
- /// aggregate field section size. Operations that would exceed these limits
- /// fail with `header-error.size-exceeded`
- @since(version = 0.3.0-rc-2026-03-15)
- resource fields {
-
- /// Construct an empty HTTP Fields.
- ///
- /// The resulting `fields` is mutable.
- constructor();
-
- /// Construct an HTTP Fields.
- ///
- /// The resulting `fields` is mutable.
- ///
- /// The list represents each name-value pair in the Fields. Names
- /// which have multiple values are represented by multiple entries in this
- /// list with the same name.
- ///
- /// The tuple is a pair of the field name, represented as a string, and
- /// Value, represented as a list of bytes. In a valid Fields, all names
- /// and values are valid UTF-8 strings. However, values are not always
- /// well-formed, so they are represented as a raw list of bytes.
- ///
- /// An error result will be returned if any header or value was
- /// syntactically invalid, if a header was forbidden, or if the
- /// entries would exceed an implementation size limit.
- from-list: static func(
- entries: list>
- ) -> result;
-
- /// Get all of the values corresponding to a name. If the name is not present
- /// in this `fields`, an empty list is returned. However, if the name is
- /// present but empty, this is represented by a list with one or more
- /// empty field-values present.
- get: func(name: field-name) -> list;
-
- /// Returns `true` when the name is present in this `fields`. If the name is
- /// syntactically invalid, `false` is returned.
- has: func(name: field-name) -> bool;
-
- /// Set all of the values for a name. Clears any existing values for that
- /// name, if they have been set.
- ///
- /// Fails with `header-error.immutable` if the `fields` are immutable.
- ///
- /// Fails with `header-error.size-exceeded` if the name or values would
- /// exceed an implementation-defined size limit.
- set: func(name: field-name, value: list) -> result<_, header-error>;
-
- /// Delete all values for a name. Does nothing if no values for the name
- /// exist.
- ///
- /// Fails with `header-error.immutable` if the `fields` are immutable.
- delete: func(name: field-name) -> result<_, header-error>;
-
- /// Delete all values for a name. Does nothing if no values for the name
- /// exist.
- ///
- /// Returns all values previously corresponding to the name, if any.
- ///
- /// Fails with `header-error.immutable` if the `fields` are immutable.
- get-and-delete: func(name: field-name) -> result, header-error>;
-
- /// Append a value for a name. Does not change or delete any existing
- /// values for that name.
- ///
- /// Fails with `header-error.immutable` if the `fields` are immutable.
- ///
- /// Fails with `header-error.size-exceeded` if the value would exceed
- /// an implementation-defined size limit.
- append: func(name: field-name, value: field-value) -> result<_, header-error>;
-
- /// Retrieve the full set of names and values in the Fields. Like the
- /// constructor, the list represents each name-value pair.
- ///
- /// The outer list represents each name-value pair in the Fields. Names
- /// which have multiple values are represented by multiple entries in this
- /// list with the same name.
- ///
- /// The names and values are always returned in the original casing and in
- /// the order in which they will be serialized for transport.
- copy-all: func() -> list>;
-
- /// Make a deep copy of the Fields. Equivalent in behavior to calling the
- /// `fields` constructor on the return value of `copy-all`. The resulting
- /// `fields` is mutable.
- clone: func() -> fields;
- }
-
- /// Headers is an alias for Fields.
- @since(version = 0.3.0-rc-2026-03-15)
- type headers = fields;
-
- /// Trailers is an alias for Fields.
- @since(version = 0.3.0-rc-2026-03-15)
- type trailers = fields;
-
- /// Represents an HTTP Request.
- @since(version = 0.3.0-rc-2026-03-15)
- resource request {
-
- /// Construct a new `request` with a default `method` of `GET`, and
- /// `none` values for `path-with-query`, `scheme`, and `authority`.
- ///
- /// `headers` is the HTTP Headers for the Request.
- ///
- /// `contents` is the optional body content stream with `none`
- /// representing a zero-length content stream.
- /// Once it is closed, `trailers` future must resolve to a result.
- /// If `trailers` resolves to an error, underlying connection
- /// will be closed immediately.
- ///
- /// `options` is optional `request-options` resource to be used
- /// if the request is sent over a network connection.
- ///
- /// It is possible to construct, or manipulate with the accessor functions
- /// below, a `request` with an invalid combination of `scheme`
- /// and `authority`, or `headers` which are not permitted to be sent.
- /// It is the obligation of the `handler.handle` implementation
- /// to reject invalid constructions of `request`.
- ///
- /// The returned future resolves to result of transmission of this request.
- new: static func(
- headers: headers,
- contents: option>,
- trailers: future, error-code>>,
- options: option
- ) -> tuple>>;
-
- /// Get the Method for the Request.
- get-method: func() -> method;
- /// Set the Method for the Request. Fails if the string present in a
- /// `method.other` argument is not a syntactically valid method.
- set-method: func(method: method) -> result;
-
- /// Get the combination of the HTTP Path and Query for the Request. When
- /// `none`, this represents an empty Path and empty Query.
- get-path-with-query: func() -> option;
- /// Set the combination of the HTTP Path and Query for the Request. When
- /// `none`, this represents an empty Path and empty Query. Fails is the
- /// string given is not a syntactically valid path and query uri component.
- set-path-with-query: func(path-with-query: option) -> result;
-
- /// Get the HTTP Related Scheme for the Request. When `none`, the
- /// implementation may choose an appropriate default scheme.
- get-scheme: func() -> option;
- /// Set the HTTP Related Scheme for the Request. When `none`, the
- /// implementation may choose an appropriate default scheme. Fails if the
- /// string given is not a syntactically valid uri scheme.
- set-scheme: func(scheme: option) -> result;
-
- /// Get the authority of the Request's target URI. A value of `none` may be used
- /// with Related Schemes which do not require an authority. The HTTP and
- /// HTTPS schemes always require an authority.
- get-authority: func() -> option;
- /// Set the authority of the Request's target URI. A value of `none` may be used
- /// with Related Schemes which do not require an authority. The HTTP and
- /// HTTPS schemes always require an authority. Fails if the string given is
- /// not a syntactically valid URI authority.
- set-authority: func(authority: option) -> result;
-
- /// Get the `request-options` to be associated with this request
- ///
- /// The returned `request-options` resource is immutable: `set-*` operations
- /// will fail if invoked.
- ///
- /// This `request-options` resource is a child: it must be dropped before
- /// the parent `request` is dropped, or its ownership is transferred to
- /// another component by e.g. `handler.handle`.
- get-options: func() -> option;
-
- /// Get the headers associated with the Request.
- ///
- /// The returned `headers` resource is immutable: `set`, `append`, and
- /// `delete` operations will fail with `header-error.immutable`.
- get-headers: func() -> headers;
-
- /// Get body of the Request.
- ///
- /// Stream returned by this method represents the contents of the body.
- /// Once the stream is reported as closed, callers should await the returned
- /// future to determine whether the body was received successfully.
- /// The future will only resolve after the stream is reported as closed.
- ///
- /// This function takes a `res` future as a parameter, which can be used to
- /// communicate an error in handling of the request.
- ///
- /// Note that function will move the `request`, but references to headers or
- /// request options acquired from it previously will remain valid.
- consume-body: static func(this: request, res: future>) -> tuple, future, error-code>>>;
- }
-
- /// Parameters for making an HTTP Request. Each of these parameters is
- /// currently an optional timeout applicable to the transport layer of the
- /// HTTP protocol.
- ///
- /// These timeouts are separate from any the user may use to bound an
- /// asynchronous call.
- @since(version = 0.3.0-rc-2026-03-15)
- resource request-options {
- /// Construct a default `request-options` value.
- constructor();
-
- /// The timeout for the initial connect to the HTTP Server.
- get-connect-timeout: func() -> option;
-
- /// Set the timeout for the initial connect to the HTTP Server. An error
- /// return value indicates that this timeout is not supported or that this
- /// handle is immutable.
- set-connect-timeout: func(duration: option) -> result<_, request-options-error>;
-
- /// The timeout for receiving the first byte of the Response body.
- get-first-byte-timeout: func() -> option;
-
- /// Set the timeout for receiving the first byte of the Response body. An
- /// error return value indicates that this timeout is not supported or that
- /// this handle is immutable.
- set-first-byte-timeout: func(duration: option) -> result<_, request-options-error>;
-
- /// The timeout for receiving subsequent chunks of bytes in the Response
- /// body stream.
- get-between-bytes-timeout: func() -> option;
-
- /// Set the timeout for receiving subsequent chunks of bytes in the Response
- /// body stream. An error return value indicates that this timeout is not
- /// supported or that this handle is immutable.
- set-between-bytes-timeout: func(duration: option) -> result<_, request-options-error>;
-
- /// Make a deep copy of the `request-options`.
- /// The resulting `request-options` is mutable.
- clone: func() -> request-options;
- }
-
- /// This type corresponds to the HTTP standard Status Code.
- @since(version = 0.3.0-rc-2026-03-15)
- type status-code = u16;
-
- /// Represents an HTTP Response.
- @since(version = 0.3.0-rc-2026-03-15)
- 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
- /// `set-status-code` method.
- ///
- /// `headers` is the HTTP Headers for the Response.
- ///
- /// `contents` is the optional body content stream with `none`
- /// representing a zero-length content stream.
- /// Once it is closed, `trailers` future must resolve to a result.
- /// If `trailers` resolves to an error, underlying connection
- /// will be closed immediately.
- ///
- /// The returned future resolves to result of transmission of this response.
- new: static func(
- headers: headers,
- contents: option>,
- trailers: future, error-code>>,
- ) -> tuple>>;
-
- /// Get the HTTP Status Code for the Response.
- get-status-code: func() -> status-code;
-
- /// Set the HTTP Status Code for the Response. Fails if the status-code
- /// given is not a valid http status code.
- set-status-code: func(status-code: status-code) -> result;
-
- /// Get the headers associated with the Response.
- ///
- /// The returned `headers` resource is immutable: `set`, `append`, and
- /// `delete` operations will fail with `header-error.immutable`.
- get-headers: func() -> headers;
-
- /// Get body of the Response.
- ///
- /// Stream returned by this method represents the contents of the body.
- /// Once the stream is reported as closed, callers should await the returned
- /// future to determine whether the body was received successfully.
- /// The future will only resolve after the stream is reported as closed.
- ///
- /// This function takes a `res` future as a parameter, which can be used to
- /// communicate an error in handling of the response.
- ///
- /// Note that function will move the `response`, but references to headers
- /// acquired from it previously will remain valid.
- consume-body: static func(this: response, res: future>) -> tuple, future, error-code>>>;
- }
-}
diff --git a/proposals/http/wit-0.3.0-draft/worlds.wit b/proposals/http/wit-0.3.0-draft/worlds.wit
deleted file mode 100644
index 9917e054..00000000
--- a/proposals/http/wit-0.3.0-draft/worlds.wit
+++ /dev/null
@@ -1,86 +0,0 @@
-package wasi:http@0.3.0-rc-2026-03-15;
-
-/// 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-03-15)
-world service {
- /// HTTP services have access to time and randomness.
- include wasi:clocks/imports@0.3.0-rc-2026-03-15;
- include wasi:random/imports@0.3.0-rc-2026-03-15;
-
- /// Services have standard output and error streams which are expected to
- /// terminate in a developer-facing console provided by the host.
- import wasi:cli/stdout@0.3.0-rc-2026-03-15;
- import wasi:cli/stderr@0.3.0-rc-2026-03-15;
-
- /// TODO: this is a temporary workaround until component tooling is able to
- /// gracefully handle the absence of stdin. Hosts must return an eof stream
- /// for this import, which is what wasi-libc + tooling will do automatically
- /// when this import is properly removed.
- import wasi:cli/stdin@0.3.0-rc-2026-03-15;
-
- /// This is the default `client` to use when user code simply wants to make an
- /// HTTP request (e.g., via `fetch()`).
- import client;
-
- /// The host delivers incoming HTTP requests to a component by calling the
- /// `handle` function of this exported interface. A host may arbitrarily reuse
- /// or not reuse component instance when delivering incoming HTTP requests and
- /// thus a component must be able to handle 0..N calls to `handle`.
- ///
- /// This may also be used to receive synthesized or forwarded requests from
- /// another component.
- export handler;
-}
-
-/// The `wasi:http/middleware` world captures HTTP services that forward HTTP
-/// Requests to another handler.
-///
-/// 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-03-15)
-world middleware {
- include service;
- import handler;
-}
-
-/// This interface defines a handler of HTTP Requests.
-///
-/// In a `wasi:http/service` this interface is exported to respond to an
-/// incoming HTTP Request with a Response.
-///
-/// 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-03-15)
-interface handler {
- use types.{request, response, error-code};
-
- /// This function may be called with either an incoming request read from the
- /// network or a request synthesized or forwarded by another component.
- handle: async func(
- request: request,
- ) -> result;
-}
-
-/// This interface defines an HTTP client for sending "outgoing" requests.
-///
-/// Most components are expected to import this interface to provide the
-/// capability to send HTTP requests to arbitrary destinations on a network.
-///
-/// The type signature of `client.send` is the same as `handler.handle`. This
-/// duplication is currently necessary because some Component Model tooling
-/// (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-03-15)
-interface client {
- use types.{request, response, error-code};
-
- // This function may be used to either send an outgoing request over the
- // network or to forward it to another component.
- send: async func(
- request: request,
- ) -> result;
-}
diff --git a/proposals/random/wit-0.3.0-draft/insecure-seed.wit b/proposals/random/wit-0.3.0-draft/insecure-seed.wit
deleted file mode 100644
index dc9100f7..00000000
--- a/proposals/random/wit-0.3.0-draft/insecure-seed.wit
+++ /dev/null
@@ -1,27 +0,0 @@
-package wasi:random@0.3.0-rc-2026-03-15;
-/// 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-03-15)
-interface insecure-seed {
- /// Return a 128-bit value that may contain a pseudo-random value.
- ///
- /// The returned value is not required to be computed from a CSPRNG, and may
- /// even be entirely deterministic. Host implementations are encouraged to
- /// provide pseudo-random values to any program exposed to
- /// attacker-controlled content, to enable DoS protection built into many
- /// languages' hash-map implementations.
- ///
- /// This function is intended to only be called once, by a source language
- /// to initialize Denial Of Service (DoS) protection in its hash-map
- /// implementation.
- ///
- /// # Expected future evolution
- ///
- /// 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-03-15)
- get-insecure-seed: func() -> tuple;
-}
diff --git a/proposals/random/wit-0.3.0-draft/insecure.wit b/proposals/random/wit-0.3.0-draft/insecure.wit
deleted file mode 100644
index ee3fef73..00000000
--- a/proposals/random/wit-0.3.0-draft/insecure.wit
+++ /dev/null
@@ -1,32 +0,0 @@
-package wasi:random@0.3.0-rc-2026-03-15;
-/// The insecure interface for insecure pseudo-random numbers.
-///
-/// It is intended to be portable at least between Unix-family platforms and
-/// Windows.
-@since(version = 0.3.0-rc-2026-03-15)
-interface insecure {
- /// Return up to `max-len` insecure pseudo-random bytes.
- ///
- /// This function is not cryptographically secure. Do not use it for
- /// anything related to security.
- ///
- /// There are no requirements on the values of the returned bytes, however
- /// implementations are encouraged to return evenly distributed values with
- /// a long period.
- ///
- /// Implementations MAY return fewer bytes than requested (a short read).
- /// Callers that require exactly `max-len` bytes MUST call this function in
- /// a loop until the desired number of bytes has been accumulated.
- /// Implementations MUST return at least 1 byte when `max-len` is greater
- /// than zero. When `max-len` is zero, implementations MUST return an empty
- /// list without trapping.
- @since(version = 0.3.0-rc-2026-03-15)
- get-insecure-random-bytes: func(max-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-03-15)
- get-insecure-random-u64: func() -> u64;
-}
diff --git a/proposals/random/wit-0.3.0-draft/random.wit b/proposals/random/wit-0.3.0-draft/random.wit
deleted file mode 100644
index e40b97d7..00000000
--- a/proposals/random/wit-0.3.0-draft/random.wit
+++ /dev/null
@@ -1,37 +0,0 @@
-package wasi:random@0.3.0-rc-2026-03-15;
-/// WASI Random is a random data API.
-///
-/// It is intended to be portable at least between Unix-family platforms and
-/// Windows.
-@since(version = 0.3.0-rc-2026-03-15)
-interface random {
- /// Return up to `max-len` cryptographically-secure random or pseudo-random
- /// bytes.
- ///
- /// This function must produce data at least as cryptographically secure and
- /// fast as an adequately seeded cryptographically-secure pseudo-random
- /// number generator (CSPRNG). It must not block, from the perspective of
- /// the calling program, under any circumstances, including on the first
- /// request and on requests for numbers of bytes. The returned data must
- /// always be unpredictable.
- ///
- /// Implementations MAY return fewer bytes than requested (a short read).
- /// Callers that require exactly `max-len` bytes MUST call this function in
- /// a loop until the desired number of bytes has been accumulated.
- /// Implementations MUST return at least 1 byte when `max-len` is greater
- /// than zero. When `max-len` is zero, implementations MUST return an empty
- /// list without trapping.
- ///
- /// 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-03-15)
- get-random-bytes: func(max-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-03-15)
- get-random-u64: func() -> u64;
-}
diff --git a/proposals/random/wit-0.3.0-draft/world.wit b/proposals/random/wit-0.3.0-draft/world.wit
deleted file mode 100644
index 59069134..00000000
--- a/proposals/random/wit-0.3.0-draft/world.wit
+++ /dev/null
@@ -1,13 +0,0 @@
-package wasi:random@0.3.0-rc-2026-03-15;
-
-@since(version = 0.3.0-rc-2026-03-15)
-world imports {
- @since(version = 0.3.0-rc-2026-03-15)
- import random;
-
- @since(version = 0.3.0-rc-2026-03-15)
- import insecure;
-
- @since(version = 0.3.0-rc-2026-03-15)
- import insecure-seed;
-}
diff --git a/proposals/sockets/wit-0.3.0-draft/deps.lock b/proposals/sockets/wit-0.3.0-draft/deps.lock
deleted file mode 100644
index a780d3df..00000000
--- a/proposals/sockets/wit-0.3.0-draft/deps.lock
+++ /dev/null
@@ -1,4 +0,0 @@
-[clocks]
-path = "../../clocks/wit-0.3.0-draft"
-sha256 = "a8702c42e23c22a458207d0efa38cc1b64c8c6299b0e3152aa84327a7c1c952b"
-sha512 = "da69a5d498b6afe0facf77ec3d21f6e8a2a4d4661ebfec310a2ce6f943cb01f92131a746765648febf96563ddac788e9f218941d3db2b905f3746dc8e05cf54d"
diff --git a/proposals/sockets/wit-0.3.0-draft/deps.toml b/proposals/sockets/wit-0.3.0-draft/deps.toml
deleted file mode 100644
index c54398c1..00000000
--- a/proposals/sockets/wit-0.3.0-draft/deps.toml
+++ /dev/null
@@ -1 +0,0 @@
-clocks = "../../clocks/wit-0.3.0-draft"
diff --git a/proposals/sockets/wit-0.3.0-draft/ip-name-lookup.wit b/proposals/sockets/wit-0.3.0-draft/ip-name-lookup.wit
deleted file mode 100644
index 2e9c9fe0..00000000
--- a/proposals/sockets/wit-0.3.0-draft/ip-name-lookup.wit
+++ /dev/null
@@ -1,61 +0,0 @@
-@since(version = 0.3.0-rc-2026-03-15)
-interface ip-name-lookup {
- @since(version = 0.3.0-rc-2026-03-15)
- use types.{ip-address};
-
- /// Lookup error codes.
- @since(version = 0.3.0-rc-2026-03-15)
- variant error-code {
- /// Access denied.
- ///
- /// POSIX equivalent: EACCES, EPERM
- access-denied,
-
- /// `name` is a syntactically invalid domain name or IP address.
- ///
- /// POSIX equivalent: EINVAL
- invalid-argument,
-
- /// Name does not exist or has no suitable associated IP addresses.
- ///
- /// POSIX equivalent: EAI_NONAME, EAI_NODATA, EAI_ADDRFAMILY
- name-unresolvable,
-
- /// A temporary failure in name resolution occurred.
- ///
- /// POSIX equivalent: EAI_AGAIN
- temporary-resolver-failure,
-
- /// A permanent failure in name resolution occurred.
- ///
- /// POSIX equivalent: EAI_FAIL
- permanent-resolver-failure,
-
- /// A catch-all for errors not captured by the existing variants.
- /// Implementations can use this to extend the error type without
- /// breaking existing code.
- other(option),
- }
-
- /// Resolve an internet host name to a list of IP addresses.
- ///
- /// Unicode domain names are automatically converted to ASCII using IDNA
- /// encoding. If the input is an IP address string, the address is parsed
- /// and returned as-is without making any external requests.
- ///
- /// See the wasi-socket proposal README.md for a comparison with getaddrinfo.
- ///
- /// The results are returned in connection order preference.
- ///
- /// This function never succeeds with 0 results. It either fails or succeeds
- /// with at least one address. Additionally, this function never returns
- /// IPv4-mapped IPv6 addresses.
- ///
- /// # References:
- /// -
- /// -
- /// -
- /// -
- @since(version = 0.3.0-rc-2026-03-15)
- resolve-addresses: async func(name: string) -> result, error-code>;
-}
diff --git a/proposals/sockets/wit-0.3.0-draft/types.wit b/proposals/sockets/wit-0.3.0-draft/types.wit
deleted file mode 100644
index fe2c0448..00000000
--- a/proposals/sockets/wit-0.3.0-draft/types.wit
+++ /dev/null
@@ -1,812 +0,0 @@
-@since(version = 0.3.0-rc-2026-03-15)
-interface types {
- @since(version = 0.3.0-rc-2026-03-15)
- use wasi:clocks/types@0.3.0-rc-2026-03-15.{duration};
-
- /// Error codes.
- ///
- /// In theory, every API can return any error code.
- /// In practice, API's typically only return the errors documented per API
- /// combined with a couple of errors that are always possible:
- /// - `other`
- /// - `access-denied`
- /// - `not-supported`
- /// - `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-03-15)
- variant error-code {
- /// Access denied.
- ///
- /// POSIX equivalent: EACCES, EPERM
- access-denied,
-
- /// The operation is not supported.
- ///
- /// POSIX equivalent: EOPNOTSUPP, ENOPROTOOPT, EPFNOSUPPORT, EPROTONOSUPPORT, ESOCKTNOSUPPORT
- not-supported,
-
- /// One of the arguments is invalid.
- ///
- /// POSIX equivalent: EINVAL, EDESTADDRREQ, EAFNOSUPPORT
- invalid-argument,
-
- /// Not enough memory to complete the operation.
- ///
- /// POSIX equivalent: ENOMEM, ENOBUFS
- out-of-memory,
-
- /// The operation timed out before it could finish completely.
- ///
- /// POSIX equivalent: ETIMEDOUT
- timeout,
-
- /// The operation is not valid in the socket's current state.
- invalid-state,
-
- /// The local address is not available.
- ///
- /// POSIX equivalent: EADDRNOTAVAIL
- address-not-bindable,
-
- /// A bind operation failed because the provided address is already in
- /// use or because there are no ephemeral ports available.
- ///
- /// POSIX equivalent: EADDRINUSE
- address-in-use,
-
- /// The remote address is not reachable.
- ///
- /// POSIX equivalent: EHOSTUNREACH, EHOSTDOWN, ENETDOWN, ENETUNREACH, ENONET
- remote-unreachable,
-
- /// The connection was forcefully rejected.
- ///
- /// POSIX equivalent: ECONNREFUSED
- connection-refused,
-
- /// A write failed because the connection was broken.
- ///
- /// POSIX equivalent: EPIPE
- connection-broken,
-
- /// The connection was reset.
- ///
- /// POSIX equivalent: ECONNRESET
- connection-reset,
-
- /// The connection was aborted.
- ///
- /// POSIX equivalent: ECONNABORTED
- connection-aborted,
-
- /// The size of a datagram sent to a UDP socket exceeded the maximum
- /// supported size.
- ///
- /// POSIX equivalent: EMSGSIZE
- datagram-too-large,
-
- /// A catch-all for errors not captured by the existing variants.
- /// Implementations can use this to extend the error type without
- /// breaking existing code.
- other(option),
- }
-
- @since(version = 0.3.0-rc-2026-03-15)
- enum ip-address-family {
- /// Similar to `AF_INET` in POSIX.
- ipv4,
-
- /// Similar to `AF_INET6` in POSIX.
- ipv6,
- }
-
- @since(version = 0.3.0-rc-2026-03-15)
- type ipv4-address = tuple;
- @since(version = 0.3.0-rc-2026-03-15)
- type ipv6-address = tuple;
-
- @since(version = 0.3.0-rc-2026-03-15)
- variant ip-address {
- ipv4(ipv4-address),
- ipv6(ipv6-address),
- }
-
- @since(version = 0.3.0-rc-2026-03-15)
- record ipv4-socket-address {
- /// sin_port
- port: u16,
- /// sin_addr
- address: ipv4-address,
- }
-
- @since(version = 0.3.0-rc-2026-03-15)
- record ipv6-socket-address {
- /// sin6_port
- port: u16,
- /// sin6_flowinfo
- flow-info: u32,
- /// sin6_addr
- address: ipv6-address,
- /// sin6_scope_id
- scope-id: u32,
- }
-
- @since(version = 0.3.0-rc-2026-03-15)
- variant ip-socket-address {
- ipv4(ipv4-socket-address),
- ipv6(ipv6-socket-address),
- }
-
- /// A TCP socket resource.
- ///
- /// The socket can be in one of the following states:
- /// - `unbound`
- /// - `bound` (See note below)
- /// - `listening`
- /// - `connecting`
- /// - `connected`
- /// - `closed`
- /// See
- /// for more information.
- ///
- /// Note: Except where explicitly mentioned, whenever this documentation uses
- /// the term "bound" without backticks it actually means: in the `bound` state *or higher*.
- /// (i.e. `bound`, `listening`, `connecting` or `connected`)
- ///
- /// WASI uses shared ownership semantics: the `tcp-socket` handle and all
- /// derived `stream` and `future` values reference a single underlying OS
- /// socket:
- /// - Send/receive streams remain functional after the original `tcp-socket`
- /// handle is dropped.
- /// - The stream returned by `listen` behaves similarly.
- /// - Client sockets returned by `tcp-socket::listen` are independent and do
- /// not keep the listening socket alive.
- ///
- /// The OS socket is closed only after the last handle is dropped. This
- /// model has observable effects; for example, it affects when the local
- /// port binding is released.
- ///
- /// 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-03-15)
- resource tcp-socket {
-
- /// Create a new TCP socket.
- ///
- /// Similar to `socket(AF_INET or AF_INET6, SOCK_STREAM, IPPROTO_TCP)`
- /// in POSIX. On IPv6 sockets, IPV6_V6ONLY is enabled by default and
- /// can't be configured otherwise.
- ///
- /// Unlike POSIX, WASI sockets have no notion of a socket-level
- /// `O_NONBLOCK` flag. Instead they fully rely on the Component Model's
- /// async support.
- ///
- /// # Typical errors
- /// - `not-supported`: The `address-family` is not supported. (EAFNOSUPPORT)
- ///
- /// # References
- /// -
- /// -
- /// -
- /// -
- @since(version = 0.3.0-rc-2026-03-15)
- create: static func(address-family: ip-address-family) -> result;
-
- /// Bind the socket to the provided IP address and port.
- ///
- /// If the IP address is zero (`0.0.0.0` in IPv4, `::` in IPv6), it is
- /// left to the implementation to decide which network interface(s) to
- /// bind to. If the TCP/UDP port is zero, the socket will be bound to a
- /// random free port.
- ///
- /// Bind can be attempted multiple times on the same socket, even with
- /// different arguments on each iteration. But never concurrently and
- /// only as long as the previous bind failed. Once a bind succeeds, the
- /// binding can't be changed anymore.
- ///
- /// # Typical errors
- /// - `invalid-argument`: The `local-address` has the wrong address family. (EAFNOSUPPORT, EFAULT on Windows)
- /// - `invalid-argument`: `local-address` is not a unicast address. (EINVAL)
- /// - `invalid-argument`: `local-address` is an IPv4-mapped IPv6 address. (EINVAL)
- /// - `invalid-state`: The socket is already bound. (EINVAL)
- /// - `address-in-use`: No ephemeral ports available. (EADDRINUSE, ENOBUFS on Windows)
- /// - `address-in-use`: Address is already in use. (EADDRINUSE)
- /// - `address-not-bindable`: `local-address` is not an address that can be bound to. (EADDRNOTAVAIL)
- ///
- /// # Implementors note
- /// The bind operation shouldn't be affected by the TIME_WAIT state of a
- /// recently closed socket on the same local address. In practice this
- /// means that the SO_REUSEADDR socket option should be set implicitly
- /// on all platforms, except on Windows where this is the default
- /// behavior and SO_REUSEADDR performs something different.
- ///
- /// # References
- /// -
- /// -
- /// -
- /// -
- @since(version = 0.3.0-rc-2026-03-15)
- bind: func(local-address: ip-socket-address) -> result<_, error-code>;
-
- /// Connect to a remote endpoint.
- ///
- /// On success, the socket is transitioned into the `connected` state
- /// and the `remote-address` of the socket is updated.
- /// The `local-address` may be updated as well, based on the best network
- /// path to `remote-address`. If the socket was not already explicitly
- /// bound, this function will implicitly bind the socket to a random
- /// free port.
- ///
- /// After a failed connection attempt, the socket will be in the `closed`
- /// state and the only valid action left is to `drop` the socket. A single
- /// socket can not be used to connect more than once.
- ///
- /// # Typical errors
- /// - `invalid-argument`: The `remote-address` has the wrong address family. (EAFNOSUPPORT)
- /// - `invalid-argument`: `remote-address` is not a unicast address. (EINVAL, ENETUNREACH on Linux, EAFNOSUPPORT on MacOS)
- /// - `invalid-argument`: `remote-address` is an IPv4-mapped IPv6 address. (EINVAL, EADDRNOTAVAIL on Illumos)
- /// - `invalid-argument`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EADDRNOTAVAIL on Windows)
- /// - `invalid-argument`: The port in `remote-address` is set to 0. (EADDRNOTAVAIL on Windows)
- /// - `invalid-state`: The socket is already in the `connecting` state. (EALREADY)
- /// - `invalid-state`: The socket is already in the `connected` state. (EISCONN)
- /// - `invalid-state`: The socket is already in the `listening` state. (EOPNOTSUPP, EINVAL on Windows)
- /// - `timeout`: Connection timed out. (ETIMEDOUT)
- /// - `connection-refused`: The connection was forcefully rejected. (ECONNREFUSED)
- /// - `connection-reset`: The connection was reset. (ECONNRESET)
- /// - `connection-aborted`: The connection was aborted. (ECONNABORTED)
- /// - `remote-unreachable`: The remote address is not reachable. (EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET)
- /// - `address-in-use`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE, EADDRNOTAVAIL on Linux, EAGAIN on BSD)
- ///
- /// # References
- /// -
- /// -
- /// -
- /// -
- @since(version = 0.3.0-rc-2026-03-15)
- connect: async func(remote-address: ip-socket-address) -> result<_, error-code>;
-
- /// Start listening and return a stream of new inbound connections.
- ///
- /// Transitions the socket into the `listening` state. This can be called
- /// at most once per socket.
- ///
- /// If the socket is not already explicitly bound, this function will
- /// implicitly bind the socket to a random free port.
- ///
- /// Normally, the returned sockets are bound, in the `connected` state
- /// and immediately ready for I/O. Though, depending on exact timing and
- /// circumstances, a newly accepted connection may already be `closed`
- /// by the time the server attempts to perform its first I/O on it. This
- /// is true regardless of whether the WASI implementation uses
- /// "synthesized" sockets or not (see Implementors Notes below).
- ///
- /// The following properties are inherited from the listener socket:
- /// - `address-family`
- /// - `keep-alive-enabled`
- /// - `keep-alive-idle-time`
- /// - `keep-alive-interval`
- /// - `keep-alive-count`
- /// - `hop-limit`
- /// - `receive-buffer-size`
- /// - `send-buffer-size`
- ///
- /// # Typical errors
- /// - `invalid-state`: The socket is already in the `connected` state. (EISCONN, EINVAL on BSD)
- /// - `invalid-state`: The socket is already in the `listening` state.
- /// - `address-in-use`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE)
- ///
- /// # Implementors note
- /// This method returns a single perpetual stream that should only close
- /// on fatal errors (if any). Yet, the POSIX' `accept` function may also
- /// return transient errors (e.g. ECONNABORTED). The exact details differ
- /// per operation system. For example, the Linux manual mentions:
- ///
- /// > Linux accept() passes already-pending network errors on the new
- /// > socket as an error code from accept(). This behavior differs from
- /// > other BSD socket implementations. For reliable operation the
- /// > application should detect the network errors defined for the
- /// > protocol after accept() and treat them like EAGAIN by retrying.
- /// > In the case of TCP/IP, these are ENETDOWN, EPROTO, ENOPROTOOPT,
- /// > EHOSTDOWN, ENONET, EHOSTUNREACH, EOPNOTSUPP, and ENETUNREACH.
- /// Source: https://man7.org/linux/man-pages/man2/accept.2.html
- ///
- /// WASI implementations have two options to handle this:
- /// - Optionally log it and then skip over non-fatal errors returned by
- /// `accept`. Guest code never gets to see these failures. Or:
- /// - Synthesize a `tcp-socket` resource that exposes the error when
- /// attempting to send or receive on it. Guest code then sees these
- /// failures as regular I/O errors.
- ///
- /// 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
- /// -
- /// -
- /// -
- /// -
- /// -
- /// -
- /// -
- /// -
- @since(version = 0.3.0-rc-2026-03-15)
- listen: func() -> result, error-code>;
-
- /// Transmit data to peer.
- ///
- /// The caller should close the stream when it has no more data to send
- /// to the peer. Under normal circumstances this will cause a FIN packet
- /// to be sent out. Closing the stream is equivalent to calling
- /// `shutdown(SHUT_WR)` in POSIX.
- ///
- /// This function may be called at most once and returns once the full
- /// contents of the stream are transmitted or an error is encountered.
- ///
- /// # Typical errors
- /// - `invalid-state`: The socket is not in the `connected` state. (ENOTCONN)
- /// - `invalid-state`: `send` has already been called on this socket.
- /// - `connection-broken`: The connection is not writable anymore. (EPIPE, ECONNABORTED on Windows)
- /// - `connection-reset`: The connection was reset. (ECONNRESET)
- /// - `remote-unreachable`: The remote address is not reachable. (EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET)
- ///
- /// # References
- /// -
- /// -
- /// -
- /// -
- @since(version = 0.3.0-rc-2026-03-15)
- send: func(data: stream) -> future>;
-
- /// Read data from peer.
- ///
- /// Returns a `stream` of data sent by the peer. The implementation
- /// drops the stream once no more data is available. At that point, the
- /// returned `future` resolves to:
- /// - `ok` after a graceful shutdown from the peer (i.e. a FIN packet), or
- /// - `err` if the socket was closed abnormally.
- ///
- /// `receive` may be called only once per socket. Subsequent calls return
- /// a closed stream and a future resolved to `err(invalid-state)`.
- ///
- /// If the caller is not expecting to receive any more data from the peer,
- /// they should drop the stream. Any data still in the receive queue
- /// will be discarded. This is equivalent to calling `shutdown(SHUT_RD)`
- /// in POSIX.
- ///
- /// # Typical errors
- /// - `invalid-state`: The socket is not in the `connected` state. (ENOTCONN)
- /// - `invalid-state`: `receive` has already been called on this socket.
- /// - `connection-reset`: The connection was reset. (ECONNRESET)
- /// - `remote-unreachable`: The remote address is not reachable. (EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET)
- ///
- /// # References
- /// -
- /// -
- /// -
- /// -
- @since(version = 0.3.0-rc-2026-03-15)
- receive: func() -> tuple, future>>;
-
- /// Get the bound local address.
- ///
- /// POSIX mentions:
- /// > If the socket has not been bound to a local name, the value
- /// > stored in the object pointed to by `address` is unspecified.
- ///
- /// WASI is stricter and requires `get-local-address` to return
- /// `invalid-state` when the socket hasn't been bound yet.
- ///
- /// # Typical errors
- /// - `invalid-state`: The socket is not bound to any local address.
- ///
- /// # References
- /// -
- /// -
- /// -
- /// -
- @since(version = 0.3.0-rc-2026-03-15)
- get-local-address: func() -> result;
-
- /// Get the remote address.
- ///
- /// # Typical errors
- /// - `invalid-state`: The socket is not connected to a remote address. (ENOTCONN)
- ///
- /// # References
- /// -
- /// -
- /// -
- /// -
- @since(version = 0.3.0-rc-2026-03-15)
- 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-03-15)
- 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-03-15)
- get-address-family: func() -> ip-address-family;
-
- /// Hints the desired listen queue size. Implementations are free to
- /// ignore this.
- ///
- /// If the provided value is 0, an `invalid-argument` error is returned.
- /// Any other value will never cause an error, but it might be silently
- /// clamped and/or rounded.
- ///
- /// # Typical errors
- /// - `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-03-15)
- set-listen-backlog-size: func(value: u64) -> result<_, error-code>;
-
- /// Enables or disables keepalive.
- ///
- /// The keepalive behavior can be adjusted using:
- /// - `keep-alive-idle-time`
- /// - `keep-alive-interval`
- /// - `keep-alive-count`
- /// 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-03-15)
- get-keep-alive-enabled: func() -> result;
- @since(version = 0.3.0-rc-2026-03-15)
- 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.
- ///
- /// If the provided value is 0, an `invalid-argument` error is returned.
- /// All other values are accepted without error, but may be
- /// clamped or rounded. As a result, the value read back from
- /// this setting may differ from the value that was set.
- ///
- /// Equivalent to the TCP_KEEPIDLE socket option. (TCP_KEEPALIVE on MacOS)
- ///
- /// # Typical errors
- /// - `invalid-argument`: (set) The provided value was 0.
- @since(version = 0.3.0-rc-2026-03-15)
- get-keep-alive-idle-time: func() -> result;
- @since(version = 0.3.0-rc-2026-03-15)
- set-keep-alive-idle-time: func(value: duration) -> result<_, error-code>;
-
- /// The time between keepalive packets.
- ///
- /// If the provided value is 0, an `invalid-argument` error is returned.
- /// All other values are accepted without error, but may be
- /// clamped or rounded. As a result, the value read back from
- /// this setting may differ from the value that was set.
- ///
- /// Equivalent to the TCP_KEEPINTVL socket option.
- ///
- /// # Typical errors
- /// - `invalid-argument`: (set) The provided value was 0.
- @since(version = 0.3.0-rc-2026-03-15)
- get-keep-alive-interval: func() -> result;
- @since(version = 0.3.0-rc-2026-03-15)
- set-keep-alive-interval: func(value: duration) -> result<_, error-code>;
-
- /// The maximum amount of keepalive packets TCP should send before
- /// aborting the connection.
- ///
- /// If the provided value is 0, an `invalid-argument` error is returned.
- /// All other values are accepted without error, but may be
- /// clamped or rounded. As a result, the value read back from
- /// this setting may differ from the value that was set.
- ///
- /// Equivalent to the TCP_KEEPCNT socket option.
- ///
- /// # Typical errors
- /// - `invalid-argument`: (set) The provided value was 0.
- @since(version = 0.3.0-rc-2026-03-15)
- get-keep-alive-count: func() -> result;
- @since(version = 0.3.0-rc-2026-03-15)
- set-keep-alive-count: func(value: u32) -> result<_, error-code>;
-
- /// Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options.
- ///
- /// If the provided value is 0, an `invalid-argument` error is returned.
- ///
- /// # Typical errors
- /// - `invalid-argument`: (set) The TTL value must be 1 or higher.
- @since(version = 0.3.0-rc-2026-03-15)
- get-hop-limit: func() -> result;
- @since(version = 0.3.0-rc-2026-03-15)
- set-hop-limit: func(value: u8) -> result<_, error-code>;
-
- /// Kernel buffer space reserved for sending/receiving on this socket.
- /// Implementations usually treat this as a cap the buffer can grow to,
- /// rather than allocating the full amount immediately.
- ///
- /// If the provided value is 0, an `invalid-argument` error is returned.
- /// All other values are accepted without error, but may be
- /// clamped or rounded. As a result, the value read back from
- /// this setting may differ from the value that was set.
- ///
- /// This is only a performance hint. The implementation may ignore it or
- /// tweak it based on real traffic patterns.
- /// Linux and macOS appear to behave differently depending on whether a
- /// buffer size was explicitly set. When set, they tend to honor it; when
- /// not set, they dynamically adjust the buffer size as the connection
- /// progresses. This is especially noticeable when comparing the values
- /// from before and after connection establishment.
- ///
- /// Equivalent to the SO_RCVBUF and SO_SNDBUF socket options.
- ///
- /// # Typical errors
- /// - `invalid-argument`: (set) The provided value was 0.
- @since(version = 0.3.0-rc-2026-03-15)
- get-receive-buffer-size: func() -> result;
- @since(version = 0.3.0-rc-2026-03-15)
- set-receive-buffer-size: func(value: u64) -> result<_, error-code>;
- @since(version = 0.3.0-rc-2026-03-15)
- get-send-buffer-size: func() -> result;
- @since(version = 0.3.0-rc-2026-03-15)
- set-send-buffer-size: func(value: u64) -> result<_, error-code>;
- }
-
- /// A UDP socket handle.
- @since(version = 0.3.0-rc-2026-03-15)
- resource udp-socket {
-
- /// Create a new UDP socket.
- ///
- /// Similar to `socket(AF_INET or AF_INET6, SOCK_DGRAM, IPPROTO_UDP)`
- /// in POSIX. On IPv6 sockets, IPV6_V6ONLY is enabled by default and
- /// can't be configured otherwise.
- ///
- /// Unlike POSIX, WASI sockets have no notion of a socket-level
- /// `O_NONBLOCK` flag. Instead they fully rely on the Component Model's
- /// async support.
- ///
- /// # References:
- /// -
- /// -
- /// -
- /// -
- @since(version = 0.3.0-rc-2026-03-15)
- create: static func(address-family: ip-address-family) -> result;
-
- /// Bind the socket to the provided IP address and port.
- ///
- /// If the IP address is zero (`0.0.0.0` in IPv4, `::` in IPv6), it is
- /// left to the implementation to decide which network interface(s) to
- /// bind to. If the port is zero, the socket will be bound to a random
- /// free port.
- ///
- /// # Typical errors
- /// - `invalid-argument`: The `local-address` has the wrong address family. (EAFNOSUPPORT, EFAULT on Windows)
- /// - `invalid-state`: The socket is already bound. (EINVAL)
- /// - `address-in-use`: No ephemeral ports available. (EADDRINUSE, ENOBUFS on Windows)
- /// - `address-in-use`: Address is already in use. (EADDRINUSE)
- /// - `address-not-bindable`: `local-address` is not an address that can be bound to. (EADDRNOTAVAIL)
- ///
- /// # References
- /// -
- /// -
- /// -
- /// -
- @since(version = 0.3.0-rc-2026-03-15)
- bind: func(local-address: ip-socket-address) -> result<_, error-code>;
-
- /// Associate this socket with a specific peer address.
- ///
- /// On success, the `remote-address` of the socket is updated.
- /// The `local-address` may be updated as well, based on the best network
- /// path to `remote-address`. If the socket was not already explicitly
- /// bound, this function will implicitly bind the socket to a random
- /// free port.
- ///
- /// When a UDP socket is "connected", the `send` and `receive` methods
- /// are limited to communicating with that peer only:
- /// - `send` can only be used to send to this destination.
- /// - `receive` will only return datagrams sent from the provided `remote-address`.
- ///
- /// The name "connect" was kept to align with the existing POSIX
- /// terminology. Other than that, this function only changes the local
- /// socket configuration and does not generate any network traffic.
- /// The peer is not aware of this "connection".
- ///
- /// This method may be called multiple times on the same socket to change
- /// its association, but only the most recent one will be effective.
- ///
- /// # Typical errors
- /// - `invalid-argument`: The `remote-address` has the wrong address family. (EAFNOSUPPORT)
- /// - `invalid-argument`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EDESTADDRREQ, EADDRNOTAVAIL)
- /// - `invalid-argument`: The port in `remote-address` is set to 0. (EDESTADDRREQ, EADDRNOTAVAIL)
- /// - `address-in-use`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE, EADDRNOTAVAIL on Linux, EAGAIN on BSD)
- ///
- /// # Implementors note
- /// If the socket is already connected, some platforms (e.g. Linux)
- /// require a disconnect before connecting to a different peer address.
- ///
- /// # References
- /// -
- /// -
- /// -
- /// -
- @since(version = 0.3.0-rc-2026-03-15)
- connect: func(remote-address: ip-socket-address) -> result<_, error-code>;
-
- /// Dissociate this socket from its peer address.
- ///
- /// After calling this method, `send` & `receive` are free to communicate
- /// with any remote address again.
- ///
- /// The POSIX equivalent of this is calling `connect` with an `AF_UNSPEC` address.
- ///
- /// # Typical errors
- /// - `invalid-state`: The socket is not connected.
- ///
- /// # References
- /// -
- /// -
- /// -
- /// -
- @since(version = 0.3.0-rc-2026-03-15)
- disconnect: func() -> result<_, error-code>;
-
- /// Send a message on the socket to a particular peer.
- ///
- /// If the socket is connected, the peer address may be left empty. In
- /// that case this is equivalent to `send` in POSIX. Otherwise it is
- /// equivalent to `sendto`.
- ///
- /// Additionally, if the socket is connected, a `remote-address` argument
- /// _may_ be provided but then it must be identical to the address
- /// passed to `connect`.
- ///
- /// If the socket has not been explicitly bound, it will be
- /// implicitly bound to a random free port.
- ///
- /// Implementations may trap if the `data` length exceeds 64 KiB.
- ///
- /// # Typical errors
- /// - `invalid-argument`: The `remote-address` has the wrong address family. (EAFNOSUPPORT)
- /// - `invalid-argument`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EDESTADDRREQ, EADDRNOTAVAIL)
- /// - `invalid-argument`: The port in `remote-address` is set to 0. (EDESTADDRREQ, EADDRNOTAVAIL)
- /// - `invalid-argument`: The socket is in "connected" mode and `remote-address` is `some` value that does not match the address passed to `connect`. (EISCONN)
- /// - `invalid-argument`: The socket is not "connected" and no value for `remote-address` was provided. (EDESTADDRREQ)
- /// - `remote-unreachable`: The remote address is not reachable. (ECONNRESET, ENETRESET on Windows, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET)
- /// - `connection-refused`: The connection was refused. (ECONNREFUSED)
- /// - `datagram-too-large`: The datagram is too large. (EMSGSIZE)
- /// - `address-in-use`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE)
- ///
- /// # Implementors note
- /// WASI requires `send` to perform an implicit bind if the socket
- /// has not been bound. Not all platforms (notably Windows) exhibit
- /// this behavior natively. On such platforms, the WASI implementation
- /// should emulate it by performing the bind if the guest has not
- /// already done so.
- ///
- /// # References
- /// -
- /// -
- /// -
- /// -
- /// -
- /// -
- /// -
- /// -
- @since(version = 0.3.0-rc-2026-03-15)
- send: async func(data: list, remote-address: option) -> result<_, error-code>;
-
- /// Receive a message on the socket.
- ///
- /// On success, the return value contains a tuple of the received data
- /// and the address of the sender. Theoretical maximum length of the
- /// data is 64 KiB. Though in practice, it will typically be less than
- /// 1500 bytes.
- ///
- /// If the socket is connected, the sender address is guaranteed to
- /// match the remote address passed to `connect`.
- ///
- /// # Typical errors
- /// - `invalid-state`: The socket has not been bound yet.
- /// - `remote-unreachable`: The remote address is not reachable. (ECONNRESET, ENETRESET on Windows, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET)
- /// - `connection-refused`: The connection was refused. (ECONNREFUSED)
- ///
- /// # References
- /// -
- /// -
- /// -
- /// -
- /// -
- /// -
- /// -
- @since(version = 0.3.0-rc-2026-03-15)
- receive: async func() -> result, ip-socket-address>, error-code>;
-
- /// Get the current bound address.
- ///
- /// POSIX mentions:
- /// > If the socket has not been bound to a local name, the value
- /// > stored in the object pointed to by `address` is unspecified.
- ///
- /// WASI is stricter and requires `get-local-address` to return
- /// `invalid-state` when the socket hasn't been bound yet.
- ///
- /// # Typical errors
- /// - `invalid-state`: The socket is not bound to any local address.
- ///
- /// # References
- /// -
- /// -
- /// -
- /// -
- @since(version = 0.3.0-rc-2026-03-15)
- get-local-address: func() -> result;
-
- /// Get the address the socket is currently "connected" to.
- ///
- /// # Typical errors
- /// - `invalid-state`: The socket is not "connected" to a specific remote address. (ENOTCONN)
- ///
- /// # References
- /// -
- /// -
- /// -
- /// -
- @since(version = 0.3.0-rc-2026-03-15)
- 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-03-15)
- get-address-family: func() -> ip-address-family;
-
- /// Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options.
- ///
- /// If the provided value is 0, an `invalid-argument` error is returned.
- ///
- /// # Typical errors
- /// - `invalid-argument`: (set) The TTL value must be 1 or higher.
- @since(version = 0.3.0-rc-2026-03-15)
- get-unicast-hop-limit: func() -> result;
- @since(version = 0.3.0-rc-2026-03-15)
- set-unicast-hop-limit: func(value: u8) -> result<_, error-code>;
-
- /// Kernel buffer space reserved for sending/receiving on this socket.
- /// Implementations usually treat this as a cap the buffer can grow to,
- /// rather than allocating the full amount immediately.
- ///
- /// If the provided value is 0, an `invalid-argument` error is returned.
- /// All other values are accepted without error, but may be
- /// clamped or rounded. As a result, the value read back from
- /// this setting may differ from the value that was set.
- ///
- /// Equivalent to the SO_RCVBUF and SO_SNDBUF socket options.
- ///
- /// # Typical errors
- /// - `invalid-argument`: (set) The provided value was 0.
- @since(version = 0.3.0-rc-2026-03-15)
- get-receive-buffer-size: func() -> result;
- @since(version = 0.3.0-rc-2026-03-15)
- set-receive-buffer-size: func(value: u64) -> result<_, error-code>;
- @since(version = 0.3.0-rc-2026-03-15)
- get-send-buffer-size: func() -> result;
- @since(version = 0.3.0-rc-2026-03-15)
- set-send-buffer-size: func(value: u64) -> result<_, error-code>;
- }
-}
diff --git a/proposals/sockets/wit-0.3.0-draft/world.wit b/proposals/sockets/wit-0.3.0-draft/world.wit
deleted file mode 100644
index 227662da..00000000
--- a/proposals/sockets/wit-0.3.0-draft/world.wit
+++ /dev/null
@@ -1,9 +0,0 @@
-package wasi:sockets@0.3.0-rc-2026-03-15;
-
-@since(version = 0.3.0-rc-2026-03-15)
-world imports {
- @since(version = 0.3.0-rc-2026-03-15)
- import types;
- @since(version = 0.3.0-rc-2026-03-15)
- import ip-name-lookup;
-}