While the framework builds off of itself, it is not all-or-nothing. You‘re free to use some components if parts of the overall runtime model don’t align with your particular use case.
Table of Contents
[Wassh] is the project for building OpenSSH for WASM & WASI. As part of the development, we found that the WASI project lacked any real JS binding support. There is generated emscripten code, but that was built once a while ago, and hasn’t been updated since. Which means wassh will need to develop its own JS runtime.
As we fleshed things out, we recognized that a lot of the framework is not specific to wassh and could be reused by other WASM applications that want to run on the web. The market here is probably not significant (compared to e.g. emscripten), but it’s also probably not nothing, and we already need to do the majority of the work regardless. Most notably, the fact that WASM/WASI require all syscalls be handled synchronously while the JS world (and many of its APIs) can only be satisfied asynchronously.
Writing JS support code is basically writing an OS. The WASM code uses the WASI C library which makes syscalls to the JS world, and the JS world manages all the standard OS state (open files, etc...) while servicing the syscall requests.
As WASI is still under development, we often require current browser runtimes. No attempt is made to provide backwards compatibility or transpiling.
These framework APIs are generally the ones we expect people to use. There are some more utility/internal APIs available if desired; see the API Reference for more details.
The SyscallHandler API is really how you bind your JS world to the WASM world. It is responsible for actually handling the syscalls via whatever unique state or paradigms used in your JS application.
For simple programs, it is expected that people will use Process.Foreground and SyscallEntry.WasiPreview1 and SyscallHandler.DirectWasiPreview1 APIs unmodified, perhaps with their own additional SyscallHandler class for things DirectWasiPreview1 does not support.
For complicated programs, it is expected that people will use Process.Background SyscallEntry.WasiPreview1 and SyscallHandler.ProxyWasiPreview1 and SyscallHandler.DirectWasiPreview1 APIs unmodified, and provide their own Worker and SyscallHandler implementations to round things out.
Check out [html/example.html].
At a very high level:
WASM -> SyscallEntry -> SyscallHandler -> -> SyscallHandler -> SyscallEntry -> WASM
They use a consistent naming convention like
sys_<WASI syscall name>, so the
fd_write WASI API syscall will be implemented here as
Once the WASI arguments have been unpacked,
handle_<WASI syscall name> will be called in the SyscallHandler implementation. The APIs will often be simpler than the WASI API, so consult the API Reference section below.
In cases of errors, the errno value will always be returned directly. If cases of success, the handler will either return ESUCCESS (for simpler syscalls, or when outputs are via arguments), or return an object for more complicated outputs in which case ESUCCESS is always assumed.
SyscallHandler classes provide the actual implementation of syscalls. They use a consistent naming convention like
handle_<WASI syscall name>, so the
fd_write WASI API syscall will be implemented as
handle_fd_write, although the function signature will be different. Consult the API Reference section below for the exact arguments.
There are two major variants: direct & proxied. Direct handlers must be synchronous as the WASM runtime does not support asynchronous calls. Proxied handlers may be asynchronous as they run in a different thread -- they may be marked async and/or return Promises that resolve to the right value (NB: in case of errors, return appropriate errno values instead of rejecting the promise).
Replace with generated docs?
See the common libapps HACK.md for details.