Host API
The boundary between the RISC-V runner's WebAssembly module and its JavaScript host is small: one shared memory and five function imports, 78 exported functions, and a shared-memory protocol for file I/O.
Inspect the module
Reads the import and export lists straight from /nano/nano.wasm with the WebAssembly JS API.
const mod = await WebAssembly.compileStreaming(fetch("/nano/nano.wasm"));
const imports = WebAssembly.Module.imports(mod);
const exports = WebAssembly.Module.exports(mod);
print(imports.length, "imports:", imports.map((i) => `${i.name}(${i.kind})`).join(" "));
print(exports.length, "exports,", exports.filter((e) => e.kind === "function").length, "functions");
print(exports.filter((e) => e.name.startsWith("vm_")).length, "vm_* exports, e.g.", ["vm_create", "vm_step", "vm_load_elf", "vm_inject_connection"].filter((n) => exports.some((e) => e.name === n)).join(" "));6 imports: memory(memory) abort_js(function) emscripten_date_now(function) emscripten_random(function) debug_log(function) console_write(function) 80 exports, 78 functions 52 vm_* exports, e.g. vm_create vm_step vm_load_elf vm_inject_connection
Imports
memoryWebAssembly.Memoryconsole_write(fd, ptr, len)debug_log(val)trace builds)abort_js() → !emscripten_random() → f32getrandomemscripten_date_now() → f64clock_gettime and timersExports
vm_create, vm_init, vm_step, vm_load_elf, vm_load_raw, vm_exit_code, vm_signalvm_ram_ptr, vm_ram_size, vm_struct_ptr, vm_struct_size (12,680 bytes)vm_fs_request_ptr, vm_fs_response_ptr, vm_fs_path_ptr, vm_fs_path2_ptr, vm_fs_path_cap, vm_thread_fs_request_ptr, vm_thread_fs_response_ptr, vm_io_retry, vm_note_host_writevm_alloc_thread, vm_thread_step, vm_fork_return, vm_shared_efd_ptr, vm_epoll_timeout_msvm_inject_connection, vm_read_response, vm_close_connection, vm_sockets_ptr, vm_sockets_sizeterm_feed, term_reset, term_cells_ptr, term_cols, term_rows, term_cursor_row, term_cursor_col, …, vm_stdin_push, vm_stdin_eof, vm_stdin_set_interactive, vm_tty_enable, vm_tty_set_sizevm_evloop_dump, vm_evloop_load, vm_evloop_size, vm_signals_dump, vm_signals_load, vm_snapshot_restore_reset, vm_blocks_ptr, vm_blocks_size, vm_reset_blocksvm_bundled_busybox_ptr/_size, vm_bundled_node_ptr/_size, vm_bundled_devenv_ptr/_size, vm_bundled_elf_ptr/_size (0 when not embedded)debug_pc, debug_reg, debug_status, debug_read_guest, debug_fault_pc, debug_fault_addr, debug_block_*, debug_baseline_insns, …malloc, free (bump allocator; free is a no-op)The FS_PENDING protocol
File syscalls don't call the host per operation. The guest fills a request block (syscall number, fd, three arguments, buffer pointer and length, two 256-byte paths), sets the VM status to 6 (FS_PENDING) and returns from vm_step. The host performs the operation on the in-memory filesystem, writes the result into register a0, resets the status to 0 and calls vm_step again.
// host loop, simplified
for (;;) {
const status = step(vm, budget);
if (status === FS_PENDING) { completeFsRequest(vm); continue; } // 6
if (status === EPOLL_BLOCKED) { setA0(vm, -EINTR); await tick(); continue; } // 7
if (status === FAULT) break; // 3: exit or trap
}-EINTRThe RISC-V emulator feature page shows the runner in action. The guest-memory layout and the 12,680-byte VM struct are on RISC-V runner. Most apps never touch this layer; the SDK and the raw API wrap it.