Copyright (c) 2022-2024 Antmicro
This project is a wasi
browser runtime that supports wasi_ext_lib api.
JSWASI
is just a kernel -- complete applications that can be served or embedded can be created by providing a root filesystem with wasm32-wasi
executables and configurations.
To build the project, you're going to need to have typescript
installed.
On Debian-like distributions it is as simple as running apt install node-typescript
.
The project can be built in two modes:
- Standalone mode (default):
make standalone
- build the kernel and provide it with a kernel config and a default index with an in-browser terminal emulator that can be used to interface with the app - Embed mode:
make embed
- build just the kernel
Both of these commands produce the output in dist/
directory.
There is also a MINIFY
variable which can be set to 1
in the make
invocation to indicate that the runtime should be built into a single jswasi.js
script.
To build the project this way, you also need to have esbuild
installed.
On Debian-like distributions you can install it by running apt install esbuild
.
Note that the minified version of the project is a script rather than a module.
The project can be run in the standalone mode by serving the dist/
directory with an HTTP server of your choice.
The default kernel config uses rootfs archives built using jswasi-rootfs.
This archive contains some essential executables like:
- wash - shell
- coreutils - basic tools like
ls
orcat
- wasibox - basic tools utilizing wasi_ext_lib, extended WASI standard
as well as some additional programs like:
- ox - a minimal text editor
- space-invaders - a demo terminal space invaders game
- python - a Python interpreter (built using python-wasi)
As for the embed mode, the dist/jswasi.js
module exposes a Jswasi
object that holds functions that allow to control the kernel:
init
: This function starts the runtime and spawns the init system. It optionally accepts aconfig
parameter which can be used to provide kernel configuration.tearDown
: This function accepts one parameter - a print feedback callback. This function can be used to clean all persistent elements of the application without interracting with the kernel in case the kernel enters an unrecoverable state.attachDevice
: Allows to pass an object representing a device to the kernel. This function should be called before callinginit
. Using it to provide devices to a working runtime isn't properly tested and will most likely cause problems.
In the minified mode, the Jswasi
object is defined as a window
object property instead.
The kernel supports a basic configuration that allows to configure the root filesystem and the init system:
init
: a list of arguments to execute the init systemrootfs
: the url of the rootfs archive to usefsType
: the type of the filesystem to mount at/
opts
: options specific to the chosen filesystem.
An example of such configuration file is avaliable in src/assets/config.json
in the project source tree.
If the config
parameter is not given to the init
function, the configuration is read from the fsa0/config.json
file in the device filesystem.
If no such file is in this location, the default configuration from dist/resources/config.json
is going to be saved there.
Virtual filesystem (vfs
) contained in memory and is backed by a fork of js-virtualfs
By default, it is mounted on /tmp
.
Note that currently, this filesystem doesn't work on Firefox.
There is an ongoing effort to fix that.
This filesystem doesn't support any configuration yet.
Fsa filesystem (fsa
) is backed by the Filesystem API and is persistent.
The default kernel configuration mounts this filesystem type on /
.
The following options are available:
name
: Label of the filesystem partition. For instance, this could befsa0
for the partition that holds the kernel config. The default label of the root partition isfsa1
.keepMetadata
: Filesystem metadata is kept in IndexedDB. This option disables saving metadata. It is useful for mounting local directories injswasi
and for mounting corrupted filesystems for recovery purposes.prompt
: Setting this totrue
makes the kernel open a directory picker which allows to choose a local directory to mount.create
: Creates a directory for the partition label if it doesn't exist.
Device filesystem (devfs
) is an extension of the virtual filesystem and supports character devices.
This filesystem doesn't support any configuration options yet.
It is mounted on /dev
by default and contains 4 character devices: null
, zero
, urandom
and ttyH0
.
The first three devices work similarly to it's Linux counterparts.
The last one, ttyH0
, is an interface to the hterm
object (the one passed to the init
function), it can be used to interact with it.
Once attaching multiple terminals to the same kernel is implemented, each of them is going to have it's device analoguous to this one.
This filesystem is rather limited and should not be used in the kernel configuration.
Proc filesystem is now an read only interface to internal kernel structures used for managing processes.
This filesystem doesn't support any configuration options.
It is mounted on /proc
by default and supports process directories and the mountinfo
special file.
For now, it doesn't expose much information.
More special files are going to be implemented soon.
This filesystem is read-only and it's usage is rather limited and cannot be mounted manually.
All of these filesystems, except for proc filesystem, can be mounted using the mount
syscall or wasibox
applet with the same name.
This repository contains two sets of tests: unit tests and syscalls tests.
The first set can be run using make test
command and it tests the integrity of the internal kernel structures.
Note that nodejs
(preferably v18
) is required in order to run the unit tests.
The latter is an userspace rust program that invokes raw syscalls to check whether the kernel responds to them correctly.
Note that for syscalls tests executable to work properly, it needs to be compiled with a custom rust toolchain that implements canonicalize
.
For instructions on how to build such toolchain, see wasi_ext_lib.
These tests can be ran by using the executable as an init system to the kernel or by just executing it using a shell.