WebAssembly at the Edge: WASI Preview 3 Runtime Architecture (2026)

WebAssembly at the Edge: WASI Preview 3 Runtime Architecture (2026)

WebAssembly at the Edge: WASI Preview 3 Runtime Architecture in 2026

Last Updated: October 2026

This is a full rewrite of our April 2026 article. The biggest correction: WASI Preview 3 is no longer a roadmap item. It was ratified as WASI 0.3.0 on June 11, 2026, and the details below reflect the final specification, not the release candidates.

Most edge teams that adopted WebAssembly in 2024 and 2025 hit the same wall. Components were fast to start and strongly sandboxed, but the moment two components had to cooperate over anything asynchronous, such as a streaming HTTP body or a socket read, the composition fell apart. Each component carried its own event loop, and a wake-up signal could not travel through a chain of components. That limitation is gone. The WASI Preview 3 runtime architecture, shipped as WASI 0.3, moves asynchrony out of a library (wasi:io) and into the Component Model’s canonical ABI, so one host scheduler drives every component in a composition.

This matters now because the specification is stable, Wasmtime enables it by default since version 46, and orchestrators such as wasmCloud and Spin have already built on it. If you design gateways, protocol translators or policy engines for factory floors and remote sites, the unit of design is changing from “a module with a few host calls” to “a graph of typed, streaming components.”

What this covers: the state of WASI in October 2026, the shared-scheduler architecture and why it replaces pollables, how to wire an edge gateway from composed components, the runtime landscape (and which tools from our April post are now obsolete), a concrete migration path from 0.2, and the failure modes that remain.

Context and Background

WebAssembly began as a browser bytecode in 2015 and reached the server through the WebAssembly System Interface (WASI), a set of capability-scoped APIs that stand in for the POSIX syscall surface. The history is easiest to read as three generations, and our April article blurred them.

Preview 1 (WASI 0.1, based on the WITX description format) gave core modules a POSIX-like surface: files, clocks, random, exit. It is now deprecated in the Component Model documentation, and its “components” are really core modules adapted after the fact. Preview 2 (WASI 0.2, released January 2024) introduced the Component Model and the WIT interface description language, plus wasi:http, wasi:sockets and wasi:filesystem. Its patch releases continued on a two-month train up to 0.2.12. Preview 3 (WASI 0.3) added native async. The WASI roadmap lists 0.3.0 as shipped on June 11, 2026 after a WASI Subgroup vote, and 0.3.1 on August 11, 2026.

For edge engineers the practical sequence matters more than the numbering. Preview 2 gave you portable, typed interfaces and strong isolation, which is why projects such as wasmCloud and Spin could ship. What it did not give you was composable concurrency. Any component that exposed a stream or waited on I/O brought its own polling machinery, and the host had to bridge between them.

The competing deployment models are worth stating plainly. Containers remain the default for edge Kubernetes distributions such as K3s (see our K3s edge Kubernetes production guide). WebAssembly occupies a different niche: small, language-neutral, capability-scoped units that start fast and can be loaded in-process by a long-running agent. Our WebAssembly versus containers ADR for edge functions walks through that decision; this article assumes you have decided Wasm is a fit and want to know how the runtime is actually put together.

The authoritative references are the WASI 0.3 release page on wasi.dev and the Bytecode Alliance’s launch article. Where this article quotes a version, date or interface change, it comes from those two sources or from the Wasmtime release notes. Where a claim is mine, it is labelled as opinion or as an engineering estimate.

What changed since our April 2026 post

Three statements in the April article need correcting. First, it described Preview 3 as covering “2025 to 2026” and as still maturing; it is now a ratified, stable release with a published patch schedule. Second, it listed Krustlet as a viable Kubernetes-native option with 50 to 150 ms start-up. The Krustlet repository was archived by its owner on February 1, 2025 and is read-only, so it should not appear in a 2026 architecture. Third, it presented runtime start-up figures in a comparison table without sources. I have removed those figures here; I discuss start-up as a mechanism rather than as a number I cannot back.

The WASI Preview 3 Runtime Architecture: One Scheduler, Typed Streams

In WASI 0.3, asynchrony is a feature of the Component Model’s canonical ABI instead of a WASI library. Three primitives, async func, stream<T> and future<T>, let the host run a single event loop and scheduler across all composed components, so a wake-up can propagate through any chain of components without each one polling.

WASI Preview 3 runtime architecture showing guest components, canonical ABI, shared event loop and capability grants over the Linux kernel

Figure 1: WASI Preview 3 runtime architecture. Guest components in any language call through the canonical ABI into one host scheduler, which grants capabilities and reaches the kernel only through WASI interfaces.

The figure shows the layering. Guest components, written in whatever language suits each job, import and export WIT interfaces. The canonical ABI is the contract that lowers high-level types such as strings, records, streams and futures into the core WebAssembly linear memory and back. The host runtime, Wasmtime in the reference case, owns the event loop and the capability table. The kernel is reached only through the WASI interface implementations the host chooses to provide.

The sandwich problem, and why pollables had to go

Preview 2 modelled readiness with a pollable resource scoped to a single component instance. Imagine a chain where component A calls component B, which calls the host. B can wait on the host’s pollable, but B cannot hand that pollable to A in a form A can wait on, because a pollable is only meaningful inside the instance that owns it. The WASI documentation calls this the sandwich problem: the component in the middle cannot forward wake-ups.

In practice it meant that two components with streaming APIs could not be composed without custom glue, and that every component carried its own copy of an event loop. For an edge gateway, which is nothing but a chain of “read, decode, filter, forward” stages, this was the central obstacle.

Sequence diagram contrasting WASI 0.2 pollable wake-up failure with WASI 0.3 host-scheduled futures for WASI Preview 3 runtime architecture

Figure 2: The sandwich problem versus the shared scheduler. In 0.2, component B holds a pollable that component A cannot wait on. In 0.3, each async call returns a future the host can resolve and use to reschedule the awaiting task.

WASI 0.3 removes wasi:io entirely. There is no 0.3 version of the package; its functionality now lives in the canonical ABI. The documented mapping is mechanical:

WASI 0.2 pattern WASI 0.3 replacement
pollable future<T>
input-stream stream<u8>
output-stream stream<u8>, passed in rather than returned
poll(list<pollable>) await on a future
subscribe() returning a pollable a call returning a future
start-foo / finish-foo pairs a single async func

The execution model: completion-based, owned handles

Two properties of the new model deserve attention because they shape how you design components.

The first is that scheduling is completion-based rather than readiness-based. The Bytecode Alliance article compares it to io_uring and Windows IOCP: you submit an operation and are told when it is done, rather than polling a file descriptor to learn that it is ready to try. Readiness-style APIs such as epoll can still be emulated on top, which matters for language runtimes that expect them.

The second is that streams and futures are owned handles. When you pass a stream<T> across a component boundary, ownership transfers; it cannot be borrowed and read by two parties. That rule is what makes cross-component streaming safe without shared mutable state, and it gives the runtime a clean place to enforce back-pressure. If a consumer reads slowly, the producer’s write completes slowly, and the effect propagates to the source.

Streams with a trailing future: error handling that survives early close

A subtle improvement is how errors are reported. In 0.2, errors appeared inline on stream reads, so a reader that stopped early could not distinguish a clean close from a failure. In 0.3, operations that produce data return a stream plus a separate future<result<_, error-code>>. The migration guide shows the filesystem read:

// WASI 0.3
read-via-stream: func(offset: filesize)
  -> tuple<stream<u8>, future<result<_, error-code>>>;

The outcome future resolves whether or not you drain the stream. For a telemetry pipeline this is a real operational gain: a downstream stage can stop reading after it has enough data and still learn whether the upstream read was healthy.

The write direction is inverted for the same reason. Rather than receiving an output stream resource and writing into it, the caller hands a stream<u8> to the callee and receives a completion future. The same shape applies to stdout, stderr, file writes and TCP sends.

What the 0.3 interface set looks like

The worlds and interfaces have been reorganised, not just re-typed. These are the changes that affect edge designs most, taken from the release page:

  • wasi:http: nine 0.2 resources collapse into two, request and response, with stream bodies and future trailers. The proxy world is replaced by service (imports client, exports handler) and middleware, which is a superset of service that also imports a handler. The handler signature becomes handle: async func(request: request) -> result<response, error-code>.
  • wasi:sockets: seven interfaces become two (types and ip-name-lookup). The network resource is removed and network access is granted at the world level. Connect and listen pairs become single async calls, and TCP listen returns a stream of sockets.
  • wasi:cli: stdin, stdout and stderr use the stream-plus-future pattern, run is async, and a shared types interface holds error-code.
  • wasi:filesystem: nearly all descriptor methods are async, and directory listing returns stream<directory-entry>.
  • wasi:clocks: wall-clock becomes system-clock, datetime becomes instant, and the subscribe functions are replaced by awaitable wait-until and wait-for.
  • wasi:random: len becomes max-len, and implementations may return fewer bytes, so callers must loop.

Version 0.3.1 added the map<K, V> type and the implements and external-id annotations. Patch releases are planned every two months on a release train, with 0.3.2 scheduled for October 13, 2026 on the published roadmap. The roadmap states that its goals are provisional.

Designing an Edge Gateway as a Graph of Streaming Components

The reason to care about the shared scheduler is that it lets you build the classic gateway pipeline as separately versioned, separately sandboxed components that are composed at deploy time. The gateway reads from a field protocol, normalises, applies policy, and ships results upstream. With 0.2 you built that as one fat module or as several processes. With 0.3 you can build it as a component graph in which each stage is a typed stream transformer.

Edge gateway component graph with driver, transform and policy components connected by streams, an HTTP uplink and host epoch and fuel limits

Figure 3: An edge gateway as composed components. Sensor readings flow as streams through transform and policy stages to an uplink, while the host applies epoch and fuel limits to the compute-heavy stages.

The figure shows a driver component that talks to a PLC or sensor bus, a transform component that decodes and unit-converts, and a policy component that decides whether to forward, drop or alert. The policy stage uses wasi:http client calls (or an MQTT library built on wasi:sockets) to uplink, and a local buffer absorbs failures. For the store-and-forward half of this design, see our MQTT store-and-forward edge gateway article; the component graph changes how the pieces are packaged, not the delivery semantics you need.

A WIT contract for the driver stage

The April post showed a synchronous driver interface with read-sensor: func() -> reading. In 0.3 the natural shape is a stream. The following is an illustrative sketch, not a tested package; check the syntax against the WIT version your toolchain pins.

package example:gateway@0.1.0;

interface driver {
  record reading {
    value: f32,
    timestamp-ns: u64,
    unit: string,
  }

  /// Yields readings until the stream is dropped.
  subscribe: async func(interval-ms: u32) -> stream<reading>;

  configure: func(interval-ms: u32) -> result<u32, string>;
}

world device-driver {
  export driver;
  import wasi:clocks/monotonic-clock@0.3.0;
}

Compare the old design. A synchronous read-sensor forces the gateway to poll every driver on a timer and to hold a thread or an instance while waiting. A stream lets the driver push at its own cadence and lets the downstream stage apply back-pressure. A thousand idle drivers cost almost nothing, because a suspended async task is just a saved continuation in the host.

Composition with the middleware world

For HTTP-shaped edges, the new wasi:http/middleware world is the most interesting piece. A middleware component both exports and imports the handler interface, so you can stack authentication, rate limiting and header rewriting as separate components in front of a business-logic service. The Bytecode Alliance article notes that this service chaining can happen inside one process rather than over the network, and it claims this reduces inter-service call cost from milliseconds to nanoseconds. That is the authors’ own claim; I have not seen an independent benchmark, so treat it as a plausible direction rather than a number to plan around. The mechanism is credible, since an in-process call avoids serialisation to a socket, but real gains depend on how much data crosses the component boundary.

Rust guest sketch

Rust is the best-supported guest language. With wit-bindgen the async handler maps to an ordinary async fn on a generated Guest trait. The sketch below is illustrative. The migration guide states that the Rust async toolchain currently needs a nightly compiler for the wit-bindgen output to link, so pin your toolchain and test in CI.

// Illustrative sketch. Names depend on your generated bindings.
wit_bindgen::generate!({ world: "service", path: "wit" });

struct Gateway;

impl Guest for Gateway {
    async fn handle(request: Request) -> Result<Response, ErrorCode> {
        // Read the request body as a stream, forward the first N bytes,
        // and return a response without buffering the whole payload.
        let (body, _trailers) = request.consume_body();
        // ... transform and forward ...
        Ok(Response::new(/* headers, body stream */))
    }
}

export!(Gateway);

The important property is not the syntax. It is that this component never spawns a thread or runs its own executor loop at the ABI boundary; the host suspends and resumes it.

Languages and the stackful versus stackless split

The ABI was designed to support both stackless coroutines (the async/await model of Rust, JavaScript, Python and C#) and stackful ones (Go’s goroutines). The launch article lists Rust via wit-bindgen as the first-class path, Go via componentize-go as stackful, and JavaScript (componentize-js), Python (componentize-py), C# and C as work in progress at the time of the announcement. For a fleet-wide strategy, this means a polyglot gateway is feasible but uneven: write latency-critical stages in Rust, and expect to track toolchain releases closely for the others.

The Runtime Landscape in October 2026

A runtime comparison table in a post like this goes stale quickly, so I have limited it to facts I could verify and omitted start-up numbers.

Runtime Role WASI 0.3 status (verified) Notes
Wasmtime (Bytecode Alliance) Reference host, embeddable in Rust, C, Python and others 46.0.0 (June 22, 2026) enables 0.3.0 and component-model-async by default Versions 41 to 45 implemented release-candidate snapshots and need -Sp3 -W component-model-async=y
jco (JavaScript toolchain) Componentizes JS and runs components in Node or browsers Supports 0.3 via preview3-shim The launch article said a default-enabled release was imminent
wasmCloud Distributed, actor-style orchestration 2.5 (community call of July 1, 2026) builds on Wasmtime 46 and turns WASI P3 on by default Also ships the implements feature behind a flag
Spin and SpinKube Event-driven functions, Kubernetes operator Spin 4.x release notes reference WASIp3 HTTP middleware and server fixes; 4.2.x builds track Wasmtime 49 I could not confirm a statement of full P3 support in the release notes
WAMR Interpreter and AOT for constrained devices Not verified Typically core modules; check the project before assuming components
Krustlet Kubernetes kubelet in Rust Not applicable Repository archived February 1, 2025; do not adopt

Two observations follow. First, Wasmtime dominates the embedding landscape: wasmCloud and Spin both sit on it, so the semantics of cancellation, resource limits and security patches flow from one project. Second, Wasmtime releases fast. The release page shows 46.0.3, 47.0.4 and 48.0.0 all dated August 20, 2026, with 48.0.1 on August 24 and a 49 release candidate on September 5. Security fixes were backported to several older lines the same day, including 36.0.14 and 24.0.13. If you embed Wasmtime in an edge agent, you need an update path measured in days, not quarters.

Where wazero, Wasmer and others fit

The April post listed wazero and Wasmer as having Preview 3 support. I could not verify that against primary sources in October 2026, so I have removed those claims. Both are real runtimes with distinct strengths: wazero is a pure-Go runtime with no CGO dependency, which makes it convenient for Go agents, and Wasmer targets embedding across many languages. Before committing, confirm the exact WASI and Component Model features each supports for your target version. The WASI release page references a shared wasi-testsuite that currently runs against Wasmtime and jco on Linux, macOS and Windows, which is a useful yardstick for what “conformant” means.

Capability Security and Resource Governance at the Edge

WebAssembly’s security argument is that a component has no ambient authority. It can call only what its world imports, and the host decides what each import connects to. That is the property that makes it attractive for multi-tenant gateways, third-party analytics plugins and customer-supplied transforms.

What the sandbox does and does not give you

A component’s linear memory is isolated per instance, so one driver cannot read another’s heap. Its filesystem view is a set of preopened directories, its network reach is whatever the world and host allow, and it cannot make a system call that the host did not import. This is a much smaller attack surface than a container sharing a kernel with every other container.

It is not a complete security story. The sandbox protects the host from the guest; it does not protect your data from a guest you have granted access to. If a plugin is granted network egress and the sensor stream, it can exfiltrate that stream. Capability design, not the sandbox itself, determines the blast radius. In 0.3 the move of network access to the world level makes that explicit: the world declaration is a reviewable manifest of what the component may reach. Treat it that way in code review.

There is also a host-side risk: the runtime is a large, security-critical codebase. The frequency of backported security releases noted above is a reminder that the sandbox boundary depends on the runtime being patched.

Bounding CPU and memory

Edge nodes are small, so unbounded guests are a liability. Wasmtime provides several mechanisms; I describe them from the project’s design, and you should confirm exact configuration APIs in the version you embed.

  • Epoch interruption lets the host increment an epoch counter from a timer thread and traps guests that exceed their deadline. It has low overhead compared with instruction counting.
  • Fuel meters instructions precisely, at higher cost, and suits billing or strict determinism.
  • Memory and table limits cap growth per store.
  • The pooling allocator pre-reserves instance slots, which is the main reason instantiation is cheap and predictable.

The wasmCloud 2.5 community notes describe a proposed cancellation design that uses epoch interruption with a per-request store ID and a cancellation handle, scoped to the workload so the blast radius stops at the workload boundary. The discussion on that call was explicitly a design in progress, with a draft pull request covering roughly 70 to 75 percent of it. That is a good illustration of an unresolved edge problem: with async, you must be able to cancel a long-running invocation, and the standard’s own cancellation story is still evolving.

Why start-up is cheap, as a mechanism

I have avoided quoting start-up figures because the sources I found for sub-millisecond claims either were not primary or mixed incompatible measurements. The mechanism is nonetheless well understood. A component is validated and compiled ahead of time, often to native code; instantiation then allocates linear memory from a pool and initialises a handful of tables and globals. There is no process creation, no page-cache cold read of a root filesystem and no dynamic linker run. The cost that remains is dominated by your own initialisation code. If you want a number for your fleet, measure it on your hardware with your component, including the first request, and report percentiles rather than a single figure.

Deploying and Migrating: From WASI 0.2 to 0.3

Migration is optional, which is the first thing to tell your stakeholders. The Component Model documentation describes 0.3 as purely additive: 0.2 components keep working, wasmtime serve runs both 0.2 and 0.3 components from one binary and falls back to wasi:http/proxy for the older ones, and implementations are allowed to polyfill 0.2 on top of 0.3 primitives. You migrate for composable async, for the newer wasi:http shape, or because your guest toolchain has moved on.

Decision flow for adopting WASI 0.3 on Kubernetes, single gateways or microcontrollers, ending with pinning one WIT version across the fleet

Figure 4: Adoption decision flow. Cross-language composition is the deciding requirement; the deployment target then selects the shim, the embedded host or the microcontroller runtime, and all paths converge on pinning one WIT version.

Where to run it: three deployment shapes

Embedded in an agent. Your edge agent, written in Rust, links Wasmtime as a library and loads components from disk or a registry. This is the lowest overhead shape and gives you full control of capabilities and limits. It also makes you responsible for runtime patching. It fits gateways where you already ship your own agent binary.

Kubernetes with a containerd shim. Projects such as runwasi and SpinKube let Kubernetes schedule Wasm workloads through a RuntimeClass, so a Wasm pod sits alongside container pods on a K3s or full Kubernetes node. Our SpinKube tutorial covers the operator workflow. The advantage is reuse of your GitOps, observability and admission tooling; the cost is that the shim version, not just the Wasmtime version, determines which WASI features you get. Verify the shim’s bundled runtime before assuming 0.3 support.

Distributed actors. wasmCloud places components on hosts across clusters and edge sites and links them by interface rather than by network address. Our wasmCloud component model architecture article goes deeper. The 2.5 release’s implements feature, which lets one component import the same interface several times under different names with separate configuration, is directly useful for edge cases such as one component talking to two databases with different roles.

A migration path that does not break the fleet

The mechanical edits are small; the risk is in the toolchain. I would run the migration in four stages.

  1. Inventory your imports. Grep your WIT worlds for wasi:io. Every use is a rewrite, not a version bump, because there is no wasi:io 0.3.0.
  2. Pick one WIT version and pin it everywhere. The WASI documentation recommends that the runtime and the bindings tools (wit-bindgen for Rust, jco for JavaScript and so on) target the same WIT version, 0.3.0. Some tools do not yet support version-aware linking through canonical interface names, so a mismatch shows up as a confusing “wrong type” error at instantiation rather than a clear version error.
  3. Port the I/O edges first. Replace start-connect/finish-connect pairs with connect: async func, convert stdout writes to the pass-a-stream-in pattern, and port HTTP handlers from the incoming-request and response-outparam pair to a single async handler returning a result. Note the guide’s caution that not every two-step call becomes async func; bind and listen may become plain functions.
  4. Run both component generations side by side. Use wasmtime serve or your orchestrator’s dual support to canary 0.3 components against 0.2 ones before retiring the old ones.

The toolchain minimums in the migration guide are worth recording, with the caveat that they will move: Wasmtime 46 or later for default-on 0.3, wit-bindgen 0.46 or later with the async feature, wkg 0.15 or later to fetch wasi:cli@0.3.0, and, for Rust, a nightly toolchain at the time of writing because the stable wasm-component-ld was too old for the generated output. Re-check these on the day you start.

Observability and operations

A component graph is harder to debug than a single process because each stage is opaque to the others. Three practices help. Propagate a trace identifier through every request and stream wrapper so OpenTelemetry spans survive the component boundary; the host can add spans around each import call, which gives you a trace of capability use for free. Export per-component counters for instantiation time, fuel or epoch traps, and memory high-water marks from the host. And log the component digest and WIT version alongside every incident, because “which build was running” is a harder question when stages update independently.

Async call stacks are now exposed through Wasmtime’s public API as of version 46, according to its release notes, which will make host-side stack capture for stuck tasks easier to build. The Wasmtime 46 release also changed the default garbage collector to the copying collector and raised the minimum Rust version to 1.94.0 for compiling the runtime. Neither affects most users, but if you build Wasmtime from source in a pipeline, the toolchain bump will.

Testing component contracts

Because components are linked by WIT contracts, contract tests give you more value than end-to-end tests. Compose each stage with a fake neighbour: feed the transform component a synthetic stream of readings, assert on its output stream, and assert that the outcome future reports success when the stream completes and an error when you inject a fault upstream. Then run the shared wasi-testsuite-style checks for any host capability you implement yourself. Because the outcome future resolves independently of how much of the stream is consumed, you can test early-close behaviour deterministically, which was awkward in 0.2.

Trade-offs, Gotchas, and What Goes Wrong

None of this removes the old trade-offs; it moves them.

Ecosystem immaturity is the main risk. Per the launch article, guest toolchain support for several languages was still in progress when 0.3 was ratified, and projects were to announce support over the following weeks and months. Rust and JavaScript are furthest along. If your team writes Python or C#, budget for toolchain churn, and do not commit a production roadmap to a language binding that has not shipped.

Version skew is easy to cause and hard to read. Wasmtime 41 to 45 implemented release-candidate snapshots (0.3.0-rc-2026-01-06 and 0.3.0-rc-2026-03-15) and require explicit flags. A component built against a candidate snapshot is not guaranteed to run on the final spec, so rebuild rather than assume. Wasmtime 44’s wasi:tls support is a draft (0.3.0-draft), so do not build a security boundary on it yet.

Cancellation and timeouts are open design areas. The roadmap lists the error-context type, threading built-ins, stackful async lift and fixed-length lists as candidate features needing future Subgroup votes. A long-running async task that holds a stream open needs a host-enforced deadline today, which means epoch interruption on your side. Do not rely on guests to behave.

Back-pressure can look like a hang. Because owned streams propagate slowness upstream, a stalled consumer will stall the driver. That is correct behaviour, but at the edge it surfaces as “the sensor stopped reporting.” Put bounded buffers and explicit drop policies at the edges of the graph and alarm on queue depth.

Not every workload fits. Components shine for I/O-bound glue and policy. They are a poor fit for heavy GPU inference, for workloads that need arbitrary native libraries, or for code that depends on threads and shared memory beyond what the current proposals offer. In my opinion, the sensible edge architecture in 2026 is hybrid: containers for the heavy, stateful services, and components for the many small, untrusted or frequently changing stages around them. If you are weighing server-side Wasm against containers more broadly, our ADR on the component model versus containers covers the cost side.

Unverified performance claims abound. Cold start, density and in-process chaining figures circulate widely, and many are unsourced. I found one popular 2026 article that presented “100 to 500 times faster” start-up with no cited dataset and internally inconsistent figures. Run your own benchmark and publish the methodology if you intend to make the claim.

Practical Recommendations

If you are starting a new edge project today, build on 0.3. The specification is ratified, the reference runtime enables it by default, and there is a published patch cadence. If you maintain a 0.2 estate that works, do not migrate for its own sake: 0.2 keeps running, and the polyfill path means you can move on your schedule.

Choose the host by the constraint that dominates. If you control the agent, embed Wasmtime and own the limits. If you already run Kubernetes at the edge, use a shim or SpinKube and verify its bundled runtime. If you need to place components across many sites by interface, evaluate wasmCloud. In every case, treat capability declarations as security policy and review them as such.

A short checklist for the first production rollout:

  • [ ] Pin one WIT version (0.3.0 or later) in every build and runtime.
  • [ ] Record the Wasmtime or shim version and component digest per node.
  • [ ] Remove all wasi:io imports and Krustlet references.
  • [ ] Configure epoch deadlines, memory limits and a pooling allocator for every guest.
  • [ ] Add bounded buffers and drop policies between stages; alarm on queue depth.
  • [ ] Test stage contracts with fake neighbours, including early stream close.
  • [ ] Subscribe to Wasmtime security advisories and rehearse a patch within days.
  • [ ] Re-verify guest toolchain support for each language before committing to it.

Finally, keep the architecture honest about what is measured and what is claimed. Where this article cites a figure, it comes from a named source; where it describes mechanism, treat it as a hypothesis to check on your hardware.

Frequently Asked Questions

Is WASI Preview 3 stable in 2026?

Yes. WASI 0.3.0 was ratified by the WASI Subgroup and released on June 11, 2026, with 0.3.1 following on August 11, 2026. The Bytecode Alliance describes it as a stable release, and patch releases are planned about every two months. Some guest language toolchains and individual interfaces, such as the wasi:tls draft, are still maturing, so stable describes the specification rather than every tool built on it.

What is the difference between WASI 0.2 and WASI 0.3?

WASI 0.2 introduced the Component Model and WIT but handled asynchrony through the wasi:io package, whose pollables could not be forwarded across components. WASI 0.3 removes that package and adds async func, stream<T> and future<T> to the canonical ABI, so a host-managed scheduler drives all components. It also restructures wasi:http, wasi:sockets, wasi:cli, wasi:filesystem and wasi:clocks.

Do I have to migrate my 0.2 components?

No. The Component Model documentation describes 0.3 as additive, and Wasmtime’s wasmtime serve runs both 0.2 and 0.3 components, falling back to the older wasi:http/proxy world for 0.2. You migrate when you need composable async across components, the new service and middleware worlds, or support from a toolchain that has moved to 0.3. Do it incrementally, starting with I/O edges.

Which runtime should I use for WASI 0.3 at the edge?

Wasmtime 46 or later is the reference choice: it enables WASI 0.3 and component-model-async by default. wasmCloud 2.5 builds on Wasmtime 46 and turns WASI P3 on by default, and Spin 4.x tracks newer Wasmtime releases. For other runtimes, verify the exact feature set before committing, because support claims change quickly and I could not confirm every runtime’s status.

Is WebAssembly faster to start than a container?

Mechanically, usually yes. A precompiled component is instantiated from a pooled allocator without creating a process or reading a root filesystem, while a container must set up namespaces, cgroups and a filesystem. The size of the gap depends on runtime, hardware and your own initialisation code, and many published numbers are unsourced. Measure start-up percentiles for your own component on your own edge hardware.

Can I run WebAssembly components on Kubernetes?

Yes. Projects such as runwasi and SpinKube let containerd run Wasm workloads through a RuntimeClass, so they schedule beside containers. Krustlet, an older Kubernetes-native approach, was archived in February 2025 and should not be used. Check which Wasmtime version your shim bundles, because that determines whether WASI 0.3 is available to your pods.

Further Reading

By Riju — about

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

Your email address will not be published. Required fields are marked *