You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I'd like to know if there's any appetite for a wasi:exec. My immediate use cases is to run existing multi-binary tools (compilers). It would also be nice to be able to sandbox a whole userspace on a local machine (so, run a shell, allow programs to start other programs) and/or to get a Docker-on-wasm flow working.
I think exec fits in nicely with the other wasi proposals (sockets, cli, files, etc.) and is I think the biggest missing feature in running wasi programs on a local machine (and perhaps maybe file locking?).
Some design questions to consider:
Should exec run arbitrary programs, or just wasm components? I think from a security point of view, only wasm components would make sense.
What permissions should a launched program run with? I would be tempted to say, default none, and there'd be some configuration object you can call grant functions on. That way the API is extensible for future runtime abilities.
How should subprocesses interact? What happens with stdout, etc.? I guess the most common APIs are to either inherit stdin/stdout, route to null, or to have a pipe. All seems valuable and doable.
What other methods should there be? I think kill (exposing perhaps some basic signals) and wait?
Where do the programs come from? From the file system seems nice, but what if you want to bundle binaries in your component? I think just files is the way to go, to mirror OS exec.
The alternative to exec I can imagine is a high-level instantiate API that can run arbitrary components (be it wasi:cli commands, or some HTTP handler, or anything). You could implement exec on top of that, but it would be hairy, especially for interposing files/sockets/etc. Because of that complexity I think both wasi:exec and instantiate could very reasonably coexist.
Personally I'd like to see this exist in one shape or another. I think this would be a useful building block for porting already-written code to work with wasm/WASI/etc. I think there's a certain class of programs that are probably going to be quite difficult to get working as-is, for example things juggling CLOEXEC or passing handles to child processes, but targeting the rough intersection of Windows/Unix process spawning I feel would still be a useful middle-ground.
API-wise I'm personally fond of std::process::Command in Rust, although I'm biased as I helped design it.
Semantics-wise I agree one of the biggest questions here is "only wasm or also native?" I'd throw out a possible idea there where the spec is such that the runtime gets to be the arbiter. For example in wasmtime we might want to, by default, only allow execution of wasm but maybe a flag would opt-in to being able to run native programs. The benefit of executing wasm is we can maintain the sandbox as-is (e.g. preopen directories, etc), but with native we effectively cannot do that so the filesystem-view of a native program is possibly quite different than that of a wasm program. This may also mean that wasi:filesystem needs to be sorted out first w.r.t. multiple preopens or just one root filesystem.
Should exec run arbitrary programs, or just wasm components?
The ability for a guest to spawn arbitrary host processes is extremely powerful. At the same time, it is effectively the WASI equivalent of a sandbox escape with arbitrary code execution. Many current embedders chose WebAssembly specifically for its security properties, so executing arbitrary host code is a hard pass for them.
Starting with pure Wasm components seems like the most sensible approach to me.
In the long run, runtime instantiation is likely the desired end state. That model gives the caller full control over how each import of the child instance is satisfied.
In the meantime, WASI could provide an API that already moves in that direction. I am thinking of some API where the caller provides the imports of the wasi:cli world as parameters. For example:
Once runtime instantiation becomes available, this API should be fully implementable in terms of it. The child would run entirely inside the same sandbox as the caller, and it would not gain access to any resources the parent could not already access.
there'd be some configuration object you can call grant functions on
That raises an interesting design question. All WASI proposals are intentionally independent from each other. Introducing a single monolithic configuration object would break composability across proposals.
It might be possible to sidestep that problem entirely. A initial use case here is to make "lift and shift" migrations of Unix-like applications easier. In that context, granular capability attenuation is probably not the highest priority.
For configuration, we could start with a simple choice: either inherit all capabilities (filesystem, sockets, random, clock, HTTP, etc.) or inherit none of them. More fine-grained control could then be deferred until runtime instantiation becomes available.
Where do the programs come from? From the file system seems nice, but what if you want to bundle binaries in your component?
From a WASI perspective, the simplest answer is: any stream<u8> will do.
Some embedders precompile their components and maintain a strict separation between compilation and instantiation. An API in this space may therefore need to account for that distinction as well.
I think there's a certain class of programs that are probably going to be quite difficult to get working as-is, for example things juggling CLOEXEC or passing handles to child processes, but targeting the rough intersection of Windows/Unix process spawning I feel would still be a useful middle-ground.
Yeah. I think porting a shell or some processes that communicate over pipes will be difficult. The libc simulation only gets you so far, and the spawned child can't really be expected to recreate the fake file descriptor table of its parent. I'd want to think about pipes as well but that seems similarly gnarly. I want to claim that stdin/stdout/stderr is good enough. If you want to redirect those, the parent could do that internally. Not ideal, but workable.
Maybe supporting an interrupt signal is important as well, but that'd need an equivalent in the cli package.
API-wise I'm personally fond of std::process::Command in Rust, although I'm biased as I helped design it.
I think this is similar enough to other high-level sub-process APIs look like in python, go, etc. that it hopefully works out.
That raises an interesting design question. All WASI proposals are intentionally independent from each other. Introducing a single monolithic configuration object would break composability across proposals.
Ah, tricky. Exec doesn't make sense to me without a filesystem, since the main purpose in my mind is to run existing tools that would be hard to change or refactor to either embed their dependencies (compilers) or that want to invoke different binaries (shell). Both are going to want to use the filesystem, and I'd want an API for setting the working directory, so I think I'd be okay with a wasi:exec dependency on the filesystem. That also means the binary to run can be a path, which at least leaves the door open for running non-wasi-components, and for pre-compiling/caching.
For (potential) configuration dependencies, maybe there's an external wit that imports both socket and exec to let you configure a command with socket permissions? Although I think I'd prefer postponing that idea altogether since existing subprocess APIs don't have this kind of granular sandboxing anyway.
Starting with pure Wasm components seems like the most sensible approach to me.
I think this feature and dynamically loading WASI components are quite different and should essentially be handled completely separately. My particular need is writing scripts that coordinate several native binaries - and porting existing scripts from Node and Python. There's nothing much Wasm-specific in there, it's just that my coordination language runs via Wasm.
The only immediate workaround seems to be wrapping the wasmtime library to add a custom import...
One big half of this problem is wanting to spawn another component. This is best covered upstream in the component model under the runtime instantiation feature (WebAssembly/component-model#423).
We generally agree that there is a desire to want to be able to do native process / posix style process spawn. That's essentially what we're going to leave this issue open to track (posting from WASI SG triage).
Activity
jellevandenhooff commentedon Mar 7, 2026
This is essentially #414 but perhaps with some new motivation.
alexcrichton commentedon Mar 9, 2026
Personally I'd like to see this exist in one shape or another. I think this would be a useful building block for porting already-written code to work with wasm/WASI/etc. I think there's a certain class of programs that are probably going to be quite difficult to get working as-is, for example things juggling
CLOEXECor passing handles to child processes, but targeting the rough intersection of Windows/Unix process spawning I feel would still be a useful middle-ground.API-wise I'm personally fond of
std::process::Commandin Rust, although I'm biased as I helped design it.Semantics-wise I agree one of the biggest questions here is "only wasm or also native?" I'd throw out a possible idea there where the spec is such that the runtime gets to be the arbiter. For example in
wasmtimewe might want to, by default, only allow execution of wasm but maybe a flag would opt-in to being able to run native programs. The benefit of executing wasm is we can maintain the sandbox as-is (e.g. preopen directories, etc), but with native we effectively cannot do that so the filesystem-view of a native program is possibly quite different than that of a wasm program. This may also mean thatwasi:filesystemneeds to be sorted out first w.r.t. multiple preopens or just one root filesystem.badeend commentedon Mar 9, 2026
Some thoughts:
The ability for a guest to spawn arbitrary host processes is extremely powerful. At the same time, it is effectively the WASI equivalent of a sandbox escape with arbitrary code execution. Many current embedders chose WebAssembly specifically for its security properties, so executing arbitrary host code is a hard pass for them.
Starting with pure Wasm components seems like the most sensible approach to me.
In the long run, runtime instantiation is likely the desired end state. That model gives the caller full control over how each import of the child instance is satisfied.
In the meantime, WASI could provide an API that already moves in that direction. I am thinking of some API where the caller provides the imports of the
wasi:cliworld as parameters. For example:Once runtime instantiation becomes available, this API should be fully implementable in terms of it. The child would run entirely inside the same sandbox as the caller, and it would not gain access to any resources the parent could not already access.
That raises an interesting design question. All WASI proposals are intentionally independent from each other. Introducing a single monolithic configuration object would break composability across proposals.
It might be possible to sidestep that problem entirely. A initial use case here is to make "lift and shift" migrations of Unix-like applications easier. In that context, granular capability attenuation is probably not the highest priority.
For configuration, we could start with a simple choice: either inherit all capabilities (filesystem, sockets, random, clock, HTTP, etc.) or inherit none of them. More fine-grained control could then be deferred until runtime instantiation becomes available.
From a WASI perspective, the simplest answer is: any
stream<u8>will do.Some embedders precompile their components and maintain a strict separation between compilation and instantiation. An API in this space may therefore need to account for that distinction as well.
jellevandenhooff commentedon Mar 9, 2026
Yeah. I think porting a shell or some processes that communicate over pipes will be difficult. The libc simulation only gets you so far, and the spawned child can't really be expected to recreate the fake file descriptor table of its parent. I'd want to think about pipes as well but that seems similarly gnarly. I want to claim that stdin/stdout/stderr is good enough. If you want to redirect those, the parent could do that internally. Not ideal, but workable.
Maybe supporting an interrupt signal is important as well, but that'd need an equivalent in the cli package.
I think this is similar enough to other high-level sub-process APIs look like in python, go, etc. that it hopefully works out.
Ah, tricky. Exec doesn't make sense to me without a filesystem, since the main purpose in my mind is to run existing tools that would be hard to change or refactor to either embed their dependencies (compilers) or that want to invoke different binaries (shell). Both are going to want to use the filesystem, and I'd want an API for setting the working directory, so I think I'd be okay with a wasi:exec dependency on the filesystem. That also means the binary to run can be a path, which at least leaves the door open for running non-wasi-components, and for pre-compiling/caching.
For (potential) configuration dependencies, maybe there's an external wit that imports both socket and exec to let you configure a command with socket permissions? Although I think I'd prefer postponing that idea altogether since existing subprocess APIs don't have this kind of granular sandboxing anyway.
justinfagnani commentedon May 5, 2026
I think this feature and dynamically loading WASI components are quite different and should essentially be handled completely separately. My particular need is writing scripts that coordinate several native binaries - and porting existing scripts from Node and Python. There's nothing much Wasm-specific in there, it's just that my coordination language runs via Wasm.
The only immediate workaround seems to be wrapping the wasmtime library to add a custom import...
ricochet commentedon Oct 1, 2026
One big half of this problem is wanting to spawn another component. This is best covered upstream in the component model under the runtime instantiation feature (WebAssembly/component-model#423).
We generally agree that there is a desire to want to be able to do native process / posix style process spawn. That's essentially what we're going to leave this issue open to track (posting from WASI SG triage).