Repository: bytecodealliance/wasmtime
Pull request #7282 (2023-10-18 17:11:58)

Gate many CLI/Wasmtime features behind Cargo features
This PR is borne out of discussions from last week's CG meetings about the suitability of Wasmtime in embedded scenarios. One of the primary concerns that arose was the binary size of the Wasmtime engine being too large for these scenarios. In my experience binary size is one aspect of Rust where it doesn't come for free so this aligns with my expectations. That being said I've yet to see a case in Rust where a particular binary footprint couldn't be achieved, hence this PR.

One thing I've noticed is that many folks evaluating Wasmtime seem to look at the size of the `wasmtime` executable itself when performing size comparisons. While this likely doesn't reflect the true size of what a custom embedding would be it's nevertheless an easy comparison that can be made. To that effect I've added a number of Cargo features to the `wasmtime-cli` crate to disable functionality up to and including `wasmtime compile` for example. This PR enables building a `wasmtime` exectuable that is stripped down to the bare bones - all it can do is run precompiled WebAssembly files. That being said it should still be a full-featured runtime at this time where only some optional features such as DWARF processing for `addr2line` are disabled.

Additionally in this PR I've added a new chapter to the Wasmtime book about building a minimal build of Wasmtime. This explains not only the existence of the `--no-default-features` build flag but additionally the consequences in terms of functionality and binary size. Furthermore this section additionally directly expands on tips and tricks for building a minimal binary using various Rust compiler flags and nightly features. This section shows how the `wasmtime` CLI is 130M in debug mode and the smallest build producable after this PR is 2.1M. 

The goal of this PR is to get the ball rolling on size-related optimizations, not necessarily be the end-all-be-all. Further optimizations to binary size will likely require more invasive changes about how we develop Wasmtime or similar. For example we may have to lose the relatively rich error information Wasmtime currently has to get the next level of significant win. That being said I'm at least personally lacking a bit in direction for where to go next to optimize. My hope is that by showcasing a much smaller `wasmtime` CLI executable it shows to folks that Wasmtime for these use cases is viable, even if it's not suitable just yet. For example the CLI uses crates such as `clap` which won't be present in custom embeddings. Having an actual custom embedding to optimize for will be useful. In lieu of this I plan on continuing to poke to see what we can do.

One option as a result of this PR is to build a `wasmtime-lite` executable on CI and upload it to GitHub Actions/Releases as well. I'm not totally convinced that'd be too useful so I haven't done that. Another option though is to add a CI builder which builds a minimal binary and verifies that it doesn't go above a particular threshold to catch regressions in binary size (e.g. if a new feature is added ensure that it can be disabled with `--no-default-features`). I've left this for a future PR as well.

I should also note that my hope is that when optimizing for binary size it would not hinder the development of Wasmtime anywhere else. In that sense it should ideally be easy for anyone to understand how the conditionally compiled code works and it shouldn't appear as a maze of cfgs to wade through to get things working. While it's definitely more `#[cfg]` than before, I'm particularly interested if folks feel this is too onerous to maintain or burdensome. If so I'd be interested in improving how this is all designed.

github-actions[bot]: #### Subscribe to Label Action

cc @peterhuene

<details>
This issue or pull request has been labeled: "wasmtime:api", "wasmtime:c-api", "wasmtime:config", "wasmtime:docs"

Thus the following users have been cc'd because of the following labels:

* peterhuene: wasmtime:api, wasmtime:c-api

To subscribe or unsubscribe from this label, edit the <code>.github/subscribe-to-label.json</code> configuration file.

[Learn more.](https://github.com/bytecodealliance/subscribe-to-label-action)
</details>
github-actions[bot]: #### Label Messager: wasmtime:config

It looks like you are changing Wasmtime's configuration options. Make sure to
complete this check list:

* [ ] If you added a new `Config` method, you wrote extensive documentation for
      it.

  <details>

  Our documentation should be of the following form:

  ```text
  Short, simple summary sentence.

  More details. These details can be multiple paragraphs. There should be
  information about not just the method, but its parameters and results as
  well.

  Is this method fallible? If so, when can it return an error?

  Can this method panic? If so, when does it panic?

  # Example

  Optional example here.
  ```

  </details>

* [ ] If you added a new `Config` method, or modified an existing one, you
  ensured that this configuration is exercised by the fuzz targets.

  <details>

  For example, if you expose a new strategy for allocating the next instance
  slot inside the pooling allocator, you should ensure that at least one of our
  fuzz targets exercises that new strategy.

  Often, all that is required of you is to ensure that there is a knob for this
  configuration option in [`wasmtime_fuzzing::Config`][fuzzing-config] (or one
  of its nested `struct`s).

  Rarely, this may require authoring a new fuzz target to specifically test this
  configuration. See [our docs on fuzzing][fuzzing-docs] for more details.

  </details>

* [ ] If you are enabling a configuration option by default, make sure that it
  has been fuzzed for at least two weeks before turning it on by default.

[fuzzing-config]: https://github.com/bytecodealliance/wasmtime/blob/ca0e8d0a1d8cefc0496dba2f77a670571d8fdcab/crates/fuzzing/src/generators.rs#L182-L194
[fuzzing-docs]: https://docs.wasmtime.dev/contributing-fuzzing.html


--------------------------------------------------------------------------------

<details>

To modify this label's message, edit the <code>.github/label-messager/wasmtime-config.md</code> file.

To add new label messages or remove existing label messages, edit the
<code>.github/label-messager.json</code> configuration file.

[Learn more.](https://github.com/bytecodealliance/label-messager-action)

</details>

Patch:
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 110b0ce6b..2df6de8fa 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -257,8 +257,7 @@ jobs:
     # Check some feature combinations of the `wasmtime` crate
     - run: cargo check -p wasmtime --no-default-features
     - run: cargo check -p wasmtime --no-default-features --features wat
-    - run: cargo check -p wasmtime --no-default-features --features jitdump
-    - run: cargo check -p wasmtime --no-default-features --features vtune
+    - run: cargo check -p wasmtime --no-default-features --features profiling
     - run: cargo check -p wasmtime --no-default-features --features cache
     - run: cargo check -p wasmtime --no-default-features --features async
     - run: cargo check -p wasmtime --no-default-features --features pooling-allocator
@@ -267,9 +266,14 @@ jobs:
     - run: cargo check -p wasmtime --no-default-features --features cranelift,wat,async,cache
     - run: cargo check -p wasmtime --no-default-features --features winch
     - run: cargo check -p wasmtime --no-default-features --features wmemcheck
+    - run: cargo check -p wasmtime --no-default-features --features demangle
+    - run: cargo check -p wasmtime --no-default-features --features addr2line
     - run: cargo check --features component-model
     - run: cargo check -p wasmtime --features incremental-cache
 
+    # Feature combinations of the `wasmtime-cli`
+    - run: cargo check -p wasmtime-cli --no-default-features
+
     # Check that benchmarks of the cranelift project build
     - run: cargo check --benches -p cranelift-codegen
 
diff --git a/Cargo.lock b/Cargo.lock
index b35c505b5..30a379510 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3379,7 +3379,6 @@ dependencies = [
  "criterion",
  "env_logger 0.10.0",
  "filecheck",
- "http-body",
  "http-body-util",
  "hyper",
  "libc",
@@ -3397,8 +3396,8 @@ dependencies = [
  "tempfile",
  "test-programs-artifacts",
  "tokio",
+ "tracing",
  "walkdir",
- "wasm-encoder",
  "wasmparser",
  "wasmtime",
  "wasmtime-cache",
diff --git a/Cargo.toml b/Cargo.toml
index 1fd35e8ed..0bf3a99af 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -22,13 +22,13 @@ path = "src/bin/wasmtime.rs"
 doc = false
 
 [dependencies]
-wasmtime = { workspace = true, features = ['cache', 'cranelift'] }
-wasmtime-cache = { workspace = true }
+wasmtime = { workspace = true }
+wasmtime-cache = { workspace = true, optional = true }
 wasmtime-cli-flags = { workspace = true }
-wasmtime-cranelift = { workspace = true }
+wasmtime-cranelift = { workspace = true, optional = true }
 wasmtime-environ = { workspace = true }
-wasmtime-explorer = { workspace = true }
-wasmtime-wast = { workspace = true }
+wasmtime-explorer = { workspace = true, optional = true }
+wasmtime-wast = { workspace = true, optional = true }
 wasmtime-wasi = { workspace = true, default-features = true, features = [
   "exit",
 ] }
@@ -41,18 +41,18 @@ anyhow = { workspace = true }
 target-lexicon = { workspace = true }
 once_cell = { workspace = true }
 listenfd = "1.0.0"
-wat = { workspace = true }
+wat = { workspace = true, optional = true }
 serde = { workspace = true }
 serde_derive = { workspace = true }
 serde_json = { workspace = true }
 wasmparser = { workspace = true }
-wasm-encoder = { workspace = true }
+tracing = { workspace = true }
+log = { workspace = true }
 
 async-trait = { workspace = true }
 tokio = { workspace = true, optional = true, features = [ "signal", "macros" ] }
 hyper = { workspace = true, optional = true }
-http-body = { workspace = true }
-http-body-util = { workspace = true }
+http-body-util = { workspace = true, optional = true }
 
 [target.'cfg(unix)'.dependencies]
 rustix = { workspace = true, features = ["mm", "param"] }
@@ -266,34 +266,87 @@ test-log = { version = "0.2", default-features = false, features = ["trace"] }
 tracing-subscriber = { version = "0.3.1", default-features = false, features = ['fmt', 'env-filter', 'ansi', 'tracing-log'] }
 url = "2.3.1"
 
+# =============================================================================
+#
+# Features for the Wasmtime CLI executable
+#
+#
+# Note that many of these features are inherited from Wasmtime itself or
+# otherwise configure the `wasmtime` crate's execution. Features are provided as
+# compile-time switches to disable functionality primarily if one is interested
+# in configuring binary size and or exploring the binary size implications of
+# various features. Most features are enabled by default but most embeddings
+# likely won't need all features.
 [features]
 default = [
-  "jitdump",
-  "wasmtime/wat",
-  "wasmtime/parallel-compilation",
-  "vtune",
+  # All subcommands are included by default.
+  "compile",
+  "explore",
+  "serve",
+  "wast",
+  "config",
+
+  # On-by-default WASI features
   "wasi-nn",
   "wasi-threads",
   "wasi-http",
+
+  # Most features of Wasmtime are enabled by default.
+  "wat",
+  "parallel-compilation",
   "pooling-allocator",
+  "cache",
+  "logging",
+  "demangle",
+  "cranelift",
+  "profiling",
+  "coredump",
+  "addr2line",
 ]
-jitdump = ["wasmtime/jitdump"]
-vtune = ["wasmtime/vtune"]
+
+# ========================================
+# Off-by-default features
+#
+# These features are off-by-default but may optionally be enabled.
+all-arch = ["wasmtime/all-arch"]
+winch = ["wasmtime/winch"]
+wmemcheck = ["wasmtime/wmemcheck"]
+
+# This feature, when enabled, will statically compile out all logging statements
+# throughout Wasmtime and its dependencies.
+disable-logging = ["log/max_level_off", "tracing/max_level_off"]
+
+# ========================================
+# On-by-default features
+#
+# These features are all included in the `default` set above and this is
+# the internal mapping for what they enable in Wasmtime itself.
 wasi-nn = ["dep:wasmtime-wasi-nn"]
 wasi-threads = ["dep:wasmtime-wasi-threads"]
 wasi-http = ["component-model", "dep:wasmtime-wasi-http", "dep:tokio", "dep:hyper", "wasmtime-wasi-http?/sync"]
 pooling-allocator = ["wasmtime/pooling-allocator", "wasmtime-cli-flags/pooling-allocator"]
-all-arch = ["wasmtime/all-arch"]
 component-model = [
   "wasmtime/component-model",
   "wasmtime-wast/component-model",
   "wasmtime-cli-flags/component-model"
 ]
-winch = ["wasmtime/winch"]
-wmemcheck = ["wasmtime/wmemcheck"]
+wat = ["dep:wat"]
+cache = ["dep:wasmtime-cache", "wasmtime-cli-flags/cache"]
+parallel-compilation = ["wasmtime-cli-flags/parallel-compilation"]
+logging = ["wasmtime-cli-flags/logging"]
+demangle = ["wasmtime/demangle"]
+cranelift = ["wasmtime-cli-flags/cranelift", "dep:wasmtime-cranelift"]
+profiling = ["wasmtime/profiling"]
+coredump = ["wasmtime-cli-flags/coredump"]
+addr2line = ["wasmtime/addr2line"]
 
-# Enable the `wasmtime serve` command
-serve = ["wasi-http", "component-model"]
+# CLI subcommands for the `wasmtime` executable. See `wasmtime $cmd --help`
+# for more information on each subcommand.
+serve = ["wasi-http", "component-model", "dep:http-body-util"]
+explore = ["dep:wasmtime-explorer"]
+wast = ["dep:wasmtime-wast"]
+config = ["cache"]
+compile = ["cranelift"]
 
 [[test]]
 name = "host_segfault"
diff --git a/cranelift/codegen/src/isa/riscv64/inst.isle b/cranelift/codegen/src/isa/riscv64/inst.isle
index 921e144ae..69413862f 100644
--- a/cranelift/codegen/src/isa/riscv64/inst.isle
+++ b/cranelift/codegen/src/isa/riscv64/inst.isle
@@ -1888,9 +1888,13 @@
 (decl pure partial i8_to_imm5 (i8) Imm5)
 (extern constructor i8_to_imm5 i8_to_imm5)
 
+;; Helper to go directly from a `Value` to an `Imm5`.
+(decl imm5_from_value (Imm5) Value)
+(extractor (imm5_from_value n) (i64_from_iconst (imm5_from_i64 n)))
+
 ;; Constructor that matches a `Value` equivalent to a replicated Imm5 on all lanes.
 (decl pure partial replicated_imm5 (Value) Imm5)
-(rule (replicated_imm5 (splat (i64_from_iconst (imm5_from_i64 n)))) n)
+(rule (replicated_imm5 (splat (imm5_from_value n))) n)
 (rule (replicated_imm5 (vconst (u128_from_constant n128)))
   (if-let (u128_replicated_u64 n64) n128)
   (if-let (u64_replicated_u32 n32) n64)
diff --git a/cranelift/codegen/src/isa/riscv64/lower.isle b/cranelift/codegen/src/isa/riscv64/lower.isle
index c5bebc4c5..9df463969 100644
--- a/cranelift/codegen/src/isa/riscv64/lower.isle
+++ b/cranelift/codegen/src/isa/riscv64/lower.isle
@@ -2700,18 +2700,23 @@
 
 ;;;; Rules for `scalar_to_vector` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
+(rule 0 (lower (has_type (ty_vec_fits_in_register ty) (scalar_to_vector x)))
+  (if (ty_vector_float ty))
+  (let ((zero VReg (rv_vmv_vx (zero_reg) ty))
+        (elem VReg (rv_vfmv_sf x ty))
+        (mask VReg (gen_vec_mask 1)))
+    (rv_vmerge_vvm zero elem mask ty)))
+
 (rule 1 (lower (has_type (ty_vec_fits_in_register ty) (scalar_to_vector x)))
   (if (ty_vector_not_float ty))
   (let ((zero VReg (rv_vmv_vx (zero_reg) ty))
         (mask VReg (gen_vec_mask 1)))
     (rv_vmerge_vxm zero x mask ty)))
 
-(rule 0 (lower (has_type (ty_vec_fits_in_register ty) (scalar_to_vector x)))
-  (if (ty_vector_float ty))
+(rule 2 (lower (has_type (ty_vec_fits_in_register ty) (scalar_to_vector (imm5_from_value x))))
   (let ((zero VReg (rv_vmv_vx (zero_reg) ty))
-        (elem VReg (rv_vfmv_sf x ty))
         (mask VReg (gen_vec_mask 1)))
-    (rv_vmerge_vvm zero elem mask ty)))
+    (rv_vmerge_vim zero x mask ty)))
 
 ;;;; Rules for `sqmul_round_sat` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
diff --git a/crates/c-api/Cargo.toml b/crates/c-api/Cargo.toml
index 47ec57d6c..3d359437e 100644
--- a/crates/c-api/Cargo.toml
+++ b/crates/c-api/Cargo.toml
@@ -36,9 +36,9 @@ wasi-common = { workspace = true, optional = true }
 futures = { workspace = true, optional = true }
 
 [features]
-default = ['jitdump', 'wat', 'wasi', 'cache', 'parallel-compilation', 'async']
+default = ['profiling', 'wat', 'wasi', 'cache', 'parallel-compilation', 'async']
 async = ['wasmtime/async', 'futures']
-jitdump = ["wasmtime/jitdump"]
+profiling = ["wasmtime/profiling"]
 cache = ["wasmtime/cache"]
 parallel-compilation = ['wasmtime/parallel-compilation']
 wasi = ['wasi-cap-std-sync', 'wasmtime-wasi', 'cap-std', 'wasi-common']
diff --git a/crates/cli-flags/Cargo.toml b/crates/cli-flags/Cargo.toml
index 725c76267..0b7665220 100644
--- a/crates/cli-flags/Cargo.toml
+++ b/crates/cli-flags/Cargo.toml
@@ -11,19 +11,17 @@ edition.workspace = true
 [dependencies]
 anyhow = { workspace = true }
 clap = { workspace = true }
-file-per-thread-logger = { workspace = true }
-tracing-subscriber = { workspace = true }
-rayon = "1.5.0"
+file-per-thread-logger = { workspace = true, optional = true }
+tracing-subscriber = { workspace = true, optional = true }
+rayon = { version = "1.5.0", optional = true }
 wasmtime = { workspace = true }
 humantime = "2.0.0"
 
 [features]
-default = [
-    "wasmtime/cache",
-    "wasmtime/cranelift",
-    "wasmtime/jitdump",
-    "wasmtime/vtune",
-    "wasmtime/parallel-compilation",
-]
 pooling-allocator = []
 component-model = ["wasmtime/component-model"]
+cache = ["wasmtime/cache"]
+parallel-compilation = ["wasmtime/parallel-compilation", "dep:rayon"]
+logging = ["dep:file-per-thread-logger", "dep:tracing-subscriber"]
+cranelift = ["wasmtime/cranelift"]
+coredump = ["wasmtime/coredump"]
diff --git a/crates/cli-flags/src/lib.rs b/crates/cli-flags/src/lib.rs
index 63ef2491d..31b0232b2 100644
--- a/crates/cli-flags/src/lib.rs
+++ b/crates/cli-flags/src/lib.rs
@@ -5,13 +5,12 @@
 
 use anyhow::Result;
 use clap::Parser;
-use std::io::IsTerminal;
 use std::time::Duration;
-use tracing_subscriber::{EnvFilter, FmtSubscriber};
 use wasmtime::Config;
 
 pub mod opt;
 
+#[cfg(feature = "logging")]
 fn init_file_per_thread_logger(prefix: &'static str) {
     file_per_thread_logger::initialize(prefix);
 
@@ -19,6 +18,7 @@ fn init_file_per_thread_logger(prefix: &'static str) {
     // https://docs.rs/rayon/1.1.0/rayon/struct.ThreadPoolBuilder.html#method.spawn_handler
     // Source code says DefaultSpawner is implementation detail and
     // shouldn't be used directly.
+    #[cfg(feature = "parallel-compilation")]
     rayon::ThreadPoolBuilder::new()
         .spawn_handler(move |thread| {
             let mut b = std::thread::Builder::new();
@@ -89,7 +89,7 @@ wasmtime_option_group! {
         /// Whether or not to enable parallel compilation of modules.
         pub parallel_compilation: Option<bool>,
         /// Whether to enable proof-carrying code (PCC)-based validation.
-        pub enable_pcc: Option<bool>,
+        pub pcc: Option<bool>,
 
         #[prefixed = "cranelift"]
         /// Set a cranelift-specific option. Use `wasmtime settings` to see
@@ -310,6 +310,27 @@ pub struct CommonOptions {
     pub wasi: WasiOptions,
 }
 
+macro_rules! match_feature {
+    (
+        [$feat:tt : $config:expr]
+        $val:ident => $e:expr,
+        $p:pat => err,
+    ) => {
+        #[cfg(feature = $feat)]
+        {
+            if let Some($val) = $config {
+                $e;
+            }
+        }
+        #[cfg(not(feature = $feat))]
+        {
+            if let Some($p) = $config {
+                anyhow::bail!(concat!("support for ", $feat, " disabled at compile time"));
+            }
+        }
+    };
+}
+
 impl CommonOptions {
     fn configure(&mut self) {
         if self.configured {
@@ -323,15 +344,18 @@ impl CommonOptions {
         self.wasi.configure_with(&self.wasi_raw);
     }
 
-    pub fn init_logging(&mut self) {
+    pub fn init_logging(&mut self) -> Result<()> {
         self.configure();
         if self.debug.logging == Some(false) {
-            return;
+            return Ok(());
         }
+        #[cfg(feature = "logging")]
         if self.debug.log_to_files == Some(true) {
             let prefix = "wasmtime.dbg.";
             init_file_per_thread_logger(prefix);
         } else {
+            use std::io::IsTerminal;
+            use tracing_subscriber::{EnvFilter, FmtSubscriber};
             let mut b = FmtSubscriber::builder()
                 .with_writer(std::io::stderr)
                 .with_env_filter(EnvFilter::from_env("WASMTIME_LOG"));
@@ -340,43 +364,60 @@ impl CommonOptions {
             }
             b.init();
         }
+        #[cfg(not(feature = "logging"))]
+        if self.debug.log_to_files == Some(true) || self.debug.logging == Some(true) {
+            anyhow::bail!("support for logging disabled at compile time");
+        }
+        Ok(())
     }
 
     pub fn config(&mut self, target: Option<&str>) -> Result<Config> {
         self.configure();
         let mut config = Config::new();
 
-        if let Some(strategy) = self.codegen.compiler {
-            config.strategy(strategy);
+        match_feature! {
+            ["cranelift" : self.codegen.compiler]
+            strategy => config.strategy(strategy),
+            _ => err,
         }
-
-        // Set the target before setting any cranelift options, since the
-        // target will reset any target-specific options.
-        if let Some(target) = target {
-            config.target(target)?;
+        match_feature! {
+            ["cranelift" : target]
+            target => config.target(target)?,
+            _ => err,
         }
-
-        if let Some(enable) = self.codegen.cranelift_debug_verifier {
-            config.cranelift_debug_verifier(enable);
+        match_feature! {
+            ["cranelift" : self.codegen.cranelift_debug_verifier]
+            enable => config.cranelift_debug_verifier(enable),
+            true => err,
         }
         if let Some(enable) = self.debug.debug_info {
             config.debug_info(enable);
         }
         if self.debug.coredump.is_some() {
+            #[cfg(feature = "coredump")]
             config.coredump_on_trap(true);
+            #[cfg(not(feature = "coredump"))]
+            anyhow::bail!("support for coredumps disabled at compile time");
         }
-        if let Some(level) = self.opts.opt_level {
-            config.cranelift_opt_level(level);
+        match_feature! {
+            ["cranelift" : self.opts.opt_level]
+            level => config.cranelift_opt_level(level),
+            _ => err,
         }
-        if let Some(enable) = self.wasm.nan_canonicalization {
-            config.cranelift_nan_canonicalization(enable);
+        match_feature! {
+            ["cranelift" : self.wasm.nan_canonicalization]
+            enable => config.cranelift_nan_canonicalization(enable),
+            true => err,
         }
-        if let Some(enable) = self.codegen.enable_pcc {
-            config.cranelift_pcc(enable);
+        match_feature! {
+            ["cranelift" : self.codegen.pcc]
+            enable => config.cranelift_pcc(enable),
+            true => err,
         }
 
         self.enable_wasm_features(&mut config)?;
 
+        #[cfg(feature = "cranelift")]
         for (name, value) in self.codegen.cranelift.iter() {
             let name = name.replace('-', "_");
             unsafe {
@@ -390,7 +431,12 @@ impl CommonOptions {
                 }
             }
         }
+        #[cfg(not(feature = "cranelift"))]
+        if !self.codegen.cranelift.is_empty() {
+            anyhow::bail!("support for cranelift disabled at compile time");
+        }
 
+        #[cfg(feature = "cache")]
         if self.codegen.cache != Some(false) {
             match &self.codegen.cache_config {
                 Some(path) => {
@@ -401,9 +447,15 @@ impl CommonOptions {
                 }
             }
         }
+        #[cfg(not(feature = "cache"))]
+        if self.codegen.cache == Some(true) {
+            anyhow::bail!("support for caching disabled at compile time");
+        }
 
-        if let Some(enable) = self.codegen.parallel_compilation {
-            config.parallel_compilation(enable);
+        match_feature! {
+            ["parallel-compilation" : self.codegen.parallel_compilation]
+            enable => config.parallel_compilation(enable),
+            true => err,
         }
 
         if let Some(max) = self.opts.static_memory_maximum_size {
@@ -440,11 +492,14 @@ impl CommonOptions {
             config.memory_init_cow(enable);
         }
 
-        if self.opts.pooling_allocator == Some(true) {
-            #[cfg(feature = "pooling-allocator")]
-            config.allocation_strategy(wasmtime::InstanceAllocationStrategy::pooling());
-            #[cfg(not(feature = "pooling-allocator"))]
-            anyhow::bail!("support for the pooling allocator was disabled at compile-time");
+        match_feature! {
+            ["pooling-allocator" : self.opts.pooling_allocator]
+            enable => {
+                if enable {
+                    config.allocation_strategy(wasmtime::InstanceAllocationStrategy::pooling());
+                }
+            },
+            true => err,
         }
 
         if let Some(max) = self.wasm.max_wasm_stack {
@@ -454,8 +509,10 @@ impl CommonOptions {
         if let Some(enable) = self.wasm.relaxed_simd_deterministic {
             config.relaxed_simd_deterministic(enable);
         }
-        if let Some(enable) = self.wasm.wmemcheck {
-            config.wmemcheck(enable);
+        match_feature! {
+            ["cranelift" : self.wasm.wmemcheck]
+            enable => config.wmemcheck(enable),
+            true => err,
         }
 
         Ok(config)
diff --git a/crates/fuzzing/src/generators/config.rs b/crates/fuzzing/src/generators/config.rs
index 5a29e689f..74b0bfb72 100644
--- a/crates/fuzzing/src/generators/config.rs
+++ b/crates/fuzzing/src/generators/config.rs
@@ -171,6 +171,10 @@ impl Config {
             .allocation_strategy(self.wasmtime.strategy.to_wasmtime())
             .generate_address_map(self.wasmtime.generate_address_map);
 
+        if !self.module_config.config.simd_enabled {
+            cfg.wasm_relaxed_simd(false);
+        }
+
         let compiler_strategy = &self.wasmtime.compiler_strategy;
         let cranelift_strategy = *compiler_strategy == CompilerStrategy::Cranelift;
         cfg.strategy(self.wasmtime.compiler_strategy.to_wasmtime());
diff --git a/crates/jit/Cargo.toml b/crates/jit/Cargo.toml
index 56d8eb223..cf7b4bdb4 100644
--- a/crates/jit/Cargo.toml
+++ b/crates/jit/Cargo.toml
@@ -23,10 +23,10 @@ gimli = { workspace = true }
 object = { workspace = true }
 serde = "1.0.188"
 serde_derive = "1.0.188"
-addr2line = { version = "0.21.0", default-features = false }
+addr2line = { version = "0.21.0", default-features = false, optional = true }
 bincode = "1.2.1"
-rustc-demangle = "0.1.16"
-cpp_demangle = "0.3.2"
+rustc-demangle = { version = "0.1.16", optional = true }
+cpp_demangle = { version = "0.3.2", optional = true }
 log = { workspace = true }
 wasmtime-jit-icache-coherence = { workspace = true }
 
@@ -41,5 +41,5 @@ features = ["Win32_System_Diagnostics_Debug"]
 ittapi = { version = "0.3.4", optional = true }
 
 [features]
-jitdump = ['wasmtime-jit-debug']
-vtune = ['ittapi']
+profiling = ['dep:wasmtime-jit-debug', 'dep:ittapi']
+demangle = ['dep:rustc-demangle', 'dep:cpp_demangle']
diff --git a/crates/jit/src/demangling.rs b/crates/jit/src/demangling.rs
index 83b07d6f8..d8116aa96 100644
--- a/crates/jit/src/demangling.rs
+++ b/crates/jit/src/demangling.rs
@@ -4,13 +4,14 @@
 ///
 /// Currently supported: Rust/C/C++ function names.
 pub fn demangle_function_name(writer: &mut impl std::fmt::Write, name: &str) -> std::fmt::Result {
+    #[cfg(feature = "demangle")]
     if let Ok(demangled) = rustc_demangle::try_demangle(name) {
-        write!(writer, "{}", demangled)
+        return write!(writer, "{}", demangled);
     } else if let Ok(demangled) = cpp_demangle::Symbol::new(name) {
-        write!(writer, "{}", demangled)
-    } else {
-        write!(writer, "{}", name)
+        return write!(writer, "{}", demangled);
     }
+
+    write!(writer, "{}", name)
 }
 
 /// Demangles a function name if it's provided, or returns a unified representation based on the
diff --git a/crates/jit/src/instantiate.rs b/crates/jit/src/instantiate.rs
index d3a260deb..a991c9a4c 100644
--- a/crates/jit/src/instantiate.rs
+++ b/crates/jit/src/instantiate.rs
@@ -667,6 +667,7 @@ impl CompiledModule {
     ///
     /// Basically this makes a thing which parses debuginfo and can tell you
     /// what filename and line number a wasm pc comes from.
+    #[cfg(feature = "addr2line")]
     pub fn symbolize_context(&self) -> Result<Option<SymbolizeContext<'_>>> {
         use gimli::EndianSlice;
         if !self.meta.has_wasm_debuginfo {
@@ -721,15 +722,18 @@ impl CompiledModule {
     }
 }
 
+#[cfg(feature = "addr2line")]
 type Addr2LineContext<'a> = addr2line::Context<gimli::EndianSlice<'a, gimli::LittleEndian>>;
 
 /// A context which contains dwarf debug information to translate program
 /// counters back to filenames and line numbers.
+#[cfg(feature = "addr2line")]
 pub struct SymbolizeContext<'a> {
     inner: Addr2LineContext<'a>,
     code_section_offset: u64,
 }
 
+#[cfg(feature = "addr2line")]
 impl<'a> SymbolizeContext<'a> {
     /// Returns access to the [`addr2line::Context`] which can be used to query
     /// frame information with.
diff --git a/crates/jit/src/lib.rs b/crates/jit/src/lib.rs
index e3afd3bf4..677575183 100644
--- a/crates/jit/src/lib.rs
+++ b/crates/jit/src/lib.rs
@@ -11,9 +11,10 @@ pub mod profiling;
 mod unwind;
 
 pub use crate::code_memory::CodeMemory;
+#[cfg(feature = "addr2line")]
+pub use crate::instantiate::SymbolizeContext;
 pub use crate::instantiate::{
     subslice_range, CompiledFunctionInfo, CompiledModule, CompiledModuleInfo, ObjectBuilder,
-    SymbolizeContext,
 };
 pub use demangling::*;
 
diff --git a/crates/jit/src/profiling.rs b/crates/jit/src/profiling.rs
index 06edec82f..bb05e5506 100644
--- a/crates/jit/src/profiling.rs
+++ b/crates/jit/src/profiling.rs
@@ -5,7 +5,7 @@ use crate::CodeMemory;
 use anyhow::{bail, Result};
 
 cfg_if::cfg_if! {
-    if #[cfg(all(feature = "jitdump", target_os = "linux"))] {
+    if #[cfg(all(feature = "profiling", target_os = "linux"))] {
         mod jitdump;
         pub use jitdump::new as new_jitdump;
     } else {
@@ -33,7 +33,7 @@ cfg_if::cfg_if! {
 cfg_if::cfg_if! {
     // Note: VTune support is disabled on windows mingw because the ittapi crate doesn't compile
     // there; see also https://github.com/bytecodealliance/wasmtime/pull/4003 for rationale.
-    if #[cfg(all(feature = "vtune", target_arch = "x86_64", not(any(target_os = "android", all(target_os = "windows", target_env = "gnu")))))] {
+    if #[cfg(all(feature = "profiling", target_arch = "x86_64", not(any(target_os = "android", all(target_os = "windows", target_env = "gnu")))))] {
         mod vtune;
         pub use vtune::new as new_vtune;
     } else {
diff --git a/crates/wasi-http/src/proxy.rs b/crates/wasi-http/src/proxy.rs
index 0f8d55221..155a55fd1 100644
--- a/crates/wasi-http/src/proxy.rs
+++ b/crates/wasi-http/src/proxy.rs
@@ -2,7 +2,7 @@ use crate::{bindings, WasiHttpView};
 use wasmtime_wasi::preview2;
 
 wasmtime::component::bindgen!({
-    world: "wasi:http/proxy@0.2.0-rc-2023-10-18",
+    world: "wasi:http/proxy",
     tracing: true,
     async: true,
     with: {
@@ -40,7 +40,7 @@ pub mod sync {
     use wasmtime_wasi::preview2;
 
     wasmtime::component::bindgen!({
-        world: "wasi:http/proxy@0.2.0-rc-2023-10-18",
+        world: "wasi:http/proxy",
         tracing: true,
         async: false,
         with: {
diff --git a/crates/wasi-http/wit/deps/sockets/instance-network.wit b/crates/wasi-http/wit/deps/sockets/instance-network.wit
index 14e4479e6..e455d0ff7 100644
--- a/crates/wasi-http/wit/deps/sockets/instance-network.wit
+++ b/crates/wasi-http/wit/deps/sockets/instance-network.wit
@@ -1,9 +1,9 @@
 
 /// This interface provides a value-export of the default network handle..
 interface instance-network {
-	use network.{network};
+    use network.{network};
 
-	/// Get a handle to the default network.
-	instance-network: func() -> network;
+    /// Get a handle to the default network.
+    instance-network: func() -> network;
 
 }
diff --git a/crates/wasi-http/wit/deps/sockets/ip-name-lookup.wit b/crates/wasi-http/wit/deps/sockets/ip-name-lookup.wit
index f2dab32ff..4f9432991 100644
--- a/crates/wasi-http/wit/deps/sockets/ip-name-lookup.wit
+++ b/crates/wasi-http/wit/deps/sockets/ip-name-lookup.wit
@@ -1,61 +1,61 @@
 
 interface ip-name-lookup {
-	use wasi:io/poll@0.2.0-rc-2023-10-18.{pollable};
-	use network.{network, error-code, ip-address, ip-address-family};
+    use wasi:io/poll@0.2.0-rc-2023-10-18.{pollable};
+    use network.{network, error-code, ip-address, ip-address-family};
 
 
-	/// Resolve an internet host name to a list of IP addresses.
-	///
-	/// See the wasi-socket proposal README.md for a comparison with getaddrinfo.
-	///
-	/// # Parameters
-	/// - `name`: The name to look up. IP addresses are not allowed. Unicode domain names are automatically converted
-	///     to ASCII using IDNA encoding.
-	/// - `address-family`: If provided, limit the results to addresses of this specific address family.
-	/// - `include-unavailable`: When set to true, this function will also return addresses of which the runtime
-	///   thinks (or knows) can't be connected to at the moment. For example, this will return IPv6 addresses on
-	///   systems without an active IPv6 interface. Notes:
-	///     - Even when no public IPv6 interfaces are present or active, names like "localhost" can still resolve to an IPv6 address.
-	///     - Whatever is "available" or "unavailable" is volatile and can change everytime a network cable is unplugged.
-	///
-	/// This function never blocks. It either immediately fails or immediately returns successfully with a `resolve-address-stream`
-	/// that can be used to (asynchronously) fetch the results.
-	///
-	/// At the moment, the stream never completes successfully with 0 items. Ie. the first call
-	/// to `resolve-next-address` never returns `ok(none)`. This may change in the future.
-	///
-	/// # Typical errors
-	/// - `invalid-argument`:     `name` is a syntactically invalid domain name.
-	/// - `invalid-argument`:     `name` is an IP address.
-	/// - `not-supported`:        The specified `address-family` is not supported. (EAI_FAMILY)
-	///
-	/// # References:
-	/// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/getaddrinfo.html>
-	/// - <https://man7.org/linux/man-pages/man3/getaddrinfo.3.html>
-	/// - <https://learn.microsoft.com/en-us/windows/win32/api/ws2tcpip/nf-ws2tcpip-getaddrinfo>
-	/// - <https://man.freebsd.org/cgi/man.cgi?query=getaddrinfo&sektion=3>
-	resolve-addresses: func(network: borrow<network>, name: string, address-family: option<ip-address-family>, include-unavailable: bool) -> result<resolve-address-stream, error-code>;
+    /// Resolve an internet host name to a list of IP addresses.
+    ///
+    /// See the wasi-socket proposal README.md for a comparison with getaddrinfo.
+    ///
+    /// # Parameters
+    /// - `name`: The name to look up. IP addresses are not allowed. Unicode domain names are automatically converted
+    ///     to ASCII using IDNA encoding.
+    /// - `address-family`: If provided, limit the results to addresses of this specific address family.
+    /// - `include-unavailable`: When set to true, this function will also return addresses of which the runtime
+    ///   thinks (or knows) can't be connected to at the moment. For example, this will return IPv6 addresses on
+    ///   systems without an active IPv6 interface. Notes:
+    ///     - Even when no public IPv6 interfaces are present or active, names like "localhost" can still resolve to an IPv6 address.
+    ///     - Whatever is "available" or "unavailable" is volatile and can change everytime a network cable is unplugged.
+    ///
+    /// This function never blocks. It either immediately fails or immediately returns successfully with a `resolve-address-stream`
+    /// that can be used to (asynchronously) fetch the results.
+    ///
+    /// At the moment, the stream never completes successfully with 0 items. Ie. the first call
+    /// to `resolve-next-address` never returns `ok(none)`. This may change in the future.
+    ///
+    /// # Typical errors
+    /// - `invalid-argument`:     `name` is a syntactically invalid domain name.
+    /// - `invalid-argument`:     `name` is an IP address.
+    /// - `not-supported`:        The specified `address-family` is not supported. (EAI_FAMILY)
+    ///
+    /// # References:
+    /// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/getaddrinfo.html>
+    /// - <https://man7.org/linux/man-pages/man3/getaddrinfo.3.html>
+    /// - <https://learn.microsoft.com/en-us/windows/win32/api/ws2tcpip/nf-ws2tcpip-getaddrinfo>
+    /// - <https://man.freebsd.org/cgi/man.cgi?query=getaddrinfo&sektion=3>
+    resolve-addresses: func(network: borrow<network>, name: string, address-family: option<ip-address-family>, include-unavailable: bool) -> result<resolve-address-stream, error-code>;
 
-	resource resolve-address-stream {
-		/// Returns the next address from the resolver.
-		///
-		/// This function should be called multiple times. On each call, it will
-		/// return the next address in connection order preference. If all
-		/// addresses have been exhausted, this function returns `none`.
-		///
-		/// This function never returns IPv4-mapped IPv6 addresses.
-		///
-		/// # Typical errors
-		/// - `name-unresolvable`:          Name does not exist or has no suitable associated IP addresses. (EAI_NONAME, EAI_NODATA, EAI_ADDRFAMILY)
-		/// - `temporary-resolver-failure`: A temporary failure in name resolution occurred. (EAI_AGAIN)
-		/// - `permanent-resolver-failure`: A permanent failure in name resolution occurred. (EAI_FAIL)
-		/// - `would-block`:                A result is not available yet. (EWOULDBLOCK, EAGAIN)
-		resolve-next-address: func() -> result<option<ip-address>, error-code>;
+    resource resolve-address-stream {
+        /// Returns the next address from the resolver.
+        ///
+        /// This function should be called multiple times. On each call, it will
+        /// return the next address in connection order preference. If all
+        /// addresses have been exhausted, this function returns `none`.
+        ///
+        /// This function never returns IPv4-mapped IPv6 addresses.
+        ///
+        /// # Typical errors
+        /// - `name-unresolvable`:          Name does not exist or has no suitable associated IP addresses. (EAI_NONAME, EAI_NODATA, EAI_ADDRFAMILY)
+        /// - `temporary-resolver-failure`: A temporary failure in name resolution occurred. (EAI_AGAIN)
+        /// - `permanent-resolver-failure`: A permanent failure in name resolution occurred. (EAI_FAIL)
+        /// - `would-block`:                A result is not available yet. (EWOULDBLOCK, EAGAIN)
+        resolve-next-address: func() -> result<option<ip-address>, error-code>;
 
-		/// Create a `pollable` which will resolve once the stream is ready for I/O.
-		///
-		/// Note: this function is here for WASI Preview2 only.
-		/// It's planned to be removed when `future` is natively supported in Preview3.
-		subscribe: func() -> pollable;
-	}
+        /// Create a `pollable` which will resolve once the stream is ready for I/O.
+        ///
+        /// Note: this function is here for WASI Preview2 only.
+        /// It's planned to be removed when `future` is natively supported in Preview3.
+        subscribe: func() -> pollable;
+    }
 }
diff --git a/crates/wasi-http/wit/deps/sockets/network.wit b/crates/wasi-http/wit/deps/sockets/network.wit
index 861ec673d..6bb07cd6f 100644
--- a/crates/wasi-http/wit/deps/sockets/network.wit
+++ b/crates/wasi-http/wit/deps/sockets/network.wit
@@ -1,147 +1,147 @@
 
 interface network {
-	/// An opaque resource that represents access to (a subset of) the network.
-	/// This enables context-based security for networking.
-	/// There is no need for this to map 1:1 to a physical network interface.
-	resource network;
+    /// An opaque resource that represents access to (a subset of) the network.
+    /// This enables context-based security for networking.
+    /// There is no need for this to map 1:1 to a physical network interface.
+    resource network;
 
-	/// 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:
-	/// - `unknown`
-	/// - `access-denied`
-	/// - `not-supported`
-	/// - `out-of-memory`
-	/// - `concurrency-conflict`
-	///
-	/// See each individual API for what the POSIX equivalents are. They sometimes differ per API.
-	enum error-code {
-		// ### GENERAL ERRORS ###
+    /// 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:
+    /// - `unknown`
+    /// - `access-denied`
+    /// - `not-supported`
+    /// - `out-of-memory`
+    /// - `concurrency-conflict`
+    ///
+    /// See each individual API for what the POSIX equivalents are. They sometimes differ per API.
+    enum error-code {
+        // ### GENERAL ERRORS ###
 
-		/// Unknown error
-		unknown,
+        /// Unknown error
+        unknown,
 
-		/// Access denied.
-		///
-		/// POSIX equivalent: EACCES, EPERM
-		access-denied,
+        /// Access denied.
+        ///
+        /// POSIX equivalent: EACCES, EPERM
+        access-denied,
 
-		/// The operation is not supported.
-		///
-		/// POSIX equivalent: EOPNOTSUPP
-		not-supported,
+        /// The operation is not supported.
+        ///
+        /// POSIX equivalent: EOPNOTSUPP
+        not-supported,
 
-		/// One of the arguments is invalid.
-		/// 
-		/// POSIX equivalent: EINVAL
-		invalid-argument,
+        /// One of the arguments is invalid.
+        ///
+        /// POSIX equivalent: EINVAL
+        invalid-argument,
 
-		/// Not enough memory to complete the operation.
-		///
-		/// POSIX equivalent: ENOMEM, ENOBUFS, EAI_MEMORY
-		out-of-memory,
+        /// Not enough memory to complete the operation.
+        ///
+        /// POSIX equivalent: ENOMEM, ENOBUFS, EAI_MEMORY
+        out-of-memory,
 
-		/// The operation timed out before it could finish completely.
-		timeout,
+        /// The operation timed out before it could finish completely.
+        timeout,
 
-		/// This operation is incompatible with another asynchronous operation that is already in progress.
-		///
-		/// POSIX equivalent: EALREADY
-		concurrency-conflict,
+        /// This operation is incompatible with another asynchronous operation that is already in progress.
+        ///
+        /// POSIX equivalent: EALREADY
+        concurrency-conflict,
 
-		/// Trying to finish an asynchronous operation that:
-		/// - has not been started yet, or:
-		/// - was already finished by a previous `finish-*` call.
-		///
-		/// Note: this is scheduled to be removed when `future`s are natively supported.
-		not-in-progress,
+        /// Trying to finish an asynchronous operation that:
+        /// - has not been started yet, or:
+        /// - was already finished by a previous `finish-*` call.
+        ///
+        /// Note: this is scheduled to be removed when `future`s are natively supported.
+        not-in-progress,
 
-		/// The operation has been aborted because it could not be completed immediately.
-		///
-		/// Note: this is scheduled to be removed when `future`s are natively supported.
-		would-block,
+        /// The operation has been aborted because it could not be completed immediately.
+        ///
+        /// Note: this is scheduled to be removed when `future`s are natively supported.
+        would-block,
 
 
 
-		// ### TCP & UDP SOCKET ERRORS ###
+        // ### TCP & UDP SOCKET ERRORS ###
 
-		/// The operation is not valid in the socket's current state.
-		invalid-state,
+        /// The operation is not valid in the socket's current state.
+        invalid-state,
 
-		/// A new socket resource could not be created because of a system limit.
-		new-socket-limit,
+        /// A new socket resource could not be created because of a system limit.
+        new-socket-limit,
 
-		/// A bind operation failed because the provided address is not an address that the `network` can bind to.
-		address-not-bindable,
+        /// A bind operation failed because the provided address is not an address that the `network` can bind to.
+        address-not-bindable,
 
-		/// A bind operation failed because the provided address is already in use or because there are no ephemeral ports available.
-		address-in-use,
+        /// A bind operation failed because the provided address is already in use or because there are no ephemeral ports available.
+        address-in-use,
 
-		/// The remote address is not reachable
-		remote-unreachable,
+        /// The remote address is not reachable
+        remote-unreachable,
 
 
-		// ### TCP SOCKET ERRORS ###
+        // ### TCP SOCKET ERRORS ###
 
-		/// The connection was forcefully rejected
-		connection-refused,
+        /// The connection was forcefully rejected
+        connection-refused,
 
-		/// The connection was reset.
-		connection-reset,
+        /// The connection was reset.
+        connection-reset,
 
-		/// A connection was aborted.
-		connection-aborted,
+        /// A connection was aborted.
+        connection-aborted,
 
 
-		// ### UDP SOCKET ERRORS ###
-		datagram-too-large,
+        // ### UDP SOCKET ERRORS ###
+        datagram-too-large,
 
 
-		// ### NAME LOOKUP ERRORS ###
+        // ### NAME LOOKUP ERRORS ###
 
-		/// Name does not exist or has no suitable associated IP addresses.
-		name-unresolvable,
+        /// Name does not exist or has no suitable associated IP addresses.
+        name-unresolvable,
 
-		/// A temporary failure in name resolution occurred.
-		temporary-resolver-failure,
+        /// A temporary failure in name resolution occurred.
+        temporary-resolver-failure,
 
-		/// A permanent failure in name resolution occurred.
-		permanent-resolver-failure,
-	}
+        /// A permanent failure in name resolution occurred.
+        permanent-resolver-failure,
+    }
 
-	enum ip-address-family {
-		/// Similar to `AF_INET` in POSIX.
-		ipv4,
+    enum ip-address-family {
+        /// Similar to `AF_INET` in POSIX.
+        ipv4,
 
-		/// Similar to `AF_INET6` in POSIX.
-		ipv6,
-	}
+        /// Similar to `AF_INET6` in POSIX.
+        ipv6,
+    }
 
-	type ipv4-address = tuple<u8, u8, u8, u8>;
-	type ipv6-address = tuple<u16, u16, u16, u16, u16, u16, u16, u16>;
+    type ipv4-address = tuple<u8, u8, u8, u8>;
+    type ipv6-address = tuple<u16, u16, u16, u16, u16, u16, u16, u16>;
 
-	variant ip-address {
-		ipv4(ipv4-address),
-		ipv6(ipv6-address),
-	}
+    variant ip-address {
+        ipv4(ipv4-address),
+        ipv6(ipv6-address),
+    }
 
-	record ipv4-socket-address {
-		port: u16, // sin_port
-		address: ipv4-address, // sin_addr
-	}
+    record ipv4-socket-address {
+        port: u16, // sin_port
+        address: ipv4-address, // sin_addr
+    }
 
-	record ipv6-socket-address {
-		port: u16, // sin6_port
-		flow-info: u32, // sin6_flowinfo
-		address: ipv6-address, // sin6_addr
-		scope-id: u32, // sin6_scope_id
-	}
+    record ipv6-socket-address {
+        port: u16, // sin6_port
+        flow-info: u32, // sin6_flowinfo
+        address: ipv6-address, // sin6_addr
+        scope-id: u32, // sin6_scope_id
+    }
 
-	variant ip-socket-address {
-		ipv4(ipv4-socket-address),
-		ipv6(ipv6-socket-address),
-	}
+    variant ip-socket-address {
+        ipv4(ipv4-socket-address),
+        ipv6(ipv6-socket-address),
+    }
 
 }
diff --git a/crates/wasi-http/wit/deps/sockets/tcp-create-socket.wit b/crates/wasi-http/wit/deps/sockets/tcp-create-socket.wit
index a9a33738b..768a07c85 100644
--- a/crates/wasi-http/wit/deps/sockets/tcp-create-socket.wit
+++ b/crates/wasi-http/wit/deps/sockets/tcp-create-socket.wit
@@ -1,26 +1,26 @@
 
 interface tcp-create-socket {
-	use network.{network, error-code, ip-address-family};
-	use tcp.{tcp-socket};
+    use network.{network, error-code, ip-address-family};
+    use tcp.{tcp-socket};
 
-	/// Create a new TCP socket.
-	///
-	/// Similar to `socket(AF_INET or AF_INET6, SOCK_STREAM, IPPROTO_TCP)` in POSIX.
-	///
-	/// This function does not require a network capability handle. This is considered to be safe because
-	/// at time of creation, the socket is not bound to any `network` yet. Up to the moment `bind`/`listen`/`connect`
-	/// is called, the socket is effectively an in-memory configuration object, unable to communicate with the outside world.
-	///
-	/// All sockets are non-blocking. Use the wasi-poll interface to block on asynchronous operations.
-	///
-	/// # Typical errors
-	/// - `not-supported`:     The specified `address-family` is not supported. (EAFNOSUPPORT)
-	/// - `new-socket-limit`:  The new socket resource could not be created because of a system limit. (EMFILE, ENFILE)
-	///
-	/// # References
-	/// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/socket.html>
-	/// - <https://man7.org/linux/man-pages/man2/socket.2.html>
-	/// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsasocketw>
-	/// - <https://man.freebsd.org/cgi/man.cgi?query=socket&sektion=2>
-	create-tcp-socket: func(address-family: ip-address-family) -> result<tcp-socket, error-code>;
+    /// Create a new TCP socket.
+    ///
+    /// Similar to `socket(AF_INET or AF_INET6, SOCK_STREAM, IPPROTO_TCP)` in POSIX.
+    ///
+    /// This function does not require a network capability handle. This is considered to be safe because
+    /// at time of creation, the socket is not bound to any `network` yet. Up to the moment `bind`/`listen`/`connect`
+    /// is called, the socket is effectively an in-memory configuration object, unable to communicate with the outside world.
+    ///
+    /// All sockets are non-blocking. Use the wasi-poll interface to block on asynchronous operations.
+    ///
+    /// # Typical errors
+    /// - `not-supported`:     The specified `address-family` is not supported. (EAFNOSUPPORT)
+    /// - `new-socket-limit`:  The new socket resource could not be created because of a system limit. (EMFILE, ENFILE)
+    ///
+    /// # References
+    /// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/socket.html>
+    /// - <https://man7.org/linux/man-pages/man2/socket.2.html>
+    /// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsasocketw>
+    /// - <https://man.freebsd.org/cgi/man.cgi?query=socket&sektion=2>
+    create-tcp-socket: func(address-family: ip-address-family) -> result<tcp-socket, error-code>;
 }
diff --git a/crates/wasi-http/wit/deps/sockets/tcp.wit b/crates/wasi-http/wit/deps/sockets/tcp.wit
index 448f629e5..c316a7245 100644
--- a/crates/wasi-http/wit/deps/sockets/tcp.wit
+++ b/crates/wasi-http/wit/deps/sockets/tcp.wit
@@ -1,268 +1,268 @@
 
 interface tcp {
-	use wasi:io/streams@0.2.0-rc-2023-10-18.{input-stream, output-stream};
-	use wasi:io/poll@0.2.0-rc-2023-10-18.{pollable};
-	use network.{network, error-code, ip-socket-address, ip-address-family};
+    use wasi:io/streams@0.2.0-rc-2023-10-18.{input-stream, output-stream};
+    use wasi:io/poll@0.2.0-rc-2023-10-18.{pollable};
+    use network.{network, error-code, ip-socket-address, ip-address-family};
 
-	enum shutdown-type {
-		/// Similar to `SHUT_RD` in POSIX.
-		receive,
+    enum shutdown-type {
+        /// Similar to `SHUT_RD` in POSIX.
+        receive,
 
-		/// Similar to `SHUT_WR` in POSIX.
-		send,
+        /// Similar to `SHUT_WR` in POSIX.
+        send,
 
-		/// Similar to `SHUT_RDWR` in POSIX.
-		both,
-	}
+        /// Similar to `SHUT_RDWR` in POSIX.
+        both,
+    }
 
 
-	/// A TCP socket handle.
-	resource tcp-socket {
-		/// Bind the socket to a specific network on 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.
-		///
-		/// When a socket is not explicitly bound, the first invocation to a listen or connect operation will
-		/// implicitly bind the socket.
-		///
-		/// Unlike in POSIX, this function is async. This enables interactive WASI hosts to inject permission prompts.
-		///
-		/// # Typical `start` 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, but the socket has `ipv6-only` enabled. (EINVAL)
-		/// - `invalid-state`:             The socket is already bound. (EINVAL)
-		///
-		/// # Typical `finish` errors
-		/// - `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 the `network` can bind to. (EADDRNOTAVAIL)
-		/// - `not-in-progress`:           A `bind` operation is not in progress.
-		/// - `would-block`:               Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN)
-		///
-		/// # References
-		/// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/bind.html>
-		/// - <https://man7.org/linux/man-pages/man2/bind.2.html>
-		/// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-bind>
-		/// - <https://man.freebsd.org/cgi/man.cgi?query=bind&sektion=2&format=html>
-		start-bind: func(network: borrow<network>, local-address: ip-socket-address) -> result<_, error-code>;
-		finish-bind: func() -> result<_, error-code>;
+    /// A TCP socket handle.
+    resource tcp-socket {
+        /// Bind the socket to a specific network on 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.
+        ///
+        /// When a socket is not explicitly bound, the first invocation to a listen or connect operation will
+        /// implicitly bind the socket.
+        ///
+        /// Unlike in POSIX, this function is async. This enables interactive WASI hosts to inject permission prompts.
+        ///
+        /// # Typical `start` 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, but the socket has `ipv6-only` enabled. (EINVAL)
+        /// - `invalid-state`:             The socket is already bound. (EINVAL)
+        ///
+        /// # Typical `finish` errors
+        /// - `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 the `network` can bind to. (EADDRNOTAVAIL)
+        /// - `not-in-progress`:           A `bind` operation is not in progress.
+        /// - `would-block`:               Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN)
+        ///
+        /// # References
+        /// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/bind.html>
+        /// - <https://man7.org/linux/man-pages/man2/bind.2.html>
+        /// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-bind>
+        /// - <https://man.freebsd.org/cgi/man.cgi?query=bind&sektion=2&format=html>
+        start-bind: func(network: borrow<network>, local-address: ip-socket-address) -> result<_, error-code>;
+        finish-bind: func() -> result<_, error-code>;
 
-		/// Connect to a remote endpoint.
-		///
-		/// On success:
-		/// - the socket is transitioned into the Connection state
-		/// - a pair of streams is returned that can be used to read & write to the connection
-		///
-		/// POSIX mentions:
-		/// > If connect() fails, the state of the socket is unspecified. Conforming applications should
-		/// > close the file descriptor and create a new socket before attempting to reconnect.
-		///
-		/// WASI prescribes the following behavior:
-		/// - If `connect` fails because an input/state validation error, the socket should remain usable.
-		/// - If a connection was actually attempted but failed, the socket should become unusable for further network communication.
-		///   Besides `drop`, any method after such a failure may return an error.
-		///
-		/// # Typical `start` 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, but the socket has `ipv6-only` enabled. (EINVAL, EADDRNOTAVAIL on Illumos)
-		/// - `invalid-argument`:          `remote-address` is a non-IPv4-mapped IPv6 address, but the socket was bound to a specific IPv4-mapped IPv6 address. (or vice versa)
-		/// - `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-argument`:          The socket is already attached to a different network. The `network` passed to `connect` must be identical to the one passed to `bind`.
-		/// - `invalid-state`:             The socket is already in the Connection state. (EISCONN)
-		/// - `invalid-state`:             The socket is already in the Listener state. (EOPNOTSUPP, EINVAL on Windows)
-		///
-		/// # Typical `finish` errors
-		/// - `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)
-		/// - `address-in-use`:            Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE, EADDRNOTAVAIL on Linux, EAGAIN on BSD)
-		/// - `not-in-progress`:           A `connect` operation is not in progress.
-		/// - `would-block`:               Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN)
-		///
-		/// # References
-		/// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/connect.html>
-		/// - <https://man7.org/linux/man-pages/man2/connect.2.html>
-		/// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-connect>
-		/// - <https://man.freebsd.org/cgi/man.cgi?connect>
-		start-connect: func(network: borrow<network>, remote-address: ip-socket-address) -> result<_, error-code>;
-		finish-connect: func() -> result<tuple<input-stream, output-stream>, error-code>;
+        /// Connect to a remote endpoint.
+        ///
+        /// On success:
+        /// - the socket is transitioned into the Connection state
+        /// - a pair of streams is returned that can be used to read & write to the connection
+        ///
+        /// POSIX mentions:
+        /// > If connect() fails, the state of the socket is unspecified. Conforming applications should
+        /// > close the file descriptor and create a new socket before attempting to reconnect.
+        ///
+        /// WASI prescribes the following behavior:
+        /// - If `connect` fails because an input/state validation error, the socket should remain usable.
+        /// - If a connection was actually attempted but failed, the socket should become unusable for further network communication.
+        ///   Besides `drop`, any method after such a failure may return an error.
+        ///
+        /// # Typical `start` 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, but the socket has `ipv6-only` enabled. (EINVAL, EADDRNOTAVAIL on Illumos)
+        /// - `invalid-argument`:          `remote-address` is a non-IPv4-mapped IPv6 address, but the socket was bound to a specific IPv4-mapped IPv6 address. (or vice versa)
+        /// - `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-argument`:          The socket is already attached to a different network. The `network` passed to `connect` must be identical to the one passed to `bind`.
+        /// - `invalid-state`:             The socket is already in the Connection state. (EISCONN)
+        /// - `invalid-state`:             The socket is already in the Listener state. (EOPNOTSUPP, EINVAL on Windows)
+        ///
+        /// # Typical `finish` errors
+        /// - `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)
+        /// - `address-in-use`:            Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE, EADDRNOTAVAIL on Linux, EAGAIN on BSD)
+        /// - `not-in-progress`:           A `connect` operation is not in progress.
+        /// - `would-block`:               Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN)
+        ///
+        /// # References
+        /// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/connect.html>
+        /// - <https://man7.org/linux/man-pages/man2/connect.2.html>
+        /// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-connect>
+        /// - <https://man.freebsd.org/cgi/man.cgi?connect>
+        start-connect: func(network: borrow<network>, remote-address: ip-socket-address) -> result<_, error-code>;
+        finish-connect: func() -> result<tuple<input-stream, output-stream>, error-code>;
 
-		/// Start listening for new connections.
-		///
-		/// Transitions the socket into the Listener state.
-		///
-		/// Unlike POSIX:
-		/// - this function is async. This enables interactive WASI hosts to inject permission prompts.
-		/// - the socket must already be explicitly bound.
-		///
-		/// # Typical `start` errors
-		/// - `invalid-state`:             The socket is not bound to any local address. (EDESTADDRREQ)
-		/// - `invalid-state`:             The socket is already in the Connection state. (EISCONN, EINVAL on BSD)
-		/// - `invalid-state`:             The socket is already in the Listener state.
-		///
-		/// # Typical `finish` errors
-		/// - `address-in-use`:            Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE)
-		/// - `not-in-progress`:           A `listen` operation is not in progress.
-		/// - `would-block`:               Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN)
-		///
-		/// # References
-		/// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/listen.html>
-		/// - <https://man7.org/linux/man-pages/man2/listen.2.html>
-		/// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-listen>
-		/// - <https://man.freebsd.org/cgi/man.cgi?query=listen&sektion=2>
-		start-listen: func() -> result<_, error-code>;
-		finish-listen: func() -> result<_, error-code>;
+        /// Start listening for new connections.
+        ///
+        /// Transitions the socket into the Listener state.
+        ///
+        /// Unlike POSIX:
+        /// - this function is async. This enables interactive WASI hosts to inject permission prompts.
+        /// - the socket must already be explicitly bound.
+        ///
+        /// # Typical `start` errors
+        /// - `invalid-state`:             The socket is not bound to any local address. (EDESTADDRREQ)
+        /// - `invalid-state`:             The socket is already in the Connection state. (EISCONN, EINVAL on BSD)
+        /// - `invalid-state`:             The socket is already in the Listener state.
+        ///
+        /// # Typical `finish` errors
+        /// - `address-in-use`:            Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE)
+        /// - `not-in-progress`:           A `listen` operation is not in progress.
+        /// - `would-block`:               Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN)
+        ///
+        /// # References
+        /// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/listen.html>
+        /// - <https://man7.org/linux/man-pages/man2/listen.2.html>
+        /// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-listen>
+        /// - <https://man.freebsd.org/cgi/man.cgi?query=listen&sektion=2>
+        start-listen: func() -> result<_, error-code>;
+        finish-listen: func() -> result<_, error-code>;
 
-		/// Accept a new client socket.
-		///
-		/// The returned socket is bound and in the Connection state. The following properties are inherited from the listener socket:
-		/// - `address-family`
-		/// - `ipv6-only`
-		/// - `keep-alive`
-		/// - `no-delay`
-		/// - `unicast-hop-limit`
-		/// - `receive-buffer-size`
-		/// - `send-buffer-size`
-		///
-		/// On success, this function returns the newly accepted client socket along with
-		/// a pair of streams that can be used to read & write to the connection.
-		///
-		/// # Typical errors
-		/// - `invalid-state`:      Socket is not in the Listener state. (EINVAL)
-		/// - `would-block`:        No pending connections at the moment. (EWOULDBLOCK, EAGAIN)
-		/// - `connection-aborted`: An incoming connection was pending, but was terminated by the client before this listener could accept it. (ECONNABORTED)
-		/// - `new-socket-limit`:   The new socket resource could not be created because of a system limit. (EMFILE, ENFILE)
-		///
-		/// # References
-		/// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/accept.html>
-		/// - <https://man7.org/linux/man-pages/man2/accept.2.html>
-		/// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-accept>
-		/// - <https://man.freebsd.org/cgi/man.cgi?query=accept&sektion=2>
-		accept: func() -> result<tuple<tcp-socket, input-stream, output-stream>, error-code>;
+        /// Accept a new client socket.
+        ///
+        /// The returned socket is bound and in the Connection state. The following properties are inherited from the listener socket:
+        /// - `address-family`
+        /// - `ipv6-only`
+        /// - `keep-alive`
+        /// - `no-delay`
+        /// - `unicast-hop-limit`
+        /// - `receive-buffer-size`
+        /// - `send-buffer-size`
+        ///
+        /// On success, this function returns the newly accepted client socket along with
+        /// a pair of streams that can be used to read & write to the connection.
+        ///
+        /// # Typical errors
+        /// - `invalid-state`:      Socket is not in the Listener state. (EINVAL)
+        /// - `would-block`:        No pending connections at the moment. (EWOULDBLOCK, EAGAIN)
+        /// - `connection-aborted`: An incoming connection was pending, but was terminated by the client before this listener could accept it. (ECONNABORTED)
+        /// - `new-socket-limit`:   The new socket resource could not be created because of a system limit. (EMFILE, ENFILE)
+        ///
+        /// # References
+        /// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/accept.html>
+        /// - <https://man7.org/linux/man-pages/man2/accept.2.html>
+        /// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-accept>
+        /// - <https://man.freebsd.org/cgi/man.cgi?query=accept&sektion=2>
+        accept: func() -> result<tuple<tcp-socket, input-stream, output-stream>, error-code>;
 
-		/// 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 `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
-		/// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/getsockname.html>
-		/// - <https://man7.org/linux/man-pages/man2/getsockname.2.html>
-		/// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-getsockname>
-		/// - <https://man.freebsd.org/cgi/man.cgi?getsockname>
-		local-address: func() -> result<ip-socket-address, error-code>;
+        /// 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 `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
+        /// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/getsockname.html>
+        /// - <https://man7.org/linux/man-pages/man2/getsockname.2.html>
+        /// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-getsockname>
+        /// - <https://man.freebsd.org/cgi/man.cgi?getsockname>
+        local-address: func() -> result<ip-socket-address, error-code>;
 
-		/// Get the remote address.
-		///
-		/// # Typical errors
-		/// - `invalid-state`: The socket is not connected to a remote address. (ENOTCONN)
-		///
-		/// # References
-		/// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpeername.html>
-		/// - <https://man7.org/linux/man-pages/man2/getpeername.2.html>
-		/// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-getpeername>
-		/// - <https://man.freebsd.org/cgi/man.cgi?query=getpeername&sektion=2&n=1>
-		remote-address: func() -> result<ip-socket-address, error-code>;
+        /// Get the remote address.
+        ///
+        /// # Typical errors
+        /// - `invalid-state`: The socket is not connected to a remote address. (ENOTCONN)
+        ///
+        /// # References
+        /// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpeername.html>
+        /// - <https://man7.org/linux/man-pages/man2/getpeername.2.html>
+        /// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-getpeername>
+        /// - <https://man.freebsd.org/cgi/man.cgi?query=getpeername&sektion=2&n=1>
+        remote-address: func() -> result<ip-socket-address, error-code>;
 
-		/// Whether this is a IPv4 or IPv6 socket.
-		///
-		/// Equivalent to the SO_DOMAIN socket option.
-		address-family: func() -> ip-address-family;
+        /// Whether this is a IPv4 or IPv6 socket.
+        ///
+        /// Equivalent to the SO_DOMAIN socket option.
+        address-family: func() -> ip-address-family;
 
-		/// Whether IPv4 compatibility (dual-stack) mode is disabled or not.
-		///
-		/// Equivalent to the IPV6_V6ONLY socket option.
-		///
-		/// # Typical errors
-		/// - `invalid-state`:        (set) The socket is already bound.
-		/// - `not-supported`:        (get/set) `this` socket is an IPv4 socket.
-		/// - `not-supported`:        (set) Host does not support dual-stack sockets. (Implementations are not required to.)
-		ipv6-only: func() -> result<bool, error-code>;
-		set-ipv6-only: func(value: bool) -> result<_, error-code>;
+        /// Whether IPv4 compatibility (dual-stack) mode is disabled or not.
+        ///
+        /// Equivalent to the IPV6_V6ONLY socket option.
+        ///
+        /// # Typical errors
+        /// - `invalid-state`:        (set) The socket is already bound.
+        /// - `not-supported`:        (get/set) `this` socket is an IPv4 socket.
+        /// - `not-supported`:        (set) Host does not support dual-stack sockets. (Implementations are not required to.)
+        ipv6-only: func() -> result<bool, error-code>;
+        set-ipv6-only: func(value: bool) -> result<_, error-code>;
 
-		/// Hints the desired listen queue size. Implementations are free to ignore this.
-		///
-		/// # Typical errors
-		/// - `not-supported`:        (set) The platform does not support changing the backlog size after the initial listen.
-		/// - `invalid-state`:        (set) The socket is already in the Connection state.
-		set-listen-backlog-size: func(value: u64) -> result<_, error-code>;
+        /// Hints the desired listen queue size. Implementations are free to ignore this.
+        ///
+        /// # Typical errors
+        /// - `not-supported`:        (set) The platform does not support changing the backlog size after the initial listen.
+        /// - `invalid-state`:        (set) The socket is already in the Connection state.
+        set-listen-backlog-size: func(value: u64) -> result<_, error-code>;
 
-		/// Equivalent to the SO_KEEPALIVE socket option.
-		keep-alive: func() -> result<bool, error-code>;
-		set-keep-alive: func(value: bool) -> result<_, error-code>;
+        /// Equivalent to the SO_KEEPALIVE socket option.
+        keep-alive: func() -> result<bool, error-code>;
+        set-keep-alive: func(value: bool) -> result<_, error-code>;
 
-		/// Equivalent to the TCP_NODELAY socket option.
-		///
-		/// The default value is `false`.
-		no-delay: func() -> result<bool, error-code>;
-		set-no-delay: func(value: bool) -> result<_, error-code>;
+        /// Equivalent to the TCP_NODELAY socket option.
+        ///
+        /// The default value is `false`.
+        no-delay: func() -> result<bool, error-code>;
+        set-no-delay: func(value: bool) -> result<_, error-code>;
 
-		/// Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options.
-		///
-		/// # Typical errors
-		/// - `invalid-argument`:     (set) The TTL value must be 1 or higher.
-		/// - `invalid-state`:        (set) The socket is already in the Connection state.
-		/// - `invalid-state`:        (set) The socket is already in the Listener state.
-		unicast-hop-limit: func() -> result<u8, error-code>;
-		set-unicast-hop-limit: func(value: u8) -> result<_, error-code>;
+        /// Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options.
+        ///
+        /// # Typical errors
+        /// - `invalid-argument`:     (set) The TTL value must be 1 or higher.
+        /// - `invalid-state`:        (set) The socket is already in the Connection state.
+        /// - `invalid-state`:        (set) The socket is already in the Listener state.
+        unicast-hop-limit: func() -> result<u8, error-code>;
+        set-unicast-hop-limit: func(value: u8) -> result<_, error-code>;
 
-		/// The kernel buffer space reserved for sends/receives on this socket.
-		///
-		/// Note #1: an implementation may choose to cap or round the buffer size when setting the value.
-		/// 	In other words, after setting a value, reading the same setting back may return a different value.
-		///
-		/// Note #2: there is not necessarily a direct relationship between the kernel buffer size and the bytes of
-		/// 	actual data to be sent/received by the application, because the kernel might also use the buffer space
-		/// 	for internal metadata structures.
-		///
-		/// Equivalent to the SO_RCVBUF and SO_SNDBUF socket options.
-		///
-		/// # Typical errors
-		/// - `invalid-state`:        (set) The socket is already in the Connection state.
-		/// - `invalid-state`:        (set) The socket is already in the Listener state.
-		receive-buffer-size: func() -> result<u64, error-code>;
-		set-receive-buffer-size: func(value: u64) -> result<_, error-code>;
-		send-buffer-size: func() -> result<u64, error-code>;
-		set-send-buffer-size: func(value: u64) -> result<_, error-code>;
+        /// The kernel buffer space reserved for sends/receives on this socket.
+        ///
+        /// Note #1: an implementation may choose to cap or round the buffer size when setting the value.
+        ///     In other words, after setting a value, reading the same setting back may return a different value.
+        ///
+        /// Note #2: there is not necessarily a direct relationship between the kernel buffer size and the bytes of
+        ///     actual data to be sent/received by the application, because the kernel might also use the buffer space
+        ///     for internal metadata structures.
+        ///
+        /// Equivalent to the SO_RCVBUF and SO_SNDBUF socket options.
+        ///
+        /// # Typical errors
+        /// - `invalid-state`:        (set) The socket is already in the Connection state.
+        /// - `invalid-state`:        (set) The socket is already in the Listener state.
+        receive-buffer-size: func() -> result<u64, error-code>;
+        set-receive-buffer-size: func(value: u64) -> result<_, error-code>;
+        send-buffer-size: func() -> result<u64, error-code>;
+        set-send-buffer-size: func(value: u64) -> result<_, error-code>;
 
-		/// Create a `pollable` which will resolve once the socket is ready for I/O.
-		///
-		/// Note: this function is here for WASI Preview2 only.
-		/// It's planned to be removed when `future` is natively supported in Preview3.
-		subscribe: func() -> pollable;
+        /// Create a `pollable` which will resolve once the socket is ready for I/O.
+        ///
+        /// Note: this function is here for WASI Preview2 only.
+        /// It's planned to be removed when `future` is natively supported in Preview3.
+        subscribe: func() -> pollable;
 
-		/// Initiate a graceful shutdown.
-		///
-		/// - receive: the socket is not expecting to receive any more data from the peer. All subsequent read
-		///   operations on the `input-stream` associated with this socket will return an End Of Stream indication.
-		///   Any data still in the receive queue at time of calling `shutdown` will be discarded.
-		/// - send: the socket is not expecting to send any more data to the peer. All subsequent write
-		///   operations on the `output-stream` associated with this socket will return an error.
-		/// - both: same effect as receive & send combined.
-		///
-		/// The shutdown function does not close (drop) the socket.
-		///
-		/// # Typical errors
-		/// - `invalid-state`: The socket is not in the Connection state. (ENOTCONN)
-		///
-		/// # References
-		/// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/shutdown.html>
-		/// - <https://man7.org/linux/man-pages/man2/shutdown.2.html>
-		/// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-shutdown>
-		/// - <https://man.freebsd.org/cgi/man.cgi?query=shutdown&sektion=2>
-		shutdown: func(shutdown-type: shutdown-type) -> result<_, error-code>;
-	}
+        /// Initiate a graceful shutdown.
+        ///
+        /// - receive: the socket is not expecting to receive any more data from the peer. All subsequent read
+        ///   operations on the `input-stream` associated with this socket will return an End Of Stream indication.
+        ///   Any data still in the receive queue at time of calling `shutdown` will be discarded.
+        /// - send: the socket is not expecting to send any more data to the peer. All subsequent write
+        ///   operations on the `output-stream` associated with this socket will return an error.
+        /// - both: same effect as receive & send combined.
+        ///
+        /// The shutdown function does not close (drop) the socket.
+        ///
+        /// # Typical errors
+        /// - `invalid-state`: The socket is not in the Connection state. (ENOTCONN)
+        ///
+        /// # References
+        /// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/shutdown.html>
+        /// - <https://man7.org/linux/man-pages/man2/shutdown.2.html>
+        /// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-shutdown>
+        /// - <https://man.freebsd.org/cgi/man.cgi?query=shutdown&sektion=2>
+        shutdown: func(shutdown-type: shutdown-type) -> result<_, error-code>;
+    }
 }
diff --git a/crates/wasi-http/wit/deps/sockets/udp-create-socket.wit b/crates/wasi-http/wit/deps/sockets/udp-create-socket.wit
index e026359fd..4e61d30fa 100644
--- a/crates/wasi-http/wit/deps/sockets/udp-create-socket.wit
+++ b/crates/wasi-http/wit/deps/sockets/udp-create-socket.wit
@@ -1,26 +1,26 @@
 
 interface udp-create-socket {
-	use network.{network, error-code, ip-address-family};
-	use udp.{udp-socket};
+    use network.{network, error-code, ip-address-family};
+    use udp.{udp-socket};
 
-	/// Create a new UDP socket.
-	///
-	/// Similar to `socket(AF_INET or AF_INET6, SOCK_DGRAM, IPPROTO_UDP)` in POSIX.
-	///
-	/// This function does not require a network capability handle. This is considered to be safe because
-	/// at time of creation, the socket is not bound to any `network` yet. Up to the moment `bind`/`connect` is called,
-	/// the socket is effectively an in-memory configuration object, unable to communicate with the outside world.
-	///
-	/// All sockets are non-blocking. Use the wasi-poll interface to block on asynchronous operations.
-	///
-	/// # Typical errors
-	/// - `not-supported`:     The specified `address-family` is not supported. (EAFNOSUPPORT)
-	/// - `new-socket-limit`:  The new socket resource could not be created because of a system limit. (EMFILE, ENFILE)
-	///
-	/// # References:
-	/// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/socket.html>
-	/// - <https://man7.org/linux/man-pages/man2/socket.2.html>
-	/// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsasocketw>
-	/// - <https://man.freebsd.org/cgi/man.cgi?query=socket&sektion=2>
-	create-udp-socket: func(address-family: ip-address-family) -> result<udp-socket, error-code>;
+    /// Create a new UDP socket.
+    ///
+    /// Similar to `socket(AF_INET or AF_INET6, SOCK_DGRAM, IPPROTO_UDP)` in POSIX.
+    ///
+    /// This function does not require a network capability handle. This is considered to be safe because
+    /// at time of creation, the socket is not bound to any `network` yet. Up to the moment `bind`/`connect` is called,
+    /// the socket is effectively an in-memory configuration object, unable to communicate with the outside world.
+    ///
+    /// All sockets are non-blocking. Use the wasi-poll interface to block on asynchronous operations.
+    ///
+    /// # Typical errors
+    /// - `not-supported`:     The specified `address-family` is not supported. (EAFNOSUPPORT)
+    /// - `new-socket-limit`:  The new socket resource could not be created because of a system limit. (EMFILE, ENFILE)
+    ///
+    /// # References:
+    /// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/socket.html>
+    /// - <https://man7.org/linux/man-pages/man2/socket.2.html>
+    /// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsasocketw>
+    /// - <https://man.freebsd.org/cgi/man.cgi?query=socket&sektion=2>
+    create-udp-socket: func(address-family: ip-address-family) -> result<udp-socket, error-code>;
 }
diff --git a/crates/wasi-http/wit/deps/sockets/udp.wit b/crates/wasi-http/wit/deps/sockets/udp.wit
index 91a8c6c4a..a86226f01 100644
--- a/crates/wasi-http/wit/deps/sockets/udp.wit
+++ b/crates/wasi-http/wit/deps/sockets/udp.wit
@@ -1,213 +1,213 @@
 
 interface udp {
-	use wasi:io/poll@0.2.0-rc-2023-10-18.{pollable};
-	use network.{network, error-code, ip-socket-address, ip-address-family};
-
-
-	record datagram {
-		data: list<u8>, // Theoretical max size: ~64 KiB. In practice, typically less than 1500 bytes.
-		remote-address: ip-socket-address,
-
-		/// Possible future additions:
-		/// local-address: ip-socket-address, // IP_PKTINFO / IP_RECVDSTADDR / IPV6_PKTINFO
-		/// local-interface: u32, // IP_PKTINFO / IP_RECVIF
-		/// ttl: u8, // IP_RECVTTL
-		/// dscp: u6, // IP_RECVTOS
-		/// ecn: u2, // IP_RECVTOS
-	}
-
-
-
-	/// A UDP socket handle.
-	resource udp-socket {
-		/// Bind the socket to a specific network on 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.
-		///
-		/// When a socket is not explicitly bound, the first invocation to connect will implicitly bind the socket.
-		///
-		/// Unlike in POSIX, this function is async. This enables interactive WASI hosts to inject permission prompts.
-		///
-		/// # Typical `start` errors
-		/// - `invalid-argument`:          The `local-address` has the wrong address family. (EAFNOSUPPORT, EFAULT on Windows)
-		/// - `invalid-state`:             The socket is already bound. (EINVAL)
-		///
-		/// # Typical `finish` errors
-		/// - `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 the `network` can bind to. (EADDRNOTAVAIL)
-		/// - `not-in-progress`:           A `bind` operation is not in progress.
-		/// - `would-block`:               Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN)
-		///
-		/// # References
-		/// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/bind.html>
-		/// - <https://man7.org/linux/man-pages/man2/bind.2.html>
-		/// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-bind>
-		/// - <https://man.freebsd.org/cgi/man.cgi?query=bind&sektion=2&format=html>
-		start-bind: func(network: borrow<network>, local-address: ip-socket-address) -> result<_, error-code>;
-		finish-bind: func() -> result<_, error-code>;
-
-		/// Set the destination address.
-		///
-		/// The local-address is updated based on the best network path to `remote-address`.
-		///
-		/// When a destination address is set:
-		/// - all receive operations will only return datagrams sent from the provided `remote-address`.
-		/// - the `send` function can only be used to send to this destination.
-		///
-		/// Note that this function does not generate any network traffic and the peer is not aware of this "connection".
-		///
-		/// Unlike in POSIX, this function is async. This enables interactive WASI hosts to inject permission prompts.
-		///
-		/// # Typical `start` errors
-		/// - `invalid-argument`:          The `remote-address` has the wrong address family. (EAFNOSUPPORT)
-		/// - `invalid-argument`:          `remote-address` is a non-IPv4-mapped IPv6 address, but the socket was bound to a specific IPv4-mapped IPv6 address. (or vice versa)
-		/// - `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 already bound to a different network. The `network` passed to `connect` must be identical to the one passed to `bind`.
-		///
-		/// # Typical `finish` errors
-		/// - `address-in-use`:            Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE, EADDRNOTAVAIL on Linux, EAGAIN on BSD)
-		/// - `not-in-progress`:           A `connect` operation is not in progress.
-		/// - `would-block`:               Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN)
-		///
-		/// # References
-		/// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/connect.html>
-		/// - <https://man7.org/linux/man-pages/man2/connect.2.html>
-		/// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-connect>
-		/// - <https://man.freebsd.org/cgi/man.cgi?connect>
-		start-connect: func(network: borrow<network>, remote-address: ip-socket-address) -> result<_, error-code>;
-		finish-connect: func() -> result<_, error-code>;
-
-		/// Receive messages on the socket.
-		///
-		/// This function attempts to receive up to `max-results` datagrams on the socket without blocking.
-		/// The returned list may contain fewer elements than requested, but never more.
-		/// If `max-results` is 0, this function returns successfully with an empty list.
-		///
-		/// # Typical errors
-		/// - `invalid-state`:      The socket is not bound to any local address. (EINVAL)
-		/// - `remote-unreachable`: The remote address is not reachable. (ECONNREFUSED, ECONNRESET, ENETRESET on Windows, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN)
-		/// - `would-block`:        There is no pending data available to be read at the moment. (EWOULDBLOCK, EAGAIN)
-		///
-		/// # References
-		/// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/recvfrom.html>
-		/// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/recvmsg.html>
-		/// - <https://man7.org/linux/man-pages/man2/recv.2.html>
-		/// - <https://man7.org/linux/man-pages/man2/recvmmsg.2.html>
-		/// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-recv>
-		/// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-recvfrom>
-		/// - <https://learn.microsoft.com/en-us/previous-versions/windows/desktop/legacy/ms741687(v=vs.85)>
-		/// - <https://man.freebsd.org/cgi/man.cgi?query=recv&sektion=2>
-		receive: func(max-results: u64) -> result<list<datagram>, error-code>;
-
-		/// Send messages on the socket.
-		///
-		/// This function attempts to send all provided `datagrams` on the socket without blocking and
-		/// returns how many messages were actually sent (or queued for sending).
-		///
-		/// This function semantically behaves the same as iterating the `datagrams` list and sequentially
-		/// sending each individual datagram until either the end of the list has been reached or the first error occurred.
-		/// If at least one datagram has been sent successfully, this function never returns an error.
-		///
-		/// If the input list is empty, the function returns `ok(0)`.
-		///
-		/// The remote address option is required. To send a message to the "connected" peer,
-		/// call `remote-address` to get their address.
-		///
-		/// # Typical errors
-		/// - `invalid-argument`:        The `remote-address` has the wrong address family. (EAFNOSUPPORT)
-		/// - `invalid-argument`:        `remote-address` is a non-IPv4-mapped IPv6 address, but the socket was bound to a specific IPv4-mapped IPv6 address. (or vice versa)
-		/// - `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 the `datagram.remote-address` does not match the address passed to `connect`. (EISCONN)
-		/// - `invalid-state`:           The socket is not bound to any local address. Unlike POSIX, this function does not perform an implicit bind.
-		/// - `remote-unreachable`:      The remote address is not reachable. (ECONNREFUSED, ECONNRESET, ENETRESET on Windows, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN)
-		/// - `datagram-too-large`:      The datagram is too large. (EMSGSIZE)
-		/// - `would-block`:             The send buffer is currently full. (EWOULDBLOCK, EAGAIN)
-		///
-		/// # References
-		/// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/sendto.html>
-		/// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/sendmsg.html>
-		/// - <https://man7.org/linux/man-pages/man2/send.2.html>
-		/// - <https://man7.org/linux/man-pages/man2/sendmmsg.2.html>
-		/// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-send>
-		/// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-sendto>
-		/// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsasendmsg>
-		/// - <https://man.freebsd.org/cgi/man.cgi?query=send&sektion=2>
-		send: func(datagrams: list<datagram>) -> result<u64, 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 `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
-		/// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/getsockname.html>
-		/// - <https://man7.org/linux/man-pages/man2/getsockname.2.html>
-		/// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-getsockname>
-		/// - <https://man.freebsd.org/cgi/man.cgi?getsockname>
-		local-address: func() -> result<ip-socket-address, error-code>;
-
-		/// Get the address set with `connect`.
-		///
-		/// # Typical errors
-		/// - `invalid-state`: The socket is not connected to a remote address. (ENOTCONN)
-		///
-		/// # References
-		/// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpeername.html>
-		/// - <https://man7.org/linux/man-pages/man2/getpeername.2.html>
-		/// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-getpeername>
-		/// - <https://man.freebsd.org/cgi/man.cgi?query=getpeername&sektion=2&n=1>
-		remote-address: func() -> result<ip-socket-address, error-code>;
-
-		/// Whether this is a IPv4 or IPv6 socket.
-		///
-		/// Equivalent to the SO_DOMAIN socket option.
-		address-family: func() -> ip-address-family;
-
-		/// Whether IPv4 compatibility (dual-stack) mode is disabled or not.
-		///
-		/// Equivalent to the IPV6_V6ONLY socket option.
-		///
-		/// # Typical errors
-		/// - `not-supported`:        (get/set) `this` socket is an IPv4 socket.
-		/// - `invalid-state`:        (set) The socket is already bound.
-		/// - `not-supported`:        (set) Host does not support dual-stack sockets. (Implementations are not required to.)
-		ipv6-only: func() -> result<bool, error-code>;
-		set-ipv6-only: func(value: bool) -> result<_, error-code>;
-
-		/// Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options.
-		unicast-hop-limit: func() -> result<u8, error-code>;
-		set-unicast-hop-limit: func(value: u8) -> result<_, error-code>;
-
-		/// The kernel buffer space reserved for sends/receives on this socket.
-		///
-		/// Note #1: an implementation may choose to cap or round the buffer size when setting the value.
-		/// 	In other words, after setting a value, reading the same setting back may return a different value.
-		///
-		/// Note #2: there is not necessarily a direct relationship between the kernel buffer size and the bytes of
-		/// 	actual data to be sent/received by the application, because the kernel might also use the buffer space
-		/// 	for internal metadata structures.
-		///
-		/// Equivalent to the SO_RCVBUF and SO_SNDBUF socket options.
-		receive-buffer-size: func() -> result<u64, error-code>;
-		set-receive-buffer-size: func(value: u64) -> result<_, error-code>;
-		send-buffer-size: func() -> result<u64, error-code>;
-		set-send-buffer-size: func(value: u64) -> result<_, error-code>;
-
-		/// Create a `pollable` which will resolve once the socket is ready for I/O.
-		///
-		/// Note: this function is here for WASI Preview2 only.
-		/// It's planned to be removed when `future` is natively supported in Preview3.
-		subscribe: func() -> pollable;
-	}
+    use wasi:io/poll@0.2.0-rc-2023-10-18.{pollable};
+    use network.{network, error-code, ip-socket-address, ip-address-family};
+
+
+    record datagram {
+        data: list<u8>, // Theoretical max size: ~64 KiB. In practice, typically less than 1500 bytes.
+        remote-address: ip-socket-address,
+
+        /// Possible future additions:
+        /// local-address: ip-socket-address, // IP_PKTINFO / IP_RECVDSTADDR / IPV6_PKTINFO
+        /// local-interface: u32, // IP_PKTINFO / IP_RECVIF
+        /// ttl: u8, // IP_RECVTTL
+        /// dscp: u6, // IP_RECVTOS
+        /// ecn: u2, // IP_RECVTOS
+    }
+
+
+
+    /// A UDP socket handle.
+    resource udp-socket {
+        /// Bind the socket to a specific network on 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.
+        ///
+        /// When a socket is not explicitly bound, the first invocation to connect will implicitly bind the socket.
+        ///
+        /// Unlike in POSIX, this function is async. This enables interactive WASI hosts to inject permission prompts.
+        ///
+        /// # Typical `start` errors
+        /// - `invalid-argument`:          The `local-address` has the wrong address family. (EAFNOSUPPORT, EFAULT on Windows)
+        /// - `invalid-state`:             The socket is already bound. (EINVAL)
+        ///
+        /// # Typical `finish` errors
+        /// - `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 the `network` can bind to. (EADDRNOTAVAIL)
+        /// - `not-in-progress`:           A `bind` operation is not in progress.
+        /// - `would-block`:               Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN)
+        ///
+        /// # References
+        /// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/bind.html>
+        /// - <https://man7.org/linux/man-pages/man2/bind.2.html>
+        /// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-bind>
+        /// - <https://man.freebsd.org/cgi/man.cgi?query=bind&sektion=2&format=html>
+        start-bind: func(network: borrow<network>, local-address: ip-socket-address) -> result<_, error-code>;
+        finish-bind: func() -> result<_, error-code>;
+
+        /// Set the destination address.
+        ///
+        /// The local-address is updated based on the best network path to `remote-address`.
+        ///
+        /// When a destination address is set:
+        /// - all receive operations will only return datagrams sent from the provided `remote-address`.
+        /// - the `send` function can only be used to send to this destination.
+        ///
+        /// Note that this function does not generate any network traffic and the peer is not aware of this "connection".
+        ///
+        /// Unlike in POSIX, this function is async. This enables interactive WASI hosts to inject permission prompts.
+        ///
+        /// # Typical `start` errors
+        /// - `invalid-argument`:          The `remote-address` has the wrong address family. (EAFNOSUPPORT)
+        /// - `invalid-argument`:          `remote-address` is a non-IPv4-mapped IPv6 address, but the socket was bound to a specific IPv4-mapped IPv6 address. (or vice versa)
+        /// - `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 already bound to a different network. The `network` passed to `connect` must be identical to the one passed to `bind`.
+        ///
+        /// # Typical `finish` errors
+        /// - `address-in-use`:            Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE, EADDRNOTAVAIL on Linux, EAGAIN on BSD)
+        /// - `not-in-progress`:           A `connect` operation is not in progress.
+        /// - `would-block`:               Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN)
+        ///
+        /// # References
+        /// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/connect.html>
+        /// - <https://man7.org/linux/man-pages/man2/connect.2.html>
+        /// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-connect>
+        /// - <https://man.freebsd.org/cgi/man.cgi?connect>
+        start-connect: func(network: borrow<network>, remote-address: ip-socket-address) -> result<_, error-code>;
+        finish-connect: func() -> result<_, error-code>;
+
+        /// Receive messages on the socket.
+        ///
+        /// This function attempts to receive up to `max-results` datagrams on the socket without blocking.
+        /// The returned list may contain fewer elements than requested, but never more.
+        /// If `max-results` is 0, this function returns successfully with an empty list.
+        ///
+        /// # Typical errors
+        /// - `invalid-state`:      The socket is not bound to any local address. (EINVAL)
+        /// - `remote-unreachable`: The remote address is not reachable. (ECONNREFUSED, ECONNRESET, ENETRESET on Windows, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN)
+        /// - `would-block`:        There is no pending data available to be read at the moment. (EWOULDBLOCK, EAGAIN)
+        ///
+        /// # References
+        /// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/recvfrom.html>
+        /// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/recvmsg.html>
+        /// - <https://man7.org/linux/man-pages/man2/recv.2.html>
+        /// - <https://man7.org/linux/man-pages/man2/recvmmsg.2.html>
+        /// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-recv>
+        /// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-recvfrom>
+        /// - <https://learn.microsoft.com/en-us/previous-versions/windows/desktop/legacy/ms741687(v=vs.85)>
+        /// - <https://man.freebsd.org/cgi/man.cgi?query=recv&sektion=2>
+        receive: func(max-results: u64) -> result<list<datagram>, error-code>;
+
+        /// Send messages on the socket.
+        ///
+        /// This function attempts to send all provided `datagrams` on the socket without blocking and
+        /// returns how many messages were actually sent (or queued for sending).
+        ///
+        /// This function semantically behaves the same as iterating the `datagrams` list and sequentially
+        /// sending each individual datagram until either the end of the list has been reached or the first error occurred.
+        /// If at least one datagram has been sent successfully, this function never returns an error.
+        ///
+        /// If the input list is empty, the function returns `ok(0)`.
+        ///
+        /// The remote address option is required. To send a message to the "connected" peer,
+        /// call `remote-address` to get their address.
+        ///
+        /// # Typical errors
+        /// - `invalid-argument`:        The `remote-address` has the wrong address family. (EAFNOSUPPORT)
+        /// - `invalid-argument`:        `remote-address` is a non-IPv4-mapped IPv6 address, but the socket was bound to a specific IPv4-mapped IPv6 address. (or vice versa)
+        /// - `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 the `datagram.remote-address` does not match the address passed to `connect`. (EISCONN)
+        /// - `invalid-state`:           The socket is not bound to any local address. Unlike POSIX, this function does not perform an implicit bind.
+        /// - `remote-unreachable`:      The remote address is not reachable. (ECONNREFUSED, ECONNRESET, ENETRESET on Windows, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN)
+        /// - `datagram-too-large`:      The datagram is too large. (EMSGSIZE)
+        /// - `would-block`:             The send buffer is currently full. (EWOULDBLOCK, EAGAIN)
+        ///
+        /// # References
+        /// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/sendto.html>
+        /// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/sendmsg.html>
+        /// - <https://man7.org/linux/man-pages/man2/send.2.html>
+        /// - <https://man7.org/linux/man-pages/man2/sendmmsg.2.html>
+        /// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-send>
+        /// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-sendto>
+        /// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsasendmsg>
+        /// - <https://man.freebsd.org/cgi/man.cgi?query=send&sektion=2>
+        send: func(datagrams: list<datagram>) -> result<u64, 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 `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
+        /// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/getsockname.html>
+        /// - <https://man7.org/linux/man-pages/man2/getsockname.2.html>
+        /// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-getsockname>
+        /// - <https://man.freebsd.org/cgi/man.cgi?getsockname>
+        local-address: func() -> result<ip-socket-address, error-code>;
+
+        /// Get the address set with `connect`.
+        ///
+        /// # Typical errors
+        /// - `invalid-state`: The socket is not connected to a remote address. (ENOTCONN)
+        ///
+        /// # References
+        /// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpeername.html>
+        /// - <https://man7.org/linux/man-pages/man2/getpeername.2.html>
+        /// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-getpeername>
+        /// - <https://man.freebsd.org/cgi/man.cgi?query=getpeername&sektion=2&n=1>
+        remote-address: func() -> result<ip-socket-address, error-code>;
+
+        /// Whether this is a IPv4 or IPv6 socket.
+        ///
+        /// Equivalent to the SO_DOMAIN socket option.
+        address-family: func() -> ip-address-family;
+
+        /// Whether IPv4 compatibility (dual-stack) mode is disabled or not.
+        ///
+        /// Equivalent to the IPV6_V6ONLY socket option.
+        ///
+        /// # Typical errors
+        /// - `not-supported`:        (get/set) `this` socket is an IPv4 socket.
+        /// - `invalid-state`:        (set) The socket is already bound.
+        /// - `not-supported`:        (set) Host does not support dual-stack sockets. (Implementations are not required to.)
+        ipv6-only: func() -> result<bool, error-code>;
+        set-ipv6-only: func(value: bool) -> result<_, error-code>;
+
+        /// Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options.
+        unicast-hop-limit: func() -> result<u8, error-code>;
+        set-unicast-hop-limit: func(value: u8) -> result<_, error-code>;
+
+        /// The kernel buffer space reserved for sends/receives on this socket.
+        ///
+        /// Note #1: an implementation may choose to cap or round the buffer size when setting the value.
+        ///     In other words, after setting a value, reading the same setting back may return a different value.
+        ///
+        /// Note #2: there is not necessarily a direct relationship between the kernel buffer size and the bytes of
+        ///     actual data to be sent/received by the application, because the kernel might also use the buffer space
+        ///     for internal metadata structures.
+        ///
+        /// Equivalent to the SO_RCVBUF and SO_SNDBUF socket options.
+        receive-buffer-size: func() -> result<u64, error-code>;
+        set-receive-buffer-size: func(value: u64) -> result<_, error-code>;
+        send-buffer-size: func() -> result<u64, error-code>;
+        set-send-buffer-size: func(value: u64) -> result<_, error-code>;
+
+        /// Create a `pollable` which will resolve once the socket is ready for I/O.
+        ///
+        /// Note: this function is here for WASI Preview2 only.
+        /// It's planned to be removed when `future` is natively supported in Preview3.
+        subscribe: func() -> pollable;
+    }
 }
diff --git a/crates/wasi-preview1-component-adapter/src/lib.rs b/crates/wasi-preview1-component-adapter/src/lib.rs
index f83dfd2ed..ff09003d1 100644
--- a/crates/wasi-preview1-component-adapter/src/lib.rs
+++ b/crates/wasi-preview1-component-adapter/src/lib.rs
@@ -30,7 +30,7 @@ pub mod bindings {
     #[cfg(feature = "command")]
     wit_bindgen::generate!({
         path: "../wasi/wit",
-        world: "wasi:cli/command@0.2.0-rc-2023-10-18",
+        world: "wasi:cli/command",
         std_feature,
         raw_strings,
         // Automatically generated bindings for these functions will allocate
diff --git a/crates/wasi/src/preview2/command.rs b/crates/wasi/src/preview2/command.rs
index 299b31c67..898311157 100644
--- a/crates/wasi/src/preview2/command.rs
+++ b/crates/wasi/src/preview2/command.rs
@@ -1,7 +1,7 @@
 use crate::preview2::WasiView;
 
 wasmtime::component::bindgen!({
-    world: "wasi:cli/command@0.2.0-rc-2023-10-18",
+    world: "wasi:cli/command",
     tracing: true,
     async: true,
     with: {
@@ -60,7 +60,7 @@ pub mod sync {
     use crate::preview2::WasiView;
 
     wasmtime::component::bindgen!({
-        world: "wasi:cli/command@0.2.0-rc-2023-10-18",
+        world: "wasi:cli/command",
         tracing: true,
         async: false,
         with: {
diff --git a/crates/wasi/src/preview2/mod.rs b/crates/wasi/src/preview2/mod.rs
index 902657f48..6208cb112 100644
--- a/crates/wasi/src/preview2/mod.rs
+++ b/crates/wasi/src/preview2/mod.rs
@@ -93,7 +93,7 @@ pub mod bindings {
 
     wasmtime::component::bindgen!({
         path: "wit",
-        interfaces: "include wasi:cli/reactor@0.2.0-rc-2023-10-18;",
+        world: "wasi:cli/reactor",
         tracing: true,
         async: {
             // Only these functions are `async` and everything else is sync
diff --git a/crates/wasi/wit/deps/sockets/instance-network.wit b/crates/wasi/wit/deps/sockets/instance-network.wit
index 14e4479e6..e455d0ff7 100644
--- a/crates/wasi/wit/deps/sockets/instance-network.wit
+++ b/crates/wasi/wit/deps/sockets/instance-network.wit
@@ -1,9 +1,9 @@
 
 /// This interface provides a value-export of the default network handle..
 interface instance-network {
-	use network.{network};
+    use network.{network};
 
-	/// Get a handle to the default network.
-	instance-network: func() -> network;
+    /// Get a handle to the default network.
+    instance-network: func() -> network;
 
 }
diff --git a/crates/wasi/wit/deps/sockets/ip-name-lookup.wit b/crates/wasi/wit/deps/sockets/ip-name-lookup.wit
index f2dab32ff..4f9432991 100644
--- a/crates/wasi/wit/deps/sockets/ip-name-lookup.wit
+++ b/crates/wasi/wit/deps/sockets/ip-name-lookup.wit
@@ -1,61 +1,61 @@
 
 interface ip-name-lookup {
-	use wasi:io/poll@0.2.0-rc-2023-10-18.{pollable};
-	use network.{network, error-code, ip-address, ip-address-family};
+    use wasi:io/poll@0.2.0-rc-2023-10-18.{pollable};
+    use network.{network, error-code, ip-address, ip-address-family};
 
 
-	/// Resolve an internet host name to a list of IP addresses.
-	///
-	/// See the wasi-socket proposal README.md for a comparison with getaddrinfo.
-	///
-	/// # Parameters
-	/// - `name`: The name to look up. IP addresses are not allowed. Unicode domain names are automatically converted
-	///     to ASCII using IDNA encoding.
-	/// - `address-family`: If provided, limit the results to addresses of this specific address family.
-	/// - `include-unavailable`: When set to true, this function will also return addresses of which the runtime
-	///   thinks (or knows) can't be connected to at the moment. For example, this will return IPv6 addresses on
-	///   systems without an active IPv6 interface. Notes:
-	///     - Even when no public IPv6 interfaces are present or active, names like "localhost" can still resolve to an IPv6 address.
-	///     - Whatever is "available" or "unavailable" is volatile and can change everytime a network cable is unplugged.
-	///
-	/// This function never blocks. It either immediately fails or immediately returns successfully with a `resolve-address-stream`
-	/// that can be used to (asynchronously) fetch the results.
-	///
-	/// At the moment, the stream never completes successfully with 0 items. Ie. the first call
-	/// to `resolve-next-address` never returns `ok(none)`. This may change in the future.
-	///
-	/// # Typical errors
-	/// - `invalid-argument`:     `name` is a syntactically invalid domain name.
-	/// - `invalid-argument`:     `name` is an IP address.
-	/// - `not-supported`:        The specified `address-family` is not supported. (EAI_FAMILY)
-	///
-	/// # References:
-	/// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/getaddrinfo.html>
-	/// - <https://man7.org/linux/man-pages/man3/getaddrinfo.3.html>
-	/// - <https://learn.microsoft.com/en-us/windows/win32/api/ws2tcpip/nf-ws2tcpip-getaddrinfo>
-	/// - <https://man.freebsd.org/cgi/man.cgi?query=getaddrinfo&sektion=3>
-	resolve-addresses: func(network: borrow<network>, name: string, address-family: option<ip-address-family>, include-unavailable: bool) -> result<resolve-address-stream, error-code>;
+    /// Resolve an internet host name to a list of IP addresses.
+    ///
+    /// See the wasi-socket proposal README.md for a comparison with getaddrinfo.
+    ///
+    /// # Parameters
+    /// - `name`: The name to look up. IP addresses are not allowed. Unicode domain names are automatically converted
+    ///     to ASCII using IDNA encoding.
+    /// - `address-family`: If provided, limit the results to addresses of this specific address family.
+    /// - `include-unavailable`: When set to true, this function will also return addresses of which the runtime
+    ///   thinks (or knows) can't be connected to at the moment. For example, this will return IPv6 addresses on
+    ///   systems without an active IPv6 interface. Notes:
+    ///     - Even when no public IPv6 interfaces are present or active, names like "localhost" can still resolve to an IPv6 address.
+    ///     - Whatever is "available" or "unavailable" is volatile and can change everytime a network cable is unplugged.
+    ///
+    /// This function never blocks. It either immediately fails or immediately returns successfully with a `resolve-address-stream`
+    /// that can be used to (asynchronously) fetch the results.
+    ///
+    /// At the moment, the stream never completes successfully with 0 items. Ie. the first call
+    /// to `resolve-next-address` never returns `ok(none)`. This may change in the future.
+    ///
+    /// # Typical errors
+    /// - `invalid-argument`:     `name` is a syntactically invalid domain name.
+    /// - `invalid-argument`:     `name` is an IP address.
+    /// - `not-supported`:        The specified `address-family` is not supported. (EAI_FAMILY)
+    ///
+    /// # References:
+    /// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/getaddrinfo.html>
+    /// - <https://man7.org/linux/man-pages/man3/getaddrinfo.3.html>
+    /// - <https://learn.microsoft.com/en-us/windows/win32/api/ws2tcpip/nf-ws2tcpip-getaddrinfo>
+    /// - <https://man.freebsd.org/cgi/man.cgi?query=getaddrinfo&sektion=3>
+    resolve-addresses: func(network: borrow<network>, name: string, address-family: option<ip-address-family>, include-unavailable: bool) -> result<resolve-address-stream, error-code>;
 
-	resource resolve-address-stream {
-		/// Returns the next address from the resolver.
-		///
-		/// This function should be called multiple times. On each call, it will
-		/// return the next address in connection order preference. If all
-		/// addresses have been exhausted, this function returns `none`.
-		///
-		/// This function never returns IPv4-mapped IPv6 addresses.
-		///
-		/// # Typical errors
-		/// - `name-unresolvable`:          Name does not exist or has no suitable associated IP addresses. (EAI_NONAME, EAI_NODATA, EAI_ADDRFAMILY)
-		/// - `temporary-resolver-failure`: A temporary failure in name resolution occurred. (EAI_AGAIN)
-		/// - `permanent-resolver-failure`: A permanent failure in name resolution occurred. (EAI_FAIL)
-		/// - `would-block`:                A result is not available yet. (EWOULDBLOCK, EAGAIN)
-		resolve-next-address: func() -> result<option<ip-address>, error-code>;
+    resource resolve-address-stream {
+        /// Returns the next address from the resolver.
+        ///
+        /// This function should be called multiple times. On each call, it will
+        /// return the next address in connection order preference. If all
+        /// addresses have been exhausted, this function returns `none`.
+        ///
+        /// This function never returns IPv4-mapped IPv6 addresses.
+        ///
+        /// # Typical errors
+        /// - `name-unresolvable`:          Name does not exist or has no suitable associated IP addresses. (EAI_NONAME, EAI_NODATA, EAI_ADDRFAMILY)
+        /// - `temporary-resolver-failure`: A temporary failure in name resolution occurred. (EAI_AGAIN)
+        /// - `permanent-resolver-failure`: A permanent failure in name resolution occurred. (EAI_FAIL)
+        /// - `would-block`:                A result is not available yet. (EWOULDBLOCK, EAGAIN)
+        resolve-next-address: func() -> result<option<ip-address>, error-code>;
 
-		/// Create a `pollable` which will resolve once the stream is ready for I/O.
-		///
-		/// Note: this function is here for WASI Preview2 only.
-		/// It's planned to be removed when `future` is natively supported in Preview3.
-		subscribe: func() -> pollable;
-	}
+        /// Create a `pollable` which will resolve once the stream is ready for I/O.
+        ///
+        /// Note: this function is here for WASI Preview2 only.
+        /// It's planned to be removed when `future` is natively supported in Preview3.
+        subscribe: func() -> pollable;
+    }
 }
diff --git a/crates/wasi/wit/deps/sockets/network.wit b/crates/wasi/wit/deps/sockets/network.wit
index 861ec673d..6bb07cd6f 100644
--- a/crates/wasi/wit/deps/sockets/network.wit
+++ b/crates/wasi/wit/deps/sockets/network.wit
@@ -1,147 +1,147 @@
 
 interface network {
-	/// An opaque resource that represents access to (a subset of) the network.
-	/// This enables context-based security for networking.
-	/// There is no need for this to map 1:1 to a physical network interface.
-	resource network;
+    /// An opaque resource that represents access to (a subset of) the network.
+    /// This enables context-based security for networking.
+    /// There is no need for this to map 1:1 to a physical network interface.
+    resource network;
 
-	/// 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:
-	/// - `unknown`
-	/// - `access-denied`
-	/// - `not-supported`
-	/// - `out-of-memory`
-	/// - `concurrency-conflict`
-	///
-	/// See each individual API for what the POSIX equivalents are. They sometimes differ per API.
-	enum error-code {
-		// ### GENERAL ERRORS ###
+    /// 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:
+    /// - `unknown`
+    /// - `access-denied`
+    /// - `not-supported`
+    /// - `out-of-memory`
+    /// - `concurrency-conflict`
+    ///
+    /// See each individual API for what the POSIX equivalents are. They sometimes differ per API.
+    enum error-code {
+        // ### GENERAL ERRORS ###
 
-		/// Unknown error
-		unknown,
+        /// Unknown error
+        unknown,
 
-		/// Access denied.
-		///
-		/// POSIX equivalent: EACCES, EPERM
-		access-denied,
+        /// Access denied.
+        ///
+        /// POSIX equivalent: EACCES, EPERM
+        access-denied,
 
-		/// The operation is not supported.
-		///
-		/// POSIX equivalent: EOPNOTSUPP
-		not-supported,
+        /// The operation is not supported.
+        ///
+        /// POSIX equivalent: EOPNOTSUPP
+        not-supported,
 
-		/// One of the arguments is invalid.
-		/// 
-		/// POSIX equivalent: EINVAL
-		invalid-argument,
+        /// One of the arguments is invalid.
+        ///
+        /// POSIX equivalent: EINVAL
+        invalid-argument,
 
-		/// Not enough memory to complete the operation.
-		///
-		/// POSIX equivalent: ENOMEM, ENOBUFS, EAI_MEMORY
-		out-of-memory,
+        /// Not enough memory to complete the operation.
+        ///
+        /// POSIX equivalent: ENOMEM, ENOBUFS, EAI_MEMORY
+        out-of-memory,
 
-		/// The operation timed out before it could finish completely.
-		timeout,
+        /// The operation timed out before it could finish completely.
+        timeout,
 
-		/// This operation is incompatible with another asynchronous operation that is already in progress.
-		///
-		/// POSIX equivalent: EALREADY
-		concurrency-conflict,
+        /// This operation is incompatible with another asynchronous operation that is already in progress.
+        ///
+        /// POSIX equivalent: EALREADY
+        concurrency-conflict,
 
-		/// Trying to finish an asynchronous operation that:
-		/// - has not been started yet, or:
-		/// - was already finished by a previous `finish-*` call.
-		///
-		/// Note: this is scheduled to be removed when `future`s are natively supported.
-		not-in-progress,
+        /// Trying to finish an asynchronous operation that:
+        /// - has not been started yet, or:
+        /// - was already finished by a previous `finish-*` call.
+        ///
+        /// Note: this is scheduled to be removed when `future`s are natively supported.
+        not-in-progress,
 
-		/// The operation has been aborted because it could not be completed immediately.
-		///
-		/// Note: this is scheduled to be removed when `future`s are natively supported.
-		would-block,
+        /// The operation has been aborted because it could not be completed immediately.
+        ///
+        /// Note: this is scheduled to be removed when `future`s are natively supported.
+        would-block,
 
 
 
-		// ### TCP & UDP SOCKET ERRORS ###
+        // ### TCP & UDP SOCKET ERRORS ###
 
-		/// The operation is not valid in the socket's current state.
-		invalid-state,
+        /// The operation is not valid in the socket's current state.
+        invalid-state,
 
-		/// A new socket resource could not be created because of a system limit.
-		new-socket-limit,
+        /// A new socket resource could not be created because of a system limit.
+        new-socket-limit,
 
-		/// A bind operation failed because the provided address is not an address that the `network` can bind to.
-		address-not-bindable,
+        /// A bind operation failed because the provided address is not an address that the `network` can bind to.
+        address-not-bindable,
 
-		/// A bind operation failed because the provided address is already in use or because there are no ephemeral ports available.
-		address-in-use,
+        /// A bind operation failed because the provided address is already in use or because there are no ephemeral ports available.
+        address-in-use,
 
-		/// The remote address is not reachable
-		remote-unreachable,
+        /// The remote address is not reachable
+        remote-unreachable,
 
 
-		// ### TCP SOCKET ERRORS ###
+        // ### TCP SOCKET ERRORS ###
 
-		/// The connection was forcefully rejected
-		connection-refused,
+        /// The connection was forcefully rejected
+        connection-refused,
 
-		/// The connection was reset.
-		connection-reset,
+        /// The connection was reset.
+        connection-reset,
 
-		/// A connection was aborted.
-		connection-aborted,
+        /// A connection was aborted.
+        connection-aborted,
 
 
-		// ### UDP SOCKET ERRORS ###
-		datagram-too-large,
+        // ### UDP SOCKET ERRORS ###
+        datagram-too-large,
 
 
-		// ### NAME LOOKUP ERRORS ###
+        // ### NAME LOOKUP ERRORS ###
 
-		/// Name does not exist or has no suitable associated IP addresses.
-		name-unresolvable,
+        /// Name does not exist or has no suitable associated IP addresses.
+        name-unresolvable,
 
-		/// A temporary failure in name resolution occurred.
-		temporary-resolver-failure,
+        /// A temporary failure in name resolution occurred.
+        temporary-resolver-failure,
 
-		/// A permanent failure in name resolution occurred.
-		permanent-resolver-failure,
-	}
+        /// A permanent failure in name resolution occurred.
+        permanent-resolver-failure,
+    }
 
-	enum ip-address-family {
-		/// Similar to `AF_INET` in POSIX.
-		ipv4,
+    enum ip-address-family {
+        /// Similar to `AF_INET` in POSIX.
+        ipv4,
 
-		/// Similar to `AF_INET6` in POSIX.
-		ipv6,
-	}
+        /// Similar to `AF_INET6` in POSIX.
+        ipv6,
+    }
 
-	type ipv4-address = tuple<u8, u8, u8, u8>;
-	type ipv6-address = tuple<u16, u16, u16, u16, u16, u16, u16, u16>;
+    type ipv4-address = tuple<u8, u8, u8, u8>;
+    type ipv6-address = tuple<u16, u16, u16, u16, u16, u16, u16, u16>;
 
-	variant ip-address {
-		ipv4(ipv4-address),
-		ipv6(ipv6-address),
-	}
+    variant ip-address {
+        ipv4(ipv4-address),
+        ipv6(ipv6-address),
+    }
 
-	record ipv4-socket-address {
-		port: u16, // sin_port
-		address: ipv4-address, // sin_addr
-	}
+    record ipv4-socket-address {
+        port: u16, // sin_port
+        address: ipv4-address, // sin_addr
+    }
 
-	record ipv6-socket-address {
-		port: u16, // sin6_port
-		flow-info: u32, // sin6_flowinfo
-		address: ipv6-address, // sin6_addr
-		scope-id: u32, // sin6_scope_id
-	}
+    record ipv6-socket-address {
+        port: u16, // sin6_port
+        flow-info: u32, // sin6_flowinfo
+        address: ipv6-address, // sin6_addr
+        scope-id: u32, // sin6_scope_id
+    }
 
-	variant ip-socket-address {
-		ipv4(ipv4-socket-address),
-		ipv6(ipv6-socket-address),
-	}
+    variant ip-socket-address {
+        ipv4(ipv4-socket-address),
+        ipv6(ipv6-socket-address),
+    }
 
 }
diff --git a/crates/wasi/wit/deps/sockets/tcp-create-socket.wit b/crates/wasi/wit/deps/sockets/tcp-create-socket.wit
index a9a33738b..768a07c85 100644
--- a/crates/wasi/wit/deps/sockets/tcp-create-socket.wit
+++ b/crates/wasi/wit/deps/sockets/tcp-create-socket.wit
@@ -1,26 +1,26 @@
 
 interface tcp-create-socket {
-	use network.{network, error-code, ip-address-family};
-	use tcp.{tcp-socket};
+    use network.{network, error-code, ip-address-family};
+    use tcp.{tcp-socket};
 
-	/// Create a new TCP socket.
-	///
-	/// Similar to `socket(AF_INET or AF_INET6, SOCK_STREAM, IPPROTO_TCP)` in POSIX.
-	///
-	/// This function does not require a network capability handle. This is considered to be safe because
-	/// at time of creation, the socket is not bound to any `network` yet. Up to the moment `bind`/`listen`/`connect`
-	/// is called, the socket is effectively an in-memory configuration object, unable to communicate with the outside world.
-	///
-	/// All sockets are non-blocking. Use the wasi-poll interface to block on asynchronous operations.
-	///
-	/// # Typical errors
-	/// - `not-supported`:     The specified `address-family` is not supported. (EAFNOSUPPORT)
-	/// - `new-socket-limit`:  The new socket resource could not be created because of a system limit. (EMFILE, ENFILE)
-	///
-	/// # References
-	/// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/socket.html>
-	/// - <https://man7.org/linux/man-pages/man2/socket.2.html>
-	/// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsasocketw>
-	/// - <https://man.freebsd.org/cgi/man.cgi?query=socket&sektion=2>
-	create-tcp-socket: func(address-family: ip-address-family) -> result<tcp-socket, error-code>;
+    /// Create a new TCP socket.
+    ///
+    /// Similar to `socket(AF_INET or AF_INET6, SOCK_STREAM, IPPROTO_TCP)` in POSIX.
+    ///
+    /// This function does not require a network capability handle. This is considered to be safe because
+    /// at time of creation, the socket is not bound to any `network` yet. Up to the moment `bind`/`listen`/`connect`
+    /// is called, the socket is effectively an in-memory configuration object, unable to communicate with the outside world.
+    ///
+    /// All sockets are non-blocking. Use the wasi-poll interface to block on asynchronous operations.
+    ///
+    /// # Typical errors
+    /// - `not-supported`:     The specified `address-family` is not supported. (EAFNOSUPPORT)
+    /// - `new-socket-limit`:  The new socket resource could not be created because of a system limit. (EMFILE, ENFILE)
+    ///
+    /// # References
+    /// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/socket.html>
+    /// - <https://man7.org/linux/man-pages/man2/socket.2.html>
+    /// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsasocketw>
+    /// - <https://man.freebsd.org/cgi/man.cgi?query=socket&sektion=2>
+    create-tcp-socket: func(address-family: ip-address-family) -> result<tcp-socket, error-code>;
 }
diff --git a/crates/wasi/wit/deps/sockets/tcp.wit b/crates/wasi/wit/deps/sockets/tcp.wit
index 448f629e5..c316a7245 100644
--- a/crates/wasi/wit/deps/sockets/tcp.wit
+++ b/crates/wasi/wit/deps/sockets/tcp.wit
@@ -1,268 +1,268 @@
 
 interface tcp {
-	use wasi:io/streams@0.2.0-rc-2023-10-18.{input-stream, output-stream};
-	use wasi:io/poll@0.2.0-rc-2023-10-18.{pollable};
-	use network.{network, error-code, ip-socket-address, ip-address-family};
+    use wasi:io/streams@0.2.0-rc-2023-10-18.{input-stream, output-stream};
+    use wasi:io/poll@0.2.0-rc-2023-10-18.{pollable};
+    use network.{network, error-code, ip-socket-address, ip-address-family};
 
-	enum shutdown-type {
-		/// Similar to `SHUT_RD` in POSIX.
-		receive,
+    enum shutdown-type {
+        /// Similar to `SHUT_RD` in POSIX.
+        receive,
 
-		/// Similar to `SHUT_WR` in POSIX.
-		send,
+        /// Similar to `SHUT_WR` in POSIX.
+        send,
 
-		/// Similar to `SHUT_RDWR` in POSIX.
-		both,
-	}
+        /// Similar to `SHUT_RDWR` in POSIX.
+        both,
+    }
 
 
-	/// A TCP socket handle.
-	resource tcp-socket {
-		/// Bind the socket to a specific network on 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.
-		///
-		/// When a socket is not explicitly bound, the first invocation to a listen or connect operation will
-		/// implicitly bind the socket.
-		///
-		/// Unlike in POSIX, this function is async. This enables interactive WASI hosts to inject permission prompts.
-		///
-		/// # Typical `start` 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, but the socket has `ipv6-only` enabled. (EINVAL)
-		/// - `invalid-state`:             The socket is already bound. (EINVAL)
-		///
-		/// # Typical `finish` errors
-		/// - `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 the `network` can bind to. (EADDRNOTAVAIL)
-		/// - `not-in-progress`:           A `bind` operation is not in progress.
-		/// - `would-block`:               Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN)
-		///
-		/// # References
-		/// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/bind.html>
-		/// - <https://man7.org/linux/man-pages/man2/bind.2.html>
-		/// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-bind>
-		/// - <https://man.freebsd.org/cgi/man.cgi?query=bind&sektion=2&format=html>
-		start-bind: func(network: borrow<network>, local-address: ip-socket-address) -> result<_, error-code>;
-		finish-bind: func() -> result<_, error-code>;
+    /// A TCP socket handle.
+    resource tcp-socket {
+        /// Bind the socket to a specific network on 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.
+        ///
+        /// When a socket is not explicitly bound, the first invocation to a listen or connect operation will
+        /// implicitly bind the socket.
+        ///
+        /// Unlike in POSIX, this function is async. This enables interactive WASI hosts to inject permission prompts.
+        ///
+        /// # Typical `start` 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, but the socket has `ipv6-only` enabled. (EINVAL)
+        /// - `invalid-state`:             The socket is already bound. (EINVAL)
+        ///
+        /// # Typical `finish` errors
+        /// - `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 the `network` can bind to. (EADDRNOTAVAIL)
+        /// - `not-in-progress`:           A `bind` operation is not in progress.
+        /// - `would-block`:               Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN)
+        ///
+        /// # References
+        /// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/bind.html>
+        /// - <https://man7.org/linux/man-pages/man2/bind.2.html>
+        /// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-bind>
+        /// - <https://man.freebsd.org/cgi/man.cgi?query=bind&sektion=2&format=html>
+        start-bind: func(network: borrow<network>, local-address: ip-socket-address) -> result<_, error-code>;
+        finish-bind: func() -> result<_, error-code>;
 
-		/// Connect to a remote endpoint.
-		///
-		/// On success:
-		/// - the socket is transitioned into the Connection state
-		/// - a pair of streams is returned that can be used to read & write to the connection
-		///
-		/// POSIX mentions:
-		/// > If connect() fails, the state of the socket is unspecified. Conforming applications should
-		/// > close the file descriptor and create a new socket before attempting to reconnect.
-		///
-		/// WASI prescribes the following behavior:
-		/// - If `connect` fails because an input/state validation error, the socket should remain usable.
-		/// - If a connection was actually attempted but failed, the socket should become unusable for further network communication.
-		///   Besides `drop`, any method after such a failure may return an error.
-		///
-		/// # Typical `start` 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, but the socket has `ipv6-only` enabled. (EINVAL, EADDRNOTAVAIL on Illumos)
-		/// - `invalid-argument`:          `remote-address` is a non-IPv4-mapped IPv6 address, but the socket was bound to a specific IPv4-mapped IPv6 address. (or vice versa)
-		/// - `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-argument`:          The socket is already attached to a different network. The `network` passed to `connect` must be identical to the one passed to `bind`.
-		/// - `invalid-state`:             The socket is already in the Connection state. (EISCONN)
-		/// - `invalid-state`:             The socket is already in the Listener state. (EOPNOTSUPP, EINVAL on Windows)
-		///
-		/// # Typical `finish` errors
-		/// - `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)
-		/// - `address-in-use`:            Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE, EADDRNOTAVAIL on Linux, EAGAIN on BSD)
-		/// - `not-in-progress`:           A `connect` operation is not in progress.
-		/// - `would-block`:               Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN)
-		///
-		/// # References
-		/// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/connect.html>
-		/// - <https://man7.org/linux/man-pages/man2/connect.2.html>
-		/// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-connect>
-		/// - <https://man.freebsd.org/cgi/man.cgi?connect>
-		start-connect: func(network: borrow<network>, remote-address: ip-socket-address) -> result<_, error-code>;
-		finish-connect: func() -> result<tuple<input-stream, output-stream>, error-code>;
+        /// Connect to a remote endpoint.
+        ///
+        /// On success:
+        /// - the socket is transitioned into the Connection state
+        /// - a pair of streams is returned that can be used to read & write to the connection
+        ///
+        /// POSIX mentions:
+        /// > If connect() fails, the state of the socket is unspecified. Conforming applications should
+        /// > close the file descriptor and create a new socket before attempting to reconnect.
+        ///
+        /// WASI prescribes the following behavior:
+        /// - If `connect` fails because an input/state validation error, the socket should remain usable.
+        /// - If a connection was actually attempted but failed, the socket should become unusable for further network communication.
+        ///   Besides `drop`, any method after such a failure may return an error.
+        ///
+        /// # Typical `start` 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, but the socket has `ipv6-only` enabled. (EINVAL, EADDRNOTAVAIL on Illumos)
+        /// - `invalid-argument`:          `remote-address` is a non-IPv4-mapped IPv6 address, but the socket was bound to a specific IPv4-mapped IPv6 address. (or vice versa)
+        /// - `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-argument`:          The socket is already attached to a different network. The `network` passed to `connect` must be identical to the one passed to `bind`.
+        /// - `invalid-state`:             The socket is already in the Connection state. (EISCONN)
+        /// - `invalid-state`:             The socket is already in the Listener state. (EOPNOTSUPP, EINVAL on Windows)
+        ///
+        /// # Typical `finish` errors
+        /// - `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)
+        /// - `address-in-use`:            Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE, EADDRNOTAVAIL on Linux, EAGAIN on BSD)
+        /// - `not-in-progress`:           A `connect` operation is not in progress.
+        /// - `would-block`:               Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN)
+        ///
+        /// # References
+        /// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/connect.html>
+        /// - <https://man7.org/linux/man-pages/man2/connect.2.html>
+        /// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-connect>
+        /// - <https://man.freebsd.org/cgi/man.cgi?connect>
+        start-connect: func(network: borrow<network>, remote-address: ip-socket-address) -> result<_, error-code>;
+        finish-connect: func() -> result<tuple<input-stream, output-stream>, error-code>;
 
-		/// Start listening for new connections.
-		///
-		/// Transitions the socket into the Listener state.
-		///
-		/// Unlike POSIX:
-		/// - this function is async. This enables interactive WASI hosts to inject permission prompts.
-		/// - the socket must already be explicitly bound.
-		///
-		/// # Typical `start` errors
-		/// - `invalid-state`:             The socket is not bound to any local address. (EDESTADDRREQ)
-		/// - `invalid-state`:             The socket is already in the Connection state. (EISCONN, EINVAL on BSD)
-		/// - `invalid-state`:             The socket is already in the Listener state.
-		///
-		/// # Typical `finish` errors
-		/// - `address-in-use`:            Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE)
-		/// - `not-in-progress`:           A `listen` operation is not in progress.
-		/// - `would-block`:               Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN)
-		///
-		/// # References
-		/// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/listen.html>
-		/// - <https://man7.org/linux/man-pages/man2/listen.2.html>
-		/// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-listen>
-		/// - <https://man.freebsd.org/cgi/man.cgi?query=listen&sektion=2>
-		start-listen: func() -> result<_, error-code>;
-		finish-listen: func() -> result<_, error-code>;
+        /// Start listening for new connections.
+        ///
+        /// Transitions the socket into the Listener state.
+        ///
+        /// Unlike POSIX:
+        /// - this function is async. This enables interactive WASI hosts to inject permission prompts.
+        /// - the socket must already be explicitly bound.
+        ///
+        /// # Typical `start` errors
+        /// - `invalid-state`:             The socket is not bound to any local address. (EDESTADDRREQ)
+        /// - `invalid-state`:             The socket is already in the Connection state. (EISCONN, EINVAL on BSD)
+        /// - `invalid-state`:             The socket is already in the Listener state.
+        ///
+        /// # Typical `finish` errors
+        /// - `address-in-use`:            Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE)
+        /// - `not-in-progress`:           A `listen` operation is not in progress.
+        /// - `would-block`:               Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN)
+        ///
+        /// # References
+        /// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/listen.html>
+        /// - <https://man7.org/linux/man-pages/man2/listen.2.html>
+        /// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-listen>
+        /// - <https://man.freebsd.org/cgi/man.cgi?query=listen&sektion=2>
+        start-listen: func() -> result<_, error-code>;
+        finish-listen: func() -> result<_, error-code>;
 
-		/// Accept a new client socket.
-		///
-		/// The returned socket is bound and in the Connection state. The following properties are inherited from the listener socket:
-		/// - `address-family`
-		/// - `ipv6-only`
-		/// - `keep-alive`
-		/// - `no-delay`
-		/// - `unicast-hop-limit`
-		/// - `receive-buffer-size`
-		/// - `send-buffer-size`
-		///
-		/// On success, this function returns the newly accepted client socket along with
-		/// a pair of streams that can be used to read & write to the connection.
-		///
-		/// # Typical errors
-		/// - `invalid-state`:      Socket is not in the Listener state. (EINVAL)
-		/// - `would-block`:        No pending connections at the moment. (EWOULDBLOCK, EAGAIN)
-		/// - `connection-aborted`: An incoming connection was pending, but was terminated by the client before this listener could accept it. (ECONNABORTED)
-		/// - `new-socket-limit`:   The new socket resource could not be created because of a system limit. (EMFILE, ENFILE)
-		///
-		/// # References
-		/// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/accept.html>
-		/// - <https://man7.org/linux/man-pages/man2/accept.2.html>
-		/// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-accept>
-		/// - <https://man.freebsd.org/cgi/man.cgi?query=accept&sektion=2>
-		accept: func() -> result<tuple<tcp-socket, input-stream, output-stream>, error-code>;
+        /// Accept a new client socket.
+        ///
+        /// The returned socket is bound and in the Connection state. The following properties are inherited from the listener socket:
+        /// - `address-family`
+        /// - `ipv6-only`
+        /// - `keep-alive`
+        /// - `no-delay`
+        /// - `unicast-hop-limit`
+        /// - `receive-buffer-size`
+        /// - `send-buffer-size`
+        ///
+        /// On success, this function returns the newly accepted client socket along with
+        /// a pair of streams that can be used to read & write to the connection.
+        ///
+        /// # Typical errors
+        /// - `invalid-state`:      Socket is not in the Listener state. (EINVAL)
+        /// - `would-block`:        No pending connections at the moment. (EWOULDBLOCK, EAGAIN)
+        /// - `connection-aborted`: An incoming connection was pending, but was terminated by the client before this listener could accept it. (ECONNABORTED)
+        /// - `new-socket-limit`:   The new socket resource could not be created because of a system limit. (EMFILE, ENFILE)
+        ///
+        /// # References
+        /// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/accept.html>
+        /// - <https://man7.org/linux/man-pages/man2/accept.2.html>
+        /// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-accept>
+        /// - <https://man.freebsd.org/cgi/man.cgi?query=accept&sektion=2>
+        accept: func() -> result<tuple<tcp-socket, input-stream, output-stream>, error-code>;
 
-		/// 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 `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
-		/// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/getsockname.html>
-		/// - <https://man7.org/linux/man-pages/man2/getsockname.2.html>
-		/// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-getsockname>
-		/// - <https://man.freebsd.org/cgi/man.cgi?getsockname>
-		local-address: func() -> result<ip-socket-address, error-code>;
+        /// 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 `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
+        /// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/getsockname.html>
+        /// - <https://man7.org/linux/man-pages/man2/getsockname.2.html>
+        /// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-getsockname>
+        /// - <https://man.freebsd.org/cgi/man.cgi?getsockname>
+        local-address: func() -> result<ip-socket-address, error-code>;
 
-		/// Get the remote address.
-		///
-		/// # Typical errors
-		/// - `invalid-state`: The socket is not connected to a remote address. (ENOTCONN)
-		///
-		/// # References
-		/// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpeername.html>
-		/// - <https://man7.org/linux/man-pages/man2/getpeername.2.html>
-		/// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-getpeername>
-		/// - <https://man.freebsd.org/cgi/man.cgi?query=getpeername&sektion=2&n=1>
-		remote-address: func() -> result<ip-socket-address, error-code>;
+        /// Get the remote address.
+        ///
+        /// # Typical errors
+        /// - `invalid-state`: The socket is not connected to a remote address. (ENOTCONN)
+        ///
+        /// # References
+        /// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpeername.html>
+        /// - <https://man7.org/linux/man-pages/man2/getpeername.2.html>
+        /// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-getpeername>
+        /// - <https://man.freebsd.org/cgi/man.cgi?query=getpeername&sektion=2&n=1>
+        remote-address: func() -> result<ip-socket-address, error-code>;
 
-		/// Whether this is a IPv4 or IPv6 socket.
-		///
-		/// Equivalent to the SO_DOMAIN socket option.
-		address-family: func() -> ip-address-family;
+        /// Whether this is a IPv4 or IPv6 socket.
+        ///
+        /// Equivalent to the SO_DOMAIN socket option.
+        address-family: func() -> ip-address-family;
 
-		/// Whether IPv4 compatibility (dual-stack) mode is disabled or not.
-		///
-		/// Equivalent to the IPV6_V6ONLY socket option.
-		///
-		/// # Typical errors
-		/// - `invalid-state`:        (set) The socket is already bound.
-		/// - `not-supported`:        (get/set) `this` socket is an IPv4 socket.
-		/// - `not-supported`:        (set) Host does not support dual-stack sockets. (Implementations are not required to.)
-		ipv6-only: func() -> result<bool, error-code>;
-		set-ipv6-only: func(value: bool) -> result<_, error-code>;
+        /// Whether IPv4 compatibility (dual-stack) mode is disabled or not.
+        ///
+        /// Equivalent to the IPV6_V6ONLY socket option.
+        ///
+        /// # Typical errors
+        /// - `invalid-state`:        (set) The socket is already bound.
+        /// - `not-supported`:        (get/set) `this` socket is an IPv4 socket.
+        /// - `not-supported`:        (set) Host does not support dual-stack sockets. (Implementations are not required to.)
+        ipv6-only: func() -> result<bool, error-code>;
+        set-ipv6-only: func(value: bool) -> result<_, error-code>;
 
-		/// Hints the desired listen queue size. Implementations are free to ignore this.
-		///
-		/// # Typical errors
-		/// - `not-supported`:        (set) The platform does not support changing the backlog size after the initial listen.
-		/// - `invalid-state`:        (set) The socket is already in the Connection state.
-		set-listen-backlog-size: func(value: u64) -> result<_, error-code>;
+        /// Hints the desired listen queue size. Implementations are free to ignore this.
+        ///
+        /// # Typical errors
+        /// - `not-supported`:        (set) The platform does not support changing the backlog size after the initial listen.
+        /// - `invalid-state`:        (set) The socket is already in the Connection state.
+        set-listen-backlog-size: func(value: u64) -> result<_, error-code>;
 
-		/// Equivalent to the SO_KEEPALIVE socket option.
-		keep-alive: func() -> result<bool, error-code>;
-		set-keep-alive: func(value: bool) -> result<_, error-code>;
+        /// Equivalent to the SO_KEEPALIVE socket option.
+        keep-alive: func() -> result<bool, error-code>;
+        set-keep-alive: func(value: bool) -> result<_, error-code>;
 
-		/// Equivalent to the TCP_NODELAY socket option.
-		///
-		/// The default value is `false`.
-		no-delay: func() -> result<bool, error-code>;
-		set-no-delay: func(value: bool) -> result<_, error-code>;
+        /// Equivalent to the TCP_NODELAY socket option.
+        ///
+        /// The default value is `false`.
+        no-delay: func() -> result<bool, error-code>;
+        set-no-delay: func(value: bool) -> result<_, error-code>;
 
-		/// Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options.
-		///
-		/// # Typical errors
-		/// - `invalid-argument`:     (set) The TTL value must be 1 or higher.
-		/// - `invalid-state`:        (set) The socket is already in the Connection state.
-		/// - `invalid-state`:        (set) The socket is already in the Listener state.
-		unicast-hop-limit: func() -> result<u8, error-code>;
-		set-unicast-hop-limit: func(value: u8) -> result<_, error-code>;
+        /// Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options.
+        ///
+        /// # Typical errors
+        /// - `invalid-argument`:     (set) The TTL value must be 1 or higher.
+        /// - `invalid-state`:        (set) The socket is already in the Connection state.
+        /// - `invalid-state`:        (set) The socket is already in the Listener state.
+        unicast-hop-limit: func() -> result<u8, error-code>;
+        set-unicast-hop-limit: func(value: u8) -> result<_, error-code>;
 
-		/// The kernel buffer space reserved for sends/receives on this socket.
-		///
-		/// Note #1: an implementation may choose to cap or round the buffer size when setting the value.
-		/// 	In other words, after setting a value, reading the same setting back may return a different value.
-		///
-		/// Note #2: there is not necessarily a direct relationship between the kernel buffer size and the bytes of
-		/// 	actual data to be sent/received by the application, because the kernel might also use the buffer space
-		/// 	for internal metadata structures.
-		///
-		/// Equivalent to the SO_RCVBUF and SO_SNDBUF socket options.
-		///
-		/// # Typical errors
-		/// - `invalid-state`:        (set) The socket is already in the Connection state.
-		/// - `invalid-state`:        (set) The socket is already in the Listener state.
-		receive-buffer-size: func() -> result<u64, error-code>;
-		set-receive-buffer-size: func(value: u64) -> result<_, error-code>;
-		send-buffer-size: func() -> result<u64, error-code>;
-		set-send-buffer-size: func(value: u64) -> result<_, error-code>;
+        /// The kernel buffer space reserved for sends/receives on this socket.
+        ///
+        /// Note #1: an implementation may choose to cap or round the buffer size when setting the value.
+        ///     In other words, after setting a value, reading the same setting back may return a different value.
+        ///
+        /// Note #2: there is not necessarily a direct relationship between the kernel buffer size and the bytes of
+        ///     actual data to be sent/received by the application, because the kernel might also use the buffer space
+        ///     for internal metadata structures.
+        ///
+        /// Equivalent to the SO_RCVBUF and SO_SNDBUF socket options.
+        ///
+        /// # Typical errors
+        /// - `invalid-state`:        (set) The socket is already in the Connection state.
+        /// - `invalid-state`:        (set) The socket is already in the Listener state.
+        receive-buffer-size: func() -> result<u64, error-code>;
+        set-receive-buffer-size: func(value: u64) -> result<_, error-code>;
+        send-buffer-size: func() -> result<u64, error-code>;
+        set-send-buffer-size: func(value: u64) -> result<_, error-code>;
 
-		/// Create a `pollable` which will resolve once the socket is ready for I/O.
-		///
-		/// Note: this function is here for WASI Preview2 only.
-		/// It's planned to be removed when `future` is natively supported in Preview3.
-		subscribe: func() -> pollable;
+        /// Create a `pollable` which will resolve once the socket is ready for I/O.
+        ///
+        /// Note: this function is here for WASI Preview2 only.
+        /// It's planned to be removed when `future` is natively supported in Preview3.
+        subscribe: func() -> pollable;
 
-		/// Initiate a graceful shutdown.
-		///
-		/// - receive: the socket is not expecting to receive any more data from the peer. All subsequent read
-		///   operations on the `input-stream` associated with this socket will return an End Of Stream indication.
-		///   Any data still in the receive queue at time of calling `shutdown` will be discarded.
-		/// - send: the socket is not expecting to send any more data to the peer. All subsequent write
-		///   operations on the `output-stream` associated with this socket will return an error.
-		/// - both: same effect as receive & send combined.
-		///
-		/// The shutdown function does not close (drop) the socket.
-		///
-		/// # Typical errors
-		/// - `invalid-state`: The socket is not in the Connection state. (ENOTCONN)
-		///
-		/// # References
-		/// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/shutdown.html>
-		/// - <https://man7.org/linux/man-pages/man2/shutdown.2.html>
-		/// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-shutdown>
-		/// - <https://man.freebsd.org/cgi/man.cgi?query=shutdown&sektion=2>
-		shutdown: func(shutdown-type: shutdown-type) -> result<_, error-code>;
-	}
+        /// Initiate a graceful shutdown.
+        ///
+        /// - receive: the socket is not expecting to receive any more data from the peer. All subsequent read
+        ///   operations on the `input-stream` associated with this socket will return an End Of Stream indication.
+        ///   Any data still in the receive queue at time of calling `shutdown` will be discarded.
+        /// - send: the socket is not expecting to send any more data to the peer. All subsequent write
+        ///   operations on the `output-stream` associated with this socket will return an error.
+        /// - both: same effect as receive & send combined.
+        ///
+        /// The shutdown function does not close (drop) the socket.
+        ///
+        /// # Typical errors
+        /// - `invalid-state`: The socket is not in the Connection state. (ENOTCONN)
+        ///
+        /// # References
+        /// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/shutdown.html>
+        /// - <https://man7.org/linux/man-pages/man2/shutdown.2.html>
+        /// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-shutdown>
+        /// - <https://man.freebsd.org/cgi/man.cgi?query=shutdown&sektion=2>
+        shutdown: func(shutdown-type: shutdown-type) -> result<_, error-code>;
+    }
 }
diff --git a/crates/wasi/wit/deps/sockets/udp-create-socket.wit b/crates/wasi/wit/deps/sockets/udp-create-socket.wit
index e026359fd..4e61d30fa 100644
--- a/crates/wasi/wit/deps/sockets/udp-create-socket.wit
+++ b/crates/wasi/wit/deps/sockets/udp-create-socket.wit
@@ -1,26 +1,26 @@
 
 interface udp-create-socket {
-	use network.{network, error-code, ip-address-family};
-	use udp.{udp-socket};
+    use network.{network, error-code, ip-address-family};
+    use udp.{udp-socket};
 
-	/// Create a new UDP socket.
-	///
-	/// Similar to `socket(AF_INET or AF_INET6, SOCK_DGRAM, IPPROTO_UDP)` in POSIX.
-	///
-	/// This function does not require a network capability handle. This is considered to be safe because
-	/// at time of creation, the socket is not bound to any `network` yet. Up to the moment `bind`/`connect` is called,
-	/// the socket is effectively an in-memory configuration object, unable to communicate with the outside world.
-	///
-	/// All sockets are non-blocking. Use the wasi-poll interface to block on asynchronous operations.
-	///
-	/// # Typical errors
-	/// - `not-supported`:     The specified `address-family` is not supported. (EAFNOSUPPORT)
-	/// - `new-socket-limit`:  The new socket resource could not be created because of a system limit. (EMFILE, ENFILE)
-	///
-	/// # References:
-	/// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/socket.html>
-	/// - <https://man7.org/linux/man-pages/man2/socket.2.html>
-	/// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsasocketw>
-	/// - <https://man.freebsd.org/cgi/man.cgi?query=socket&sektion=2>
-	create-udp-socket: func(address-family: ip-address-family) -> result<udp-socket, error-code>;
+    /// Create a new UDP socket.
+    ///
+    /// Similar to `socket(AF_INET or AF_INET6, SOCK_DGRAM, IPPROTO_UDP)` in POSIX.
+    ///
+    /// This function does not require a network capability handle. This is considered to be safe because
+    /// at time of creation, the socket is not bound to any `network` yet. Up to the moment `bind`/`connect` is called,
+    /// the socket is effectively an in-memory configuration object, unable to communicate with the outside world.
+    ///
+    /// All sockets are non-blocking. Use the wasi-poll interface to block on asynchronous operations.
+    ///
+    /// # Typical errors
+    /// - `not-supported`:     The specified `address-family` is not supported. (EAFNOSUPPORT)
+    /// - `new-socket-limit`:  The new socket resource could not be created because of a system limit. (EMFILE, ENFILE)
+    ///
+    /// # References:
+    /// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/socket.html>
+    /// - <https://man7.org/linux/man-pages/man2/socket.2.html>
+    /// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsasocketw>
+    /// - <https://man.freebsd.org/cgi/man.cgi?query=socket&sektion=2>
+    create-udp-socket: func(address-family: ip-address-family) -> result<udp-socket, error-code>;
 }
diff --git a/crates/wasi/wit/deps/sockets/udp.wit b/crates/wasi/wit/deps/sockets/udp.wit
index 91a8c6c4a..a86226f01 100644
--- a/crates/wasi/wit/deps/sockets/udp.wit
+++ b/crates/wasi/wit/deps/sockets/udp.wit
@@ -1,213 +1,213 @@
 
 interface udp {
-	use wasi:io/poll@0.2.0-rc-2023-10-18.{pollable};
-	use network.{network, error-code, ip-socket-address, ip-address-family};
-
-
-	record datagram {
-		data: list<u8>, // Theoretical max size: ~64 KiB. In practice, typically less than 1500 bytes.
-		remote-address: ip-socket-address,
-
-		/// Possible future additions:
-		/// local-address: ip-socket-address, // IP_PKTINFO / IP_RECVDSTADDR / IPV6_PKTINFO
-		/// local-interface: u32, // IP_PKTINFO / IP_RECVIF
-		/// ttl: u8, // IP_RECVTTL
-		/// dscp: u6, // IP_RECVTOS
-		/// ecn: u2, // IP_RECVTOS
-	}
-
-
-
-	/// A UDP socket handle.
-	resource udp-socket {
-		/// Bind the socket to a specific network on 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.
-		///
-		/// When a socket is not explicitly bound, the first invocation to connect will implicitly bind the socket.
-		///
-		/// Unlike in POSIX, this function is async. This enables interactive WASI hosts to inject permission prompts.
-		///
-		/// # Typical `start` errors
-		/// - `invalid-argument`:          The `local-address` has the wrong address family. (EAFNOSUPPORT, EFAULT on Windows)
-		/// - `invalid-state`:             The socket is already bound. (EINVAL)
-		///
-		/// # Typical `finish` errors
-		/// - `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 the `network` can bind to. (EADDRNOTAVAIL)
-		/// - `not-in-progress`:           A `bind` operation is not in progress.
-		/// - `would-block`:               Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN)
-		///
-		/// # References
-		/// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/bind.html>
-		/// - <https://man7.org/linux/man-pages/man2/bind.2.html>
-		/// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-bind>
-		/// - <https://man.freebsd.org/cgi/man.cgi?query=bind&sektion=2&format=html>
-		start-bind: func(network: borrow<network>, local-address: ip-socket-address) -> result<_, error-code>;
-		finish-bind: func() -> result<_, error-code>;
-
-		/// Set the destination address.
-		///
-		/// The local-address is updated based on the best network path to `remote-address`.
-		///
-		/// When a destination address is set:
-		/// - all receive operations will only return datagrams sent from the provided `remote-address`.
-		/// - the `send` function can only be used to send to this destination.
-		///
-		/// Note that this function does not generate any network traffic and the peer is not aware of this "connection".
-		///
-		/// Unlike in POSIX, this function is async. This enables interactive WASI hosts to inject permission prompts.
-		///
-		/// # Typical `start` errors
-		/// - `invalid-argument`:          The `remote-address` has the wrong address family. (EAFNOSUPPORT)
-		/// - `invalid-argument`:          `remote-address` is a non-IPv4-mapped IPv6 address, but the socket was bound to a specific IPv4-mapped IPv6 address. (or vice versa)
-		/// - `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 already bound to a different network. The `network` passed to `connect` must be identical to the one passed to `bind`.
-		///
-		/// # Typical `finish` errors
-		/// - `address-in-use`:            Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE, EADDRNOTAVAIL on Linux, EAGAIN on BSD)
-		/// - `not-in-progress`:           A `connect` operation is not in progress.
-		/// - `would-block`:               Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN)
-		///
-		/// # References
-		/// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/connect.html>
-		/// - <https://man7.org/linux/man-pages/man2/connect.2.html>
-		/// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-connect>
-		/// - <https://man.freebsd.org/cgi/man.cgi?connect>
-		start-connect: func(network: borrow<network>, remote-address: ip-socket-address) -> result<_, error-code>;
-		finish-connect: func() -> result<_, error-code>;
-
-		/// Receive messages on the socket.
-		///
-		/// This function attempts to receive up to `max-results` datagrams on the socket without blocking.
-		/// The returned list may contain fewer elements than requested, but never more.
-		/// If `max-results` is 0, this function returns successfully with an empty list.
-		///
-		/// # Typical errors
-		/// - `invalid-state`:      The socket is not bound to any local address. (EINVAL)
-		/// - `remote-unreachable`: The remote address is not reachable. (ECONNREFUSED, ECONNRESET, ENETRESET on Windows, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN)
-		/// - `would-block`:        There is no pending data available to be read at the moment. (EWOULDBLOCK, EAGAIN)
-		///
-		/// # References
-		/// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/recvfrom.html>
-		/// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/recvmsg.html>
-		/// - <https://man7.org/linux/man-pages/man2/recv.2.html>
-		/// - <https://man7.org/linux/man-pages/man2/recvmmsg.2.html>
-		/// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-recv>
-		/// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-recvfrom>
-		/// - <https://learn.microsoft.com/en-us/previous-versions/windows/desktop/legacy/ms741687(v=vs.85)>
-		/// - <https://man.freebsd.org/cgi/man.cgi?query=recv&sektion=2>
-		receive: func(max-results: u64) -> result<list<datagram>, error-code>;
-
-		/// Send messages on the socket.
-		///
-		/// This function attempts to send all provided `datagrams` on the socket without blocking and
-		/// returns how many messages were actually sent (or queued for sending).
-		///
-		/// This function semantically behaves the same as iterating the `datagrams` list and sequentially
-		/// sending each individual datagram until either the end of the list has been reached or the first error occurred.
-		/// If at least one datagram has been sent successfully, this function never returns an error.
-		///
-		/// If the input list is empty, the function returns `ok(0)`.
-		///
-		/// The remote address option is required. To send a message to the "connected" peer,
-		/// call `remote-address` to get their address.
-		///
-		/// # Typical errors
-		/// - `invalid-argument`:        The `remote-address` has the wrong address family. (EAFNOSUPPORT)
-		/// - `invalid-argument`:        `remote-address` is a non-IPv4-mapped IPv6 address, but the socket was bound to a specific IPv4-mapped IPv6 address. (or vice versa)
-		/// - `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 the `datagram.remote-address` does not match the address passed to `connect`. (EISCONN)
-		/// - `invalid-state`:           The socket is not bound to any local address. Unlike POSIX, this function does not perform an implicit bind.
-		/// - `remote-unreachable`:      The remote address is not reachable. (ECONNREFUSED, ECONNRESET, ENETRESET on Windows, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN)
-		/// - `datagram-too-large`:      The datagram is too large. (EMSGSIZE)
-		/// - `would-block`:             The send buffer is currently full. (EWOULDBLOCK, EAGAIN)
-		///
-		/// # References
-		/// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/sendto.html>
-		/// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/sendmsg.html>
-		/// - <https://man7.org/linux/man-pages/man2/send.2.html>
-		/// - <https://man7.org/linux/man-pages/man2/sendmmsg.2.html>
-		/// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-send>
-		/// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-sendto>
-		/// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsasendmsg>
-		/// - <https://man.freebsd.org/cgi/man.cgi?query=send&sektion=2>
-		send: func(datagrams: list<datagram>) -> result<u64, 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 `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
-		/// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/getsockname.html>
-		/// - <https://man7.org/linux/man-pages/man2/getsockname.2.html>
-		/// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-getsockname>
-		/// - <https://man.freebsd.org/cgi/man.cgi?getsockname>
-		local-address: func() -> result<ip-socket-address, error-code>;
-
-		/// Get the address set with `connect`.
-		///
-		/// # Typical errors
-		/// - `invalid-state`: The socket is not connected to a remote address. (ENOTCONN)
-		///
-		/// # References
-		/// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpeername.html>
-		/// - <https://man7.org/linux/man-pages/man2/getpeername.2.html>
-		/// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-getpeername>
-		/// - <https://man.freebsd.org/cgi/man.cgi?query=getpeername&sektion=2&n=1>
-		remote-address: func() -> result<ip-socket-address, error-code>;
-
-		/// Whether this is a IPv4 or IPv6 socket.
-		///
-		/// Equivalent to the SO_DOMAIN socket option.
-		address-family: func() -> ip-address-family;
-
-		/// Whether IPv4 compatibility (dual-stack) mode is disabled or not.
-		///
-		/// Equivalent to the IPV6_V6ONLY socket option.
-		///
-		/// # Typical errors
-		/// - `not-supported`:        (get/set) `this` socket is an IPv4 socket.
-		/// - `invalid-state`:        (set) The socket is already bound.
-		/// - `not-supported`:        (set) Host does not support dual-stack sockets. (Implementations are not required to.)
-		ipv6-only: func() -> result<bool, error-code>;
-		set-ipv6-only: func(value: bool) -> result<_, error-code>;
-
-		/// Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options.
-		unicast-hop-limit: func() -> result<u8, error-code>;
-		set-unicast-hop-limit: func(value: u8) -> result<_, error-code>;
-
-		/// The kernel buffer space reserved for sends/receives on this socket.
-		///
-		/// Note #1: an implementation may choose to cap or round the buffer size when setting the value.
-		/// 	In other words, after setting a value, reading the same setting back may return a different value.
-		///
-		/// Note #2: there is not necessarily a direct relationship between the kernel buffer size and the bytes of
-		/// 	actual data to be sent/received by the application, because the kernel might also use the buffer space
-		/// 	for internal metadata structures.
-		///
-		/// Equivalent to the SO_RCVBUF and SO_SNDBUF socket options.
-		receive-buffer-size: func() -> result<u64, error-code>;
-		set-receive-buffer-size: func(value: u64) -> result<_, error-code>;
-		send-buffer-size: func() -> result<u64, error-code>;
-		set-send-buffer-size: func(value: u64) -> result<_, error-code>;
-
-		/// Create a `pollable` which will resolve once the socket is ready for I/O.
-		///
-		/// Note: this function is here for WASI Preview2 only.
-		/// It's planned to be removed when `future` is natively supported in Preview3.
-		subscribe: func() -> pollable;
-	}
+    use wasi:io/poll@0.2.0-rc-2023-10-18.{pollable};
+    use network.{network, error-code, ip-socket-address, ip-address-family};
+
+
+    record datagram {
+        data: list<u8>, // Theoretical max size: ~64 KiB. In practice, typically less than 1500 bytes.
+        remote-address: ip-socket-address,
+
+        /// Possible future additions:
+        /// local-address: ip-socket-address, // IP_PKTINFO / IP_RECVDSTADDR / IPV6_PKTINFO
+        /// local-interface: u32, // IP_PKTINFO / IP_RECVIF
+        /// ttl: u8, // IP_RECVTTL
+        /// dscp: u6, // IP_RECVTOS
+        /// ecn: u2, // IP_RECVTOS
+    }
+
+
+
+    /// A UDP socket handle.
+    resource udp-socket {
+        /// Bind the socket to a specific network on 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.
+        ///
+        /// When a socket is not explicitly bound, the first invocation to connect will implicitly bind the socket.
+        ///
+        /// Unlike in POSIX, this function is async. This enables interactive WASI hosts to inject permission prompts.
+        ///
+        /// # Typical `start` errors
+        /// - `invalid-argument`:          The `local-address` has the wrong address family. (EAFNOSUPPORT, EFAULT on Windows)
+        /// - `invalid-state`:             The socket is already bound. (EINVAL)
+        ///
+        /// # Typical `finish` errors
+        /// - `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 the `network` can bind to. (EADDRNOTAVAIL)
+        /// - `not-in-progress`:           A `bind` operation is not in progress.
+        /// - `would-block`:               Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN)
+        ///
+        /// # References
+        /// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/bind.html>
+        /// - <https://man7.org/linux/man-pages/man2/bind.2.html>
+        /// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-bind>
+        /// - <https://man.freebsd.org/cgi/man.cgi?query=bind&sektion=2&format=html>
+        start-bind: func(network: borrow<network>, local-address: ip-socket-address) -> result<_, error-code>;
+        finish-bind: func() -> result<_, error-code>;
+
+        /// Set the destination address.
+        ///
+        /// The local-address is updated based on the best network path to `remote-address`.
+        ///
+        /// When a destination address is set:
+        /// - all receive operations will only return datagrams sent from the provided `remote-address`.
+        /// - the `send` function can only be used to send to this destination.
+        ///
+        /// Note that this function does not generate any network traffic and the peer is not aware of this "connection".
+        ///
+        /// Unlike in POSIX, this function is async. This enables interactive WASI hosts to inject permission prompts.
+        ///
+        /// # Typical `start` errors
+        /// - `invalid-argument`:          The `remote-address` has the wrong address family. (EAFNOSUPPORT)
+        /// - `invalid-argument`:          `remote-address` is a non-IPv4-mapped IPv6 address, but the socket was bound to a specific IPv4-mapped IPv6 address. (or vice versa)
+        /// - `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 already bound to a different network. The `network` passed to `connect` must be identical to the one passed to `bind`.
+        ///
+        /// # Typical `finish` errors
+        /// - `address-in-use`:            Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE, EADDRNOTAVAIL on Linux, EAGAIN on BSD)
+        /// - `not-in-progress`:           A `connect` operation is not in progress.
+        /// - `would-block`:               Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN)
+        ///
+        /// # References
+        /// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/connect.html>
+        /// - <https://man7.org/linux/man-pages/man2/connect.2.html>
+        /// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-connect>
+        /// - <https://man.freebsd.org/cgi/man.cgi?connect>
+        start-connect: func(network: borrow<network>, remote-address: ip-socket-address) -> result<_, error-code>;
+        finish-connect: func() -> result<_, error-code>;
+
+        /// Receive messages on the socket.
+        ///
+        /// This function attempts to receive up to `max-results` datagrams on the socket without blocking.
+        /// The returned list may contain fewer elements than requested, but never more.
+        /// If `max-results` is 0, this function returns successfully with an empty list.
+        ///
+        /// # Typical errors
+        /// - `invalid-state`:      The socket is not bound to any local address. (EINVAL)
+        /// - `remote-unreachable`: The remote address is not reachable. (ECONNREFUSED, ECONNRESET, ENETRESET on Windows, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN)
+        /// - `would-block`:        There is no pending data available to be read at the moment. (EWOULDBLOCK, EAGAIN)
+        ///
+        /// # References
+        /// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/recvfrom.html>
+        /// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/recvmsg.html>
+        /// - <https://man7.org/linux/man-pages/man2/recv.2.html>
+        /// - <https://man7.org/linux/man-pages/man2/recvmmsg.2.html>
+        /// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-recv>
+        /// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-recvfrom>
+        /// - <https://learn.microsoft.com/en-us/previous-versions/windows/desktop/legacy/ms741687(v=vs.85)>
+        /// - <https://man.freebsd.org/cgi/man.cgi?query=recv&sektion=2>
+        receive: func(max-results: u64) -> result<list<datagram>, error-code>;
+
+        /// Send messages on the socket.
+        ///
+        /// This function attempts to send all provided `datagrams` on the socket without blocking and
+        /// returns how many messages were actually sent (or queued for sending).
+        ///
+        /// This function semantically behaves the same as iterating the `datagrams` list and sequentially
+        /// sending each individual datagram until either the end of the list has been reached or the first error occurred.
+        /// If at least one datagram has been sent successfully, this function never returns an error.
+        ///
+        /// If the input list is empty, the function returns `ok(0)`.
+        ///
+        /// The remote address option is required. To send a message to the "connected" peer,
+        /// call `remote-address` to get their address.
+        ///
+        /// # Typical errors
+        /// - `invalid-argument`:        The `remote-address` has the wrong address family. (EAFNOSUPPORT)
+        /// - `invalid-argument`:        `remote-address` is a non-IPv4-mapped IPv6 address, but the socket was bound to a specific IPv4-mapped IPv6 address. (or vice versa)
+        /// - `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 the `datagram.remote-address` does not match the address passed to `connect`. (EISCONN)
+        /// - `invalid-state`:           The socket is not bound to any local address. Unlike POSIX, this function does not perform an implicit bind.
+        /// - `remote-unreachable`:      The remote address is not reachable. (ECONNREFUSED, ECONNRESET, ENETRESET on Windows, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN)
+        /// - `datagram-too-large`:      The datagram is too large. (EMSGSIZE)
+        /// - `would-block`:             The send buffer is currently full. (EWOULDBLOCK, EAGAIN)
+        ///
+        /// # References
+        /// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/sendto.html>
+        /// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/sendmsg.html>
+        /// - <https://man7.org/linux/man-pages/man2/send.2.html>
+        /// - <https://man7.org/linux/man-pages/man2/sendmmsg.2.html>
+        /// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-send>
+        /// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-sendto>
+        /// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsasendmsg>
+        /// - <https://man.freebsd.org/cgi/man.cgi?query=send&sektion=2>
+        send: func(datagrams: list<datagram>) -> result<u64, 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 `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
+        /// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/getsockname.html>
+        /// - <https://man7.org/linux/man-pages/man2/getsockname.2.html>
+        /// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-getsockname>
+        /// - <https://man.freebsd.org/cgi/man.cgi?getsockname>
+        local-address: func() -> result<ip-socket-address, error-code>;
+
+        /// Get the address set with `connect`.
+        ///
+        /// # Typical errors
+        /// - `invalid-state`: The socket is not connected to a remote address. (ENOTCONN)
+        ///
+        /// # References
+        /// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpeername.html>
+        /// - <https://man7.org/linux/man-pages/man2/getpeername.2.html>
+        /// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-getpeername>
+        /// - <https://man.freebsd.org/cgi/man.cgi?query=getpeername&sektion=2&n=1>
+        remote-address: func() -> result<ip-socket-address, error-code>;
+
+        /// Whether this is a IPv4 or IPv6 socket.
+        ///
+        /// Equivalent to the SO_DOMAIN socket option.
+        address-family: func() -> ip-address-family;
+
+        /// Whether IPv4 compatibility (dual-stack) mode is disabled or not.
+        ///
+        /// Equivalent to the IPV6_V6ONLY socket option.
+        ///
+        /// # Typical errors
+        /// - `not-supported`:        (get/set) `this` socket is an IPv4 socket.
+        /// - `invalid-state`:        (set) The socket is already bound.
+        /// - `not-supported`:        (set) Host does not support dual-stack sockets. (Implementations are not required to.)
+        ipv6-only: func() -> result<bool, error-code>;
+        set-ipv6-only: func(value: bool) -> result<_, error-code>;
+
+        /// Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options.
+        unicast-hop-limit: func() -> result<u8, error-code>;
+        set-unicast-hop-limit: func(value: u8) -> result<_, error-code>;
+
+        /// The kernel buffer space reserved for sends/receives on this socket.
+        ///
+        /// Note #1: an implementation may choose to cap or round the buffer size when setting the value.
+        ///     In other words, after setting a value, reading the same setting back may return a different value.
+        ///
+        /// Note #2: there is not necessarily a direct relationship between the kernel buffer size and the bytes of
+        ///     actual data to be sent/received by the application, because the kernel might also use the buffer space
+        ///     for internal metadata structures.
+        ///
+        /// Equivalent to the SO_RCVBUF and SO_SNDBUF socket options.
+        receive-buffer-size: func() -> result<u64, error-code>;
+        set-receive-buffer-size: func(value: u64) -> result<_, error-code>;
+        send-buffer-size: func() -> result<u64, error-code>;
+        set-send-buffer-size: func(value: u64) -> result<_, error-code>;
+
+        /// Create a `pollable` which will resolve once the socket is ready for I/O.
+        ///
+        /// Note: this function is here for WASI Preview2 only.
+        /// It's planned to be removed when `future` is natively supported in Preview3.
+        subscribe: func() -> pollable;
+    }
 }
diff --git a/crates/wasmtime/Cargo.toml b/crates/wasmtime/Cargo.toml
index d735b74b6..e0e3ed458 100644
--- a/crates/wasmtime/Cargo.toml
+++ b/crates/wasmtime/Cargo.toml
@@ -29,7 +29,7 @@ wasmtime-component-macro = { workspace = true, optional = true }
 wasmtime-component-util = { workspace = true, optional = true }
 target-lexicon = { workspace = true }
 wasmparser = { workspace = true }
-wasm-encoder = { workspace = true }
+wasm-encoder = { workspace = true, optional = true }
 anyhow = { workspace = true }
 libc = "0.2"
 cfg-if = { workspace = true }
@@ -47,7 +47,7 @@ object = { workspace = true }
 async-trait = { workspace = true, optional = true }
 encoding_rs = { version = "0.8.31", optional = true }
 bumpalo = "3.11.0"
-fxprof-processed-profile = "0.6.0"
+fxprof-processed-profile = { version = "0.6.0", optional = true }
 
 [target.'cfg(target_os = "windows")'.dependencies.windows-sys]
 workspace = true
@@ -63,16 +63,27 @@ tempfile = "3.0"
 wasmtime-wasi = { path = "../wasi", default-features = true }
 wasi-cap-std-sync = { path = "../wasi-common/cap-std-sync" }
 
+# =============================================================================
+#
+# Features for the Wasmtime crate.
+#
+# This section contains the compile-time features of the `wasmtime` crate. These
+# features can be used to add more functionality and APIs exposed from the
+# crate's API. Most of them are enabled by default. Disabling features can be
+# done to cut down on binary size by disabling features that are not required
+# for a particular embedding.
 [features]
 default = [
   'async',
   'cache',
   'wat',
-  'jitdump',
+  'profiling',
   'parallel-compilation',
   'cranelift',
   'pooling-allocator',
-  'vtune',
+  'demangle',
+  'addr2line',
+  'coredump',
 ]
 
 # An on-by-default feature enabling runtime compilation of WebAssembly modules
@@ -89,11 +100,8 @@ winch = ["dep:wasmtime-winch"]
 # Enables support for incremental compilation cache to be enabled in `Config`.
 incremental-cache = ["wasmtime-cranelift?/incremental-cache"]
 
-# Enables support for the `perf` jitdump profiler
-jitdump = ["wasmtime-jit/jitdump"]
-
-# Enables support for the `VTune` profiler
-vtune = ["wasmtime-jit/vtune"]
+# Enables support for profiling guest modules.
+profiling = ["wasmtime-jit/profiling", "dep:fxprof-processed-profile"]
 
 # Enables parallel compilation of WebAssembly code.
 parallel-compilation = ["dep:rayon"]
@@ -135,3 +143,14 @@ component-model = [
 ]
 
 wmemcheck = ["wasmtime-runtime/wmemcheck", "wasmtime-cranelift?/wmemcheck"]
+
+# Enables support for demangling WebAssembly function names at runtime in
+# errors such as backtraces.
+demangle = ["wasmtime-jit/demangle"]
+
+# Enable support for generating core dumps on traps.
+coredump = ["dep:wasm-encoder"]
+
+# Support address-to-file/line information in traps when wasm files have DWARF
+# debugging information.
+addr2line = ["wasmtime-jit/addr2line"]
diff --git a/crates/wasmtime/src/config.rs b/crates/wasmtime/src/config.rs
index 5b90c28a1..31bc85e41 100644
--- a/crates/wasmtime/src/config.rs
+++ b/crates/wasmtime/src/config.rs
@@ -1516,6 +1516,8 @@ impl Config {
     /// the anyhow::Error when a trap is raised.
     ///
     /// This option is disabled by default.
+    #[cfg(feature = "coredump")]
+    #[cfg_attr(nightlydoc, doc(cfg(feature = "coredump")))]
     pub fn coredump_on_trap(&mut self, enable: bool) -> &mut Self {
         self.coredump_on_trap = enable;
         self
diff --git a/crates/wasmtime/src/lib.rs b/crates/wasmtime/src/lib.rs
index f59ea2bcf..466ed2614 100644
--- a/crates/wasmtime/src/lib.rs
+++ b/crates/wasmtime/src/lib.rs
@@ -288,6 +288,23 @@
 //!   run-time via [`Config::memory_init_cow`] (which is also enabled by
 //!   default).
 //!
+//! * `demangle` - Enabled by default, this will affect how backtraces are
+//!   printed and whether symbol names from WebAssembly are attempted to be
+//!   demangled. Rust and C++ demanglings are currently supported.
+//!
+//! * `coredump` - Enabled by default, this will provide support for generating
+//!   a core dump when a trap happens. This can be configured via
+//!   [`Config::coredump_on_trap`].
+//!
+//! * `addr2line` - Enabled by default, this feature configures whether traps
+//!   will attempt to parse DWARF debug information and convert WebAssembly
+//!   addresses to source filenames and line numbers.
+//!
+//! More crate features can be found in the [manifest] of Wasmtime itself for
+//! seeing what can be enabled and disabled.
+//!
+//! [manifest]: https://github.com/bytecodealliance/wasmtime/blob/main/crates/wasmtime/Cargo.toml
+//!
 //! ## Examples
 //!
 //! In addition to the examples below be sure to check out the [online embedding
@@ -395,7 +412,6 @@ mod compiler;
 
 mod code;
 mod config;
-mod coredump;
 mod engine;
 mod externals;
 mod instance;
@@ -403,6 +419,7 @@ mod limits;
 mod linker;
 mod memory;
 mod module;
+#[cfg(feature = "profiling")]
 mod profiling;
 mod r#ref;
 mod resources;
@@ -418,7 +435,6 @@ mod values;
 mod stack;
 
 pub use crate::config::*;
-pub use crate::coredump::*;
 pub use crate::engine::*;
 pub use crate::externals::*;
 pub use crate::func::*;
@@ -427,6 +443,7 @@ pub use crate::limits::*;
 pub use crate::linker::*;
 pub use crate::memory::*;
 pub use crate::module::Module;
+#[cfg(feature = "profiling")]
 pub use crate::profiling::GuestProfiler;
 pub use crate::r#ref::ExternRef;
 pub use crate::resources::*;
@@ -443,6 +460,11 @@ pub use crate::values::*;
 #[cfg(feature = "async")]
 pub use crate::stack::*;
 
+#[cfg(feature = "coredump")]
+mod coredump;
+#[cfg(feature = "coredump")]
+pub use crate::coredump::*;
+
 /// A convenience wrapper for `Result<T, anyhow::Error>`.
 ///
 /// This type can be used to interact with `wasmtimes`'s extensive use
diff --git a/crates/wasmtime/src/trap.rs b/crates/wasmtime/src/trap.rs
index 3f8b2b0f7..af85c9a04 100644
--- a/crates/wasmtime/src/trap.rs
+++ b/crates/wasmtime/src/trap.rs
@@ -1,3 +1,4 @@
+#[cfg(feature = "coredump")]
 use crate::coredump::WasmCoreDump;
 use crate::store::StoreOpaque;
 use crate::{AsContext, Module};
@@ -134,11 +135,14 @@ pub(crate) fn from_runtime_box(
         }
     }
 
+    let _ = &coredumpstack;
+    #[cfg(feature = "coredump")]
     if let Some(coredump) = coredumpstack {
         let bt = WasmBacktrace::from_captured(store, coredump.bt, pc);
         let cd = WasmCoreDump::new(store, bt);
         error = error.context(cd);
     }
+
     error
 }
 
@@ -474,6 +478,8 @@ impl FrameInfo {
         // custom section contents.
         let mut symbols = Vec::new();
 
+        let _ = &mut symbols;
+        #[cfg(feature = "addr2line")]
         if let Some(s) = &compiled_module.symbolize_context().ok().and_then(|c| c) {
             if let Some(offset) = instr.and_then(|i| i.file_offset()) {
                 let to_lookup = u64::from(offset) - s.code_section_offset();
diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md
index c9cb56991..73ff7f807 100644
--- a/docs/SUMMARY.md
+++ b/docs/SUMMARY.md
@@ -28,6 +28,7 @@
     - [Debugging](./examples-c-debugging.md)
     - [Using Multi-Value](./examples-c-multi-value.md)
   - [Checking Guests' Memory Accesses](./wmemcheck.md)
+  - [Building a minimal embedding](./examples-minimal.md)
 - [Using WebAssembly from your language](./lang.md)
   - [Rust](./lang-rust.md)
   - [C](./lang-c.md)
diff --git a/docs/examples-minimal.md b/docs/examples-minimal.md
new file mode 100644
index 000000000..3f9e9891f
--- /dev/null
+++ b/docs/examples-minimal.md
@@ -0,0 +1,240 @@
+# Building a Minimal Wasmtime embedding
+
+Wasmtime embeddings may wish to optimize for binary size and runtime footprint
+to fit on a small system. This documentation is intended to guide some features
+of Wasmtime and how to best produce a minimal build of Wasmtime.
+
+## Building a minimal CLI
+
+> *Note*: the exact numbers in this section were last updated on 2023-10-18 on a
+> macOS aarch64 host. They should provide a general ballpark estimate but should
+> be confirmed locally again before being totally relied upon.
+
+Many Wasmtime embeddings go through the `wasmtime` crate as opposed to the
+`wasmtime` CLI executable, but to start out let's take a look at minimizing the
+command line executable. By default the wasmtime command line executable is
+relatively large:
+
+```shell
+$ cargo build
+$ ls -l ./target/debug/wasmtime
+-rwxr-xr-x@ 1 root  root    140M Oct 18 08:33 target/debug/wasmtime
+```
+
+The easiest size optimization is to compile with optimizations. This will strip
+lots of dead code and additionally generate much less debug information by
+default
+
+```shell
+$ cargo build --release
+$ ls -l ./target/release/wasmtime
+-rwxr-xr-x@ 1 root  root     33M Oct 18 08:34 target/release/wasmtime
+```
+
+Much better, but still relatively large! The next thing that can be done is to
+disable the default features of the `wasmtime-cli` crate. This will remove all
+optional functionality from the crate and strip it down to the bare bones
+functionality.
+
+```shell
+$ cargo build --release --no-default-features
+$ ls -l ./target/release/wasmtime
+-rwxr-xr-x@ 1 root  root    6.7M Oct 18 08:37 target/release/wasmtime
+```
+
+Note that this executable is stripped to the bare minimum of functionality which
+notably means it does not have a compiler for WebAssembly files. This means that
+`wasmtime compile` is no longer supported meaning that `*.cwasm` files must be
+fed to `wasmtime run` to execute files. Additionally error messages will be
+worse in this mode as less contextual information is provided.
+
+The final Wasmtime-specific optimization you can apply is to disable logging
+statements. Wasmtime and its dependencies make use of the [`log`
+crate](https://docs.rs/log) and [`tracing` crate](https://docs.rs/tracing) for
+debugging and diagnosing. For a minimal build this isn't needed though so this
+can all be disabled through Cargo features to shave off a small amount of code.
+Note that for custom embeddings you'd need to replicate the `disable-logging`
+feature which sets the `max_level_off` feature for the `log` and `tracing`
+crate.
+
+```shell
+$ cargo build --release --no-default-features --features disable-logging
+$ ls -l ./target/release/wasmtime
+-rwxr-xr-x@ 1 root  root    6.7M Oct 18 08:37 target/release/wasmtime
+```
+
+At this point the next line of tricks to apply to minimize binary size are
+[general tricks-of-the-trade for Rust
+programs](https://github.com/johnthagen/min-sized-rust) and are no longer
+specific to Wasmtime. For example the first thing that can be done is to
+optimize for size rather than speed via rustc's `s` optimization level.
+This uses Cargo's [environment-variable based configuration][cargo-env-config]
+via the `CARGO_PROFILE_RELEASE_OPT_LEVEL=s` environment variable to configure
+this.
+
+[cargo-env-config]: https://doc.rust-lang.org/cargo/reference/config.html#profile
+
+```shell
+$ export CARGO_PROFILE_RELEASE_OPT_LEVEL=s
+$ cargo build --release --no-default-features --features disable-logging
+$ ls -l ./target/release/wasmtime
+-rwxr-xr-x@ 1 root  root    6.8M Oct 18 08:40 target/release/wasmtime
+```
+
+Note that the size has increased here slightly instead of going down. Optimizing
+for speed-vs-size can affect a number of heuristics in LLVM so it's best to test
+out locally what's best for your embedding. Further examples below continue to
+pass this flag since by the end it will produce a smaller binary than the
+default optimization level of "3" for release mode. You may wish to also try an
+optimization level of "2" and see which produces a smaller build for you.
+
+After optimizations levels the next compilation setting to configure is
+Rust's "panic=abort" mode where panics translate to process aborts rather than
+unwinding. This removes landing pads from code as well as unwind tables from the
+executable.
+
+```shell
+$ export CARGO_PROFILE_RELEASE_OPT_LEVEL=s
+$ export CARGO_PROFILE_RELEASE_PANIC=abort
+$ cargo build --release --no-default-features --features disable-logging
+$ ls -l ./target/release/wasmtime
+-rwxr-xr-x@ 1 root  root    5.0M Oct 18 08:40 target/release/wasmtime
+```
+
+Next, if the compile time hit is acceptable, LTO can be enabled to provide
+deeper opportunities for compiler optimizations to remove dead code and
+deduplicate. Do note that this will take a significantly longer amount of time
+to compile than previously. Here LTO is configured with
+`CARGO_PROFILE_RELEASE_LTO=true`.
+
+```shell
+$ export CARGO_PROFILE_RELEASE_OPT_LEVEL=s
+$ export CARGO_PROFILE_RELEASE_PANIC=abort
+$ export CARGO_PROFILE_RELEASE_LTO=true
+$ cargo build --release --no-default-features --features disable-logging
+$ ls -l ./target/release/wasmtime
+-rwxr-xr-x@ 1 root  root    3.3M Oct 18 08:42 target/release/wasmtime
+```
+
+Similar to LTO above rustc can be further instructed to place all crates into
+their own single object file instead of multiple by default. This again
+increases compile times. Here that's done with
+`CARGO_PROFILE_RELEASE_CODEGEN_UNITS=1`.
+
+```shell
+$ export CARGO_PROFILE_RELEASE_OPT_LEVEL=s
+$ export CARGO_PROFILE_RELEASE_PANIC=abort
+$ export CARGO_PROFILE_RELEASE_LTO=true
+$ export CARGO_PROFILE_RELEASE_CODEGEN_UNITS=1
+$ cargo build --release --no-default-features --features disable-logging
+$ ls -l ./target/release/wasmtime
+-rwxr-xr-x@ 1 root  root    3.3M Oct 18 08:43 target/release/wasmtime
+```
+
+Note that with LTO using a single codegen unit may only have marginal benefit.
+If not using LTO, however, a single codegen unit will likely provide benefit
+over the default 16 codegen units.
+
+One final flag before getting to nightly features is to strip debug information
+from the standard library. In `--release` mode Cargo by default doesn't generate
+debug information for local crates, but the Rust standard library may have debug
+information still included with it. This is configured via
+`CARGO_PROFILE_RELEASE_STRIP=debuginfo`
+
+```shell
+$ export CARGO_PROFILE_RELEASE_OPT_LEVEL=s
+$ export CARGO_PROFILE_RELEASE_PANIC=abort
+$ export CARGO_PROFILE_RELEASE_LTO=true
+$ export CARGO_PROFILE_RELEASE_CODEGEN_UNITS=1
+$ export CARGO_PROFILE_RELEASE_STRIP=debuginfo
+$ cargo build --release --no-default-features --features disable-logging
+$ ls -l ./target/release/wasmtime
+-rwxr-xr-x@ 1 root  root    2.4M Oct 18 08:44 target/release/wasmtime
+```
+
+Next, if your use case allows it, the Nightly Rust toolchain provides a number
+of other options to minimize the size of binaries. Note the usage of `+nightly` here
+to the `cargo` command to use a Nightly toolchain (assuming your local toolchain
+is installed with rustup). Also note that due to the nature of nightly the exact
+flags here may not work in the future. Please open an issue with Wasmtime if
+these commands don't work and we'll update the documentation.
+
+The first nightly feature we can leverage is to remove filename and line number
+information in panics with `-Zlocation-detail=none`
+
+```shell
+$ export CARGO_PROFILE_RELEASE_OPT_LEVEL=s
+$ export CARGO_PROFILE_RELEASE_PANIC=abort
+$ export CARGO_PROFILE_RELEASE_LTO=true
+$ export CARGO_PROFILE_RELEASE_CODEGEN_UNITS=1
+$ export CARGO_PROFILE_RELEASE_STRIP=debuginfo
+$ export RUSTFLAGS="-Zlocation-detail=none"
+$ cargo +nightly build --release --no-default-features --features disable-logging
+$ ls -l ./target/release/wasmtime
+-rwxr-xr-x@ 1 root  root    2.4M Oct 18 08:43 target/release/wasmtime
+```
+
+Further along the line of nightly features the next optimization will recompile
+the standard library without unwinding information, trimming out a bit more from
+the standard library. This uses the `-Zbuild-std` flag to Cargo. Note that this
+additionally requires `--target` as well which will need to be configured for
+your particular platform.
+
+```shell
+$ export CARGO_PROFILE_RELEASE_OPT_LEVEL=s
+$ export CARGO_PROFILE_RELEASE_PANIC=abort
+$ export CARGO_PROFILE_RELEASE_LTO=true
+$ export CARGO_PROFILE_RELEASE_CODEGEN_UNITS=1
+$ export CARGO_PROFILE_RELEASE_STRIP=debuginfo
+$ export RUSTFLAGS="-Zlocation-detail=none"
+$ cargo +nightly build --release --no-default-features --features disable-logging \
+    -Z build-std=std,panic_abort --target aarch64-apple-darwin
+$ ls -l ./target/aarch64-apple-darwin/release/wasmtime
+-rwxr-xr-x@ 1 root  root    2.3M Oct 18 09:39 target/aarch64-apple-darwin/release/wasmtime
+```
+
+Next the Rust standard library has some optional features in addition to
+Wasmtime, such as printing of backtraces. This may not be required in minimal
+environments so the features of the standard library can be disabled with the
+`-Zbuild-std-features=` flag which configures the set of enabled features to be
+empty.
+
+```shell
+$ export CARGO_PROFILE_RELEASE_OPT_LEVEL=s
+$ export CARGO_PROFILE_RELEASE_PANIC=abort
+$ export CARGO_PROFILE_RELEASE_LTO=true
+$ export CARGO_PROFILE_RELEASE_CODEGEN_UNITS=1
+$ export CARGO_PROFILE_RELEASE_STRIP=debuginfo
+$ export RUSTFLAGS="-Zlocation-detail=none"
+$ cargo +nightly build --release --no-default-features --features disable-logging \
+    -Z build-std=std,panic_abort --target aarch64-apple-darwin \
+    -Z build-std-features=
+$ ls -l ./target/aarch64-apple-darwin/release/wasmtime
+-rwxr-xr-x@ 1 root  root    2.1M Oct 18 09:39 target/aarch64-apple-darwin/release/wasmtime
+```
+
+## Minimizing further
+
+Above shows an example of taking the default `cargo build` result of 130M down
+to a 2.1M binary for the `wasmtime` executable. The remaining space in this
+binary is occupied by features which often aren't needed in all embeddings, for
+example:
+
+* Command-line argument parsing via the `clap` crate. Custom embeddings likely
+  won't use this at all and/or will have their own command line parsing
+  elsewhere. In the above 2.1M number this is about ~200k.
+
+* WASI implementations may not all be needed or may be slimmed down. For example
+  the above binary contains two implementations of `wasi_snapshot_preview1` at
+  this time and removing one of them shaves off around 300k.
+
+Most Wasmtime embeddings are unlikely to be the `wasmtime` CLI itself meaning
+that the above sources of size will be eliminated as well in a custom embedding
+of the `wasmtime` crate.
+
+If, however, after applying the above optimizations, flags, etc, results in a
+binary too large for your use case we'd be quite interested to hear about it!
+Please feel free to [open an
+issue](https://github.com/bytecodealliance/wasmtime/issues/new) and let us know.
+There's still remaining fruit to be picked to minimize Wasmtime's footprint
+further and user feedback is helpful to prioritize this work.
diff --git a/src/bin/wasmtime.rs b/src/bin/wasmtime.rs
index 46868b3ac..32fcd040b 100644
--- a/src/bin/wasmtime.rs
+++ b/src/bin/wasmtime.rs
@@ -5,11 +5,6 @@
 
 use anyhow::Result;
 use clap::Parser;
-#[cfg(feature = "serve")]
-use wasmtime_cli::commands::ServeCommand;
-use wasmtime_cli::commands::{
-    CompileCommand, ConfigCommand, ExploreCommand, RunCommand, SettingsCommand, WastCommand,
-};
 
 /// Wasmtime WebAssembly Runtime
 #[derive(Parser)]
@@ -42,26 +37,37 @@ struct Wasmtime {
     #[clap(subcommand)]
     subcommand: Option<Subcommand>,
     #[clap(flatten)]
-    run: RunCommand,
+    run: wasmtime_cli::commands::RunCommand,
 }
 
 #[derive(Parser)]
 enum Subcommand {
+    /// Runs a WebAssembly module
+    Run(wasmtime_cli::commands::RunCommand),
+
     /// Controls Wasmtime configuration settings
-    Config(ConfigCommand),
+    #[cfg(feature = "cache")]
+    Config(wasmtime_cli::commands::ConfigCommand),
+
     /// Compiles a WebAssembly module.
-    Compile(CompileCommand),
+    #[cfg(feature = "cranelift")]
+    Compile(wasmtime_cli::commands::CompileCommand),
+
     /// Explore the compilation of a WebAssembly module to native code.
-    Explore(ExploreCommand),
-    /// Runs a WebAssembly module
-    Run(RunCommand),
+    #[cfg(feature = "explore")]
+    Explore(wasmtime_cli::commands::ExploreCommand),
+
     /// Serves requests from a wasi-http proxy component.
     #[cfg(feature = "serve")]
-    Serve(ServeCommand),
+    Serve(wasmtime_cli::commands::ServeCommand),
+
     /// Displays available Cranelift settings for a target.
-    Settings(SettingsCommand),
+    #[cfg(feature = "cranelift")]
+    Settings(wasmtime_cli::commands::SettingsCommand),
+
     /// Runs a WebAssembly test script file
-    Wast(WastCommand),
+    #[cfg(feature = "wast")]
+    Wast(wasmtime_cli::commands::WastCommand),
 }
 
 impl Wasmtime {
@@ -69,13 +75,24 @@ impl Wasmtime {
     pub fn execute(self) -> Result<()> {
         let subcommand = self.subcommand.unwrap_or(Subcommand::Run(self.run));
         match subcommand {
+            Subcommand::Run(c) => c.execute(),
+
+            #[cfg(feature = "cache")]
             Subcommand::Config(c) => c.execute(),
+
+            #[cfg(feature = "cranelift")]
             Subcommand::Compile(c) => c.execute(),
+
+            #[cfg(feature = "explore")]
             Subcommand::Explore(c) => c.execute(),
-            Subcommand::Run(c) => c.execute(),
+
             #[cfg(feature = "serve")]
             Subcommand::Serve(c) => c.execute(),
+
+            #[cfg(feature = "cranelift")]
             Subcommand::Settings(c) => c.execute(),
+
+            #[cfg(feature = "wast")]
             Subcommand::Wast(c) => c.execute(),
         }
     }
diff --git a/src/commands.rs b/src/commands.rs
index ae46a2264..86ed4e90c 100644
--- a/src/commands.rs
+++ b/src/commands.rs
@@ -1,16 +1,34 @@
 //! The module for the Wasmtime CLI commands.
 
-mod compile;
-mod config;
-mod explore;
 mod run;
-mod settings;
-mod wast;
+pub use self::run::*;
 
 #[cfg(feature = "serve")]
 mod serve;
-
-pub use self::{compile::*, config::*, explore::*, run::*, settings::*, wast::*};
-
 #[cfg(feature = "serve")]
 pub use self::serve::*;
+
+#[cfg(feature = "explore")]
+mod explore;
+#[cfg(feature = "explore")]
+pub use self::explore::*;
+
+#[cfg(feature = "wast")]
+mod wast;
+#[cfg(feature = "wast")]
+pub use self::wast::*;
+
+#[cfg(feature = "cache")]
+mod config;
+#[cfg(feature = "cache")]
+pub use self::config::*;
+
+#[cfg(feature = "compile")]
+mod compile;
+#[cfg(feature = "compile")]
+pub use self::compile::*;
+
+#[cfg(feature = "cranelift")]
+mod settings;
+#[cfg(feature = "cranelift")]
+pub use self::settings::*;
diff --git a/src/commands/compile.rs b/src/commands/compile.rs
index a26fee670..d4309d01d 100644
--- a/src/commands/compile.rs
+++ b/src/commands/compile.rs
@@ -59,7 +59,7 @@ pub struct CompileCommand {
 impl CompileCommand {
     /// Executes the command.
     pub fn execute(mut self) -> Result<()> {
-        self.common.init_logging();
+        self.common.init_logging()?;
 
         let mut config = self.common.config(self.target.as_deref())?;
 
@@ -87,7 +87,11 @@ impl CompileCommand {
             );
         }
 
+        #[cfg(feature = "wat")]
         let input = wat::parse_file(&self.module).with_context(|| "failed to read input file")?;
+        #[cfg(not(feature = "wat"))]
+        let input = std::fs::read(&self.module)
+            .with_context(|| format!("failed to read input file: {:?}", self.module))?;
 
         let output = self.output.take().unwrap_or_else(|| {
             let mut output: PathBuf = self.module.file_name().unwrap().into();
diff --git a/src/commands/explore.rs b/src/commands/explore.rs
index 59ca4baa6..ff03dc4a8 100644
--- a/src/commands/explore.rs
+++ b/src/commands/explore.rs
@@ -29,7 +29,7 @@ pub struct ExploreCommand {
 impl ExploreCommand {
     /// Executes the command.
     pub fn execute(mut self) -> Result<()> {
-        self.common.init_logging();
+        self.common.init_logging()?;
 
         let config = self.common.config(self.target.as_deref())?;
 
diff --git a/src/commands/run.rs b/src/commands/run.rs
index 77af38827..dc7c8ab05 100644
--- a/src/commands/run.rs
+++ b/src/commands/run.rs
@@ -10,15 +10,10 @@ use crate::common::{Profile, RunCommon, RunTarget};
 use anyhow::{anyhow, bail, Context as _, Error, Result};
 use clap::Parser;
 use std::ffi::OsString;
-use std::fs::File;
-use std::io::Write;
 use std::path::{Path, PathBuf};
 use std::sync::Arc;
 use std::thread;
-use wasmtime::{
-    AsContextMut, Engine, Func, GuestProfiler, Module, Store, StoreLimits, UpdateDeadline, Val,
-    ValType,
-};
+use wasmtime::{Engine, Func, Module, Store, StoreLimits, Val, ValType};
 use wasmtime_wasi::maybe_exit_on_error;
 use wasmtime_wasi::preview2;
 use wasmtime_wasi::sync::{ambient_authority, Dir, TcpListener, WasiCtxBuilder};
@@ -115,7 +110,7 @@ enum CliLinker {
 impl RunCommand {
     /// Executes the command.
     pub fn execute(mut self) -> Result<()> {
-        self.run.common.init_logging();
+        self.run.common.init_logging()?;
 
         let mut config = self.run.common.config(None)?;
 
@@ -195,6 +190,7 @@ impl RunCommand {
 
             // Add the module's functions to the linker.
             match &mut linker {
+                #[cfg(feature = "cranelift")]
                 CliLinker::Core(linker) => {
                     linker.module(&mut store, name, &module).context(format!(
                         "failed to process preload `{}` at `{}`",
@@ -202,6 +198,10 @@ impl RunCommand {
                         path.display()
                     ))?;
                 }
+                #[cfg(not(feature = "cranelift"))]
+                CliLinker::Core(_) => {
+                    bail!("support for --preload disabled at compile time");
+                }
                 #[cfg(feature = "component-model")]
                 CliLinker::Component(_) => {
                     bail!("--preload cannot be used with components");
@@ -283,78 +283,96 @@ impl RunCommand {
         &self,
         store: &mut Store<Host>,
         modules: Vec<(String, Module)>,
-    ) -> Box<dyn FnOnce(&mut Store<Host>)> {
+    ) -> Result<Box<dyn FnOnce(&mut Store<Host>)>> {
         if let Some(Profile::Guest { path, interval }) = &self.run.profile {
-            let module_name = self.module_and_args[0].to_str().unwrap_or("<main module>");
-            let interval = *interval;
-            store.data_mut().guest_profiler =
-                Some(Arc::new(GuestProfiler::new(module_name, interval, modules)));
-
-            fn sample(mut store: impl AsContextMut<Data = Host>) {
-                let mut profiler = store
-                    .as_context_mut()
-                    .data_mut()
-                    .guest_profiler
-                    .take()
-                    .unwrap();
-                Arc::get_mut(&mut profiler)
-                    .expect("profiling doesn't support threads yet")
-                    .sample(&store);
-                store.as_context_mut().data_mut().guest_profiler = Some(profiler);
-            }
-
-            if let Some(timeout) = self.run.common.wasm.timeout {
-                let mut timeout = (timeout.as_secs_f64() / interval.as_secs_f64()).ceil() as u64;
-                assert!(timeout > 0);
-                store.epoch_deadline_callback(move |mut store| {
-                    sample(&mut store);
-                    timeout -= 1;
-                    if timeout == 0 {
-                        bail!("timeout exceeded");
-                    }
-                    Ok(UpdateDeadline::Continue(1))
-                });
-            } else {
-                store.epoch_deadline_callback(move |mut store| {
-                    sample(&mut store);
-                    Ok(UpdateDeadline::Continue(1))
-                });
+            #[cfg(feature = "profiling")]
+            return Ok(self.setup_guest_profiler(store, modules, path, *interval));
+            #[cfg(not(feature = "profiling"))]
+            {
+                let _ = (modules, path, interval);
+                bail!("support for profiling disabled at compile time");
             }
+        }
 
+        if let Some(timeout) = self.run.common.wasm.timeout {
             store.set_epoch_deadline(1);
             let engine = store.engine().clone();
-            thread::spawn(move || loop {
-                thread::sleep(interval);
+            thread::spawn(move || {
+                thread::sleep(timeout);
                 engine.increment_epoch();
             });
+        }
 
-            let path = path.clone();
-            return Box::new(move |store| {
-                let profiler = Arc::try_unwrap(store.data_mut().guest_profiler.take().unwrap())
-                    .expect("profiling doesn't support threads yet");
-                if let Err(e) = std::fs::File::create(&path)
-                    .map_err(anyhow::Error::new)
-                    .and_then(|output| profiler.finish(std::io::BufWriter::new(output)))
-                {
-                    eprintln!("failed writing profile at {path}: {e:#}");
-                } else {
-                    eprintln!();
-                    eprintln!("Profile written to: {path}");
-                    eprintln!("View this profile at https://profiler.firefox.com/.");
-                }
-            });
+        Ok(Box::new(|_store| {}))
+    }
+
+    #[cfg(feature = "profiling")]
+    fn setup_guest_profiler(
+        &self,
+        store: &mut Store<Host>,
+        modules: Vec<(String, Module)>,
+        path: &str,
+        interval: std::time::Duration,
+    ) -> Box<dyn FnOnce(&mut Store<Host>)> {
+        use wasmtime::{AsContextMut, GuestProfiler, UpdateDeadline};
+
+        let module_name = self.module_and_args[0].to_str().unwrap_or("<main module>");
+        store.data_mut().guest_profiler =
+            Some(Arc::new(GuestProfiler::new(module_name, interval, modules)));
+
+        fn sample(mut store: impl AsContextMut<Data = Host>) {
+            let mut profiler = store
+                .as_context_mut()
+                .data_mut()
+                .guest_profiler
+                .take()
+                .unwrap();
+            Arc::get_mut(&mut profiler)
+                .expect("profiling doesn't support threads yet")
+                .sample(&store);
+            store.as_context_mut().data_mut().guest_profiler = Some(profiler);
         }
 
         if let Some(timeout) = self.run.common.wasm.timeout {
-            store.set_epoch_deadline(1);
-            let engine = store.engine().clone();
-            thread::spawn(move || {
-                thread::sleep(timeout);
-                engine.increment_epoch();
+            let mut timeout = (timeout.as_secs_f64() / interval.as_secs_f64()).ceil() as u64;
+            assert!(timeout > 0);
+            store.epoch_deadline_callback(move |mut store| {
+                sample(&mut store);
+                timeout -= 1;
+                if timeout == 0 {
+                    bail!("timeout exceeded");
+                }
+                Ok(UpdateDeadline::Continue(1))
+            });
+        } else {
+            store.epoch_deadline_callback(move |mut store| {
+                sample(&mut store);
+                Ok(UpdateDeadline::Continue(1))
             });
         }
 
-        Box::new(|_store| {})
+        store.set_epoch_deadline(1);
+        let engine = store.engine().clone();
+        thread::spawn(move || loop {
+            thread::sleep(interval);
+            engine.increment_epoch();
+        });
+
+        let path = path.to_string();
+        return Box::new(move |store| {
+            let profiler = Arc::try_unwrap(store.data_mut().guest_profiler.take().unwrap())
+                .expect("profiling doesn't support threads yet");
+            if let Err(e) = std::fs::File::create(&path)
+                .map_err(anyhow::Error::new)
+                .and_then(|output| profiler.finish(std::io::BufWriter::new(output)))
+            {
+                eprintln!("failed writing profile at {path}: {e:#}");
+            } else {
+                eprintln!();
+                eprintln!("Profile written to: {path}");
+                eprintln!("View this profile at https://profiler.firefox.com/.");
+            }
+        });
     }
 
     fn load_main_module(
@@ -367,50 +385,64 @@ impl RunCommand {
         // The main module might be allowed to have unknown imports, which
         // should be defined as traps:
         if self.run.common.wasm.unknown_imports_trap == Some(true) {
+            #[cfg(feature = "cranelift")]
             match linker {
                 CliLinker::Core(linker) => {
                     linker.define_unknown_imports_as_traps(module.unwrap_core())?;
                 }
                 _ => bail!("cannot use `--trap-unknown-imports` with components"),
             }
+            #[cfg(not(feature = "cranelift"))]
+            bail!("support for `unknown-imports-trap` disabled at compile time");
         }
 
         // ...or as default values.
         if self.run.common.wasm.unknown_imports_default == Some(true) {
+            #[cfg(feature = "cranelift")]
             match linker {
                 CliLinker::Core(linker) => {
                     linker.define_unknown_imports_as_default_values(module.unwrap_core())?;
                 }
                 _ => bail!("cannot use `--default-values-unknown-imports` with components"),
             }
+            #[cfg(not(feature = "cranelift"))]
+            bail!("support for `unknown-imports-trap` disabled at compile time");
         }
 
-        let finish_epoch_handler = self.setup_epoch_handler(store, modules);
+        let finish_epoch_handler = self.setup_epoch_handler(store, modules)?;
 
         let result = match linker {
             CliLinker::Core(linker) => {
-                // Use "" as a default module name.
                 let module = module.unwrap_core();
-                linker.module(&mut *store, "", &module).context(format!(
+                let instance = linker.instantiate(&mut *store, &module).context(format!(
                     "failed to instantiate {:?}",
                     self.module_and_args[0]
                 ))?;
 
-                // If a function to invoke was given, invoke it.
+                // If `_initialize` is present, meaning a reactor, then invoke
+                // the function.
+                if let Some(func) = instance.get_func(&mut *store, "_initialize") {
+                    func.typed::<(), ()>(&store)?.call(&mut *store, ())?;
+                }
+
+                // Look for the specific function provided or otherwise look for
+                // "" or "_start" exports to run as a "main" function.
                 let func = if let Some(name) = &self.invoke {
-                    match linker
-                        .get(&mut *store, "", name)
-                        .ok_or_else(|| anyhow!("no export named `{}` found", name))?
-                        .into_func()
-                    {
-                        Some(func) => func,
-                        None => bail!("export of `{}` wasn't a function", name),
-                    }
+                    Some(
+                        instance
+                            .get_func(&mut *store, name)
+                            .ok_or_else(|| anyhow!("no func export named `{}` found", name))?,
+                    )
                 } else {
-                    linker.get_default(&mut *store, "")?
+                    instance
+                        .get_func(&mut *store, "")
+                        .or_else(|| instance.get_func(&mut *store, "_start"))
                 };
 
-                self.invoke_func(store, func)
+                match func {
+                    Some(func) => self.invoke_func(store, func),
+                    None => Ok(()),
+                }
             }
             #[cfg(feature = "component-model")]
             CliLinker::Component(linker) => {
@@ -516,6 +548,7 @@ impl RunCommand {
         Ok(())
     }
 
+    #[cfg(feature = "coredump")]
     fn handle_core_dump(&self, store: &mut Store<Host>, err: Error) -> Error {
         let coredump_path = match &self.run.common.debug.coredump {
             Some(path) => path,
@@ -536,6 +569,11 @@ impl RunCommand {
         }
     }
 
+    #[cfg(not(feature = "coredump"))]
+    fn handle_core_dump(&self, _store: &mut Store<Host>, err: Error) -> Error {
+        err
+    }
+
     /// Populates the given `Linker` with WASI APIs.
     fn populate_with_wasi(
         &self,
@@ -611,7 +649,7 @@ impl RunCommand {
             {
                 // Silence the unused warning for `module` as it is only used in the
                 // conditionally-compiled wasi-threads.
-                drop(&module);
+                let _ = &module;
 
                 bail!(
                     "Cannot enable wasi-threads when the binary is not compiled with this feature."
@@ -751,7 +789,8 @@ struct Host {
     #[cfg(feature = "wasi-http")]
     wasi_http: Option<Arc<WasiHttpCtx>>,
     limits: StoreLimits,
-    guest_profiler: Option<Arc<GuestProfiler>>,
+    #[cfg(feature = "profiling")]
+    guest_profiler: Option<Arc<wasmtime::GuestProfiler>>,
 }
 
 impl preview2::WasiView for Host {
@@ -824,12 +863,16 @@ fn ctx_set_listenfd(mut num_fd: usize, builder: &mut WasiCtxBuilder) -> Result<u
     Ok(num_fd)
 }
 
+#[cfg(feature = "coredump")]
 fn write_core_dump(
     store: &mut Store<Host>,
     err: &anyhow::Error,
     name: &str,
     path: &str,
 ) -> Result<()> {
+    use std::fs::File;
+    use std::io::Write;
+
     let core_dump = err
         .downcast_ref::<wasmtime::WasmCoreDump>()
         .expect("should have been configured to capture core dumps");
diff --git a/src/commands/serve.rs b/src/commands/serve.rs
index 699321783..ce9c2560a 100644
--- a/src/commands/serve.rs
+++ b/src/commands/serve.rs
@@ -80,7 +80,7 @@ pub struct ServeCommand {
 impl ServeCommand {
     /// Start a server to run the given wasi-http proxy component
     pub fn execute(mut self) -> Result<()> {
-        self.run.common.init_logging();
+        self.run.common.init_logging()?;
 
         // We force cli errors before starting to listen for connections so tha we don't
         // accidentally delay them to the first request.
diff --git a/src/commands/wast.rs b/src/commands/wast.rs
index 9e02ebd0a..a3ac85f68 100644
--- a/src/commands/wast.rs
+++ b/src/commands/wast.rs
@@ -22,7 +22,7 @@ pub struct WastCommand {
 impl WastCommand {
     /// Executes the command.
     pub fn execute(mut self) -> Result<()> {
-        self.common.init_logging();
+        self.common.init_logging()?;
 
         let config = self.common.config(None)?;
         let store = Store::new(&Engine::new(&config)?, ());
diff --git a/src/common.rs b/src/common.rs
index 92708335d..00c04cfb1 100644
--- a/src/common.rs
+++ b/src/common.rs
@@ -189,22 +189,27 @@ impl RunCommon {
             None => {
                 // Parse the text format here specifically to add the `path` to
                 // the error message if there's a syntax error.
-                let wasm = wat::parse_bytes(bytes).map_err(|mut e| {
+                #[cfg(feature = "wat")]
+                let bytes = wat::parse_bytes(bytes).map_err(|mut e| {
                     e.set_path(path);
                     e
                 })?;
-                if wasmparser::Parser::is_component(&wasm) {
+                let _ = path;
+                if wasmparser::Parser::is_component(&bytes) {
                     #[cfg(feature = "component-model")]
                     {
                         self.ensure_allow_components()?;
-                        RunTarget::Component(Component::new(engine, &wasm)?)
+                        RunTarget::Component(Component::new(engine, &bytes)?)
                     }
                     #[cfg(not(feature = "component-model"))]
                     {
                         bail!("support for components was not enabled at compile time");
                     }
                 } else {
-                    RunTarget::Core(Module::new(engine, &wasm)?)
+                    #[cfg(feature = "cranelift")]
+                    return Ok(RunTarget::Core(Module::new(engine, &bytes)?));
+                    #[cfg(not(feature = "cranelift"))]
+                    bail!("support for compiling modules was disabled at compile time");
                 }
             }
         })


Test patch:
diff --git a/cranelift/filetests/filetests/isa/riscv64/simd-scalartovector.clif b/cranelift/filetests/filetests/isa/riscv64/simd-scalartovector.clif
index e9be3c845..d706a5708 100644
--- a/cranelift/filetests/filetests/isa/riscv64/simd-scalartovector.clif
+++ b/cranelift/filetests/filetests/isa/riscv64/simd-scalartovector.clif
@@ -157,3 +157,108 @@ block0(v0: f64):
 ;   .byte 0xa7, 0x04, 0x05, 0x02
 ;   ret
 
+function %scalartovector_i8_imm(i8) -> i8x16 {
+block0(v0: i8):
+    v1 = iconst.i8 7
+    v2 = scalar_to_vector.i8x16 v1
+    return v2
+}
+
+; VCode:
+; block0:
+;   vmv.v.x v11,zero #avl=16, #vtype=(e8, m1, ta, ma)
+;   vmv.v.i v0,1 #avl=2, #vtype=(e64, m1, ta, ma)
+;   vmerge.vim v15,v11,7,v0.t #avl=16, #vtype=(e8, m1, ta, ma)
+;   vse8.v v15,0(a1) #avl=16, #vtype=(e8, m1, ta, ma)
+;   ret
+;
+; Disassembled:
+; block0: ; offset 0x0
+;   .byte 0x57, 0x70, 0x08, 0xcc
+;   .byte 0xd7, 0x45, 0x00, 0x5e
+;   .byte 0x57, 0x70, 0x81, 0xcd
+;   .byte 0x57, 0xb0, 0x00, 0x5e
+;   .byte 0x57, 0x70, 0x08, 0xcc
+;   .byte 0xd7, 0xb7, 0xb3, 0x5c
+;   .byte 0xa7, 0x87, 0x05, 0x02
+;   ret
+
+function %scalartovector_i16_imm(i16) -> i16x8 {
+block0(v0: i16):
+    v1 = iconst.i16 7
+    v2 = scalar_to_vector.i16x8 v1
+    return v2
+}
+
+; VCode:
+; block0:
+;   vmv.v.x v11,zero #avl=8, #vtype=(e16, m1, ta, ma)
+;   vmv.v.i v0,1 #avl=2, #vtype=(e64, m1, ta, ma)
+;   vmerge.vim v15,v11,7,v0.t #avl=8, #vtype=(e16, m1, ta, ma)
+;   vse8.v v15,0(a1) #avl=16, #vtype=(e8, m1, ta, ma)
+;   ret
+;
+; Disassembled:
+; block0: ; offset 0x0
+;   .byte 0x57, 0x70, 0x84, 0xcc
+;   .byte 0xd7, 0x45, 0x00, 0x5e
+;   .byte 0x57, 0x70, 0x81, 0xcd
+;   .byte 0x57, 0xb0, 0x00, 0x5e
+;   .byte 0x57, 0x70, 0x84, 0xcc
+;   .byte 0xd7, 0xb7, 0xb3, 0x5c
+;   .byte 0x57, 0x70, 0x08, 0xcc
+;   .byte 0xa7, 0x87, 0x05, 0x02
+;   ret
+
+function %scalartovector_i32_imm(i32) -> i32x4 {
+block0(v0: i32):
+    v1 = iconst.i32 7
+    v2 = scalar_to_vector.i32x4 v1
+    return v2
+}
+
+; VCode:
+; block0:
+;   vmv.v.x v11,zero #avl=4, #vtype=(e32, m1, ta, ma)
+;   vmv.v.i v0,1 #avl=2, #vtype=(e64, m1, ta, ma)
+;   vmerge.vim v15,v11,7,v0.t #avl=4, #vtype=(e32, m1, ta, ma)
+;   vse8.v v15,0(a1) #avl=16, #vtype=(e8, m1, ta, ma)
+;   ret
+;
+; Disassembled:
+; block0: ; offset 0x0
+;   .byte 0x57, 0x70, 0x02, 0xcd
+;   .byte 0xd7, 0x45, 0x00, 0x5e
+;   .byte 0x57, 0x70, 0x81, 0xcd
+;   .byte 0x57, 0xb0, 0x00, 0x5e
+;   .byte 0x57, 0x70, 0x02, 0xcd
+;   .byte 0xd7, 0xb7, 0xb3, 0x5c
+;   .byte 0x57, 0x70, 0x08, 0xcc
+;   .byte 0xa7, 0x87, 0x05, 0x02
+;   ret
+
+function %scalartovector_i64_imm(i64) -> i64x2 {
+block0(v0: i64):
+    v1 = iconst.i64 7
+    v2 = scalar_to_vector.i64x2 v1
+    return v2
+}
+
+; VCode:
+; block0:
+;   vmv.v.x v11,zero #avl=2, #vtype=(e64, m1, ta, ma)
+;   vmv.v.i v0,1 #avl=2, #vtype=(e64, m1, ta, ma)
+;   vmerge.vim v15,v11,7,v0.t #avl=2, #vtype=(e64, m1, ta, ma)
+;   vse8.v v15,0(a1) #avl=16, #vtype=(e8, m1, ta, ma)
+;   ret
+;
+; Disassembled:
+; block0: ; offset 0x0
+;   .byte 0x57, 0x70, 0x81, 0xcd
+;   .byte 0xd7, 0x45, 0x00, 0x5e
+;   .byte 0x57, 0xb0, 0x00, 0x5e
+;   .byte 0xd7, 0xb7, 0xb3, 0x5c
+;   .byte 0x57, 0x70, 0x08, 0xcc
+;   .byte 0xa7, 0x87, 0x05, 0x02
+;   ret
+
diff --git a/cranelift/filetests/filetests/runtests/simd-scalartovector.clif b/cranelift/filetests/filetests/runtests/simd-scalartovector.clif
index 5ec07bc47..5a92405b6 100644
--- a/cranelift/filetests/filetests/runtests/simd-scalartovector.clif
+++ b/cranelift/filetests/filetests/runtests/simd-scalartovector.clif
@@ -15,6 +15,14 @@ block0(v0: i32):
 ; run: %scalartovector_i32(1) == [1 0 0 0]
 ; run: %scalartovector_i32(4294967295) == [4294967295 0 0 0]
 
+function %scalartovector_i32_imm(i32) -> i32x4 {
+block0(v0: i32):
+    v1 = iconst.i32 7
+    v2 = scalar_to_vector.i32x4 v1
+    return v2
+}
+; run: %scalartovector_i32_imm(1) == [7 0 0 0]
+
 function %scalartovector_i64(i64) -> i64x2 {
 block0(v0: i64):
     v1 = scalar_to_vector.i64x2 v0
@@ -24,6 +32,14 @@ block0(v0: i64):
 ; run: %scalartovector_i64(1) == [1 0]
 ; run: %scalartovector_i64(18446744073709551615) == [18446744073709551615 0]
 
+function %scalartovector_i64_imm(i64) -> i64x2 {
+block0(v0: i64):
+    v1 = iconst.i64 7
+    v2 = scalar_to_vector.i64x2 v1
+    return v2
+}
+; run: %scalartovector_i64_imm(1) == [7 0]
+
 function %scalartovector_f32(f32) -> f32x4 {
 block0(v0: f32):
     v1 = scalar_to_vector.f32x4 v0
diff --git a/crates/test-programs/src/bin/api_proxy.rs b/crates/test-programs/src/bin/api_proxy.rs
index b81e81508..6798d4e30 100644
--- a/crates/test-programs/src/bin/api_proxy.rs
+++ b/crates/test-programs/src/bin/api_proxy.rs
@@ -3,7 +3,7 @@ pub mod bindings {
 
     wit_bindgen::generate!({
         path: "../wasi-http/wit",
-        world: "wasi:http/proxy@0.2.0-rc-2023-10-18",
+        world: "wasi:http/proxy",
         exports: {
             "wasi:http/incoming-handler": T,
         },