← winix

API reference

winix runs a Linux kernel and its userland in the browser. This page describes the JavaScript API (winix.js), the curl command inside the guest, and the .winix session format. The reference matches the source as of this release.

Boot

import { Winix, proxyFetch } from './winix.js';

const w = await Winix.boot({ fetch: proxyFetch() });

Winix.boot(options) starts a machine and resolves to a Winix instance. In our tests, boot took under a second. If the kernel panics during boot, it is retried a few times.

OptionDefaultMeaning
baseUrlthe module's folderWhere the kernel, initramfs and linux.js are loaded from.
variant'wasm32_nommu'Build variant. Only this one is shipped.
onLognoneReceives host diagnostics (boot messages and runtime events).
onConsolenoneReceives the guest console text, as it is printed.
cmdlinebuilt-inKernel command line.
fetchthe page's fetchFunction used for every HTTP request the guest makes. See Network.

exec

const r = await w.exec('echo hi; ls /bin | wc -l', { cwd: '/work', env: { FOO: '1' }, timeoutMs: 60000 });
// r = { stdout, stderr, exitCode, timedOut, truncated, durationMs }

exec(command, options) runs command with sh -c in the guest and resolves to:

FieldMeaning
stdout, stderrOutput as UTF-8 text.
exitCodeExit status. -1 if the machine stopped answering.
timedOuttrue if the command ran past timeoutMs. Its process group is killed.
truncatedtrue if output was longer than maxOutputBytes. The output is cut, not the command.
durationMsWall-clock time for the call, in milliseconds.
OptionDefaultMeaning
cwd'/work'Working directory.
env{}Environment variables. Names must match [A-Za-z_][A-Za-z0-9_]*.
timeoutMs60000Time limit for the command.
maxOutputBytes1048576Output limit for each of stdout and stderr.

Calls run one at a time, in the order they were made. The filesystem persists between calls. Shell variables and working directory changes do not: each call is a new sh -c.

Files

await w.writeFile('/work/a.js', 'console.log(6 * 7)');   // string is written as UTF-8
await w.writeFile('/work/data.bin', bytes, { mode: 0o600 });
const text  = await w.readFile('/work/a.js', { encoding: 'utf8' });
const bytes = await w.readFile('/work/data.bin');           // Uint8Array
MethodMeaning
writeFile(path, data, { mode })Writes a string (as UTF-8) or bytes. Creates parent directories. Default mode 0o644.
readFile(path, { encoding })Returns a Uint8Array, or a string with encoding: 'utf8'.

keyInput

w.keyInput(text) types text into the guest console, as a keyboard would. It is not queued and does not wait for the result. Use exec for anything that needs output. It is for interactive programs (vi, top) and for the terminal page.

Sessions

const blob = await w.exportSession({ hostState: { notes: 'anything JSON' }, onProgress: (p) => {} });

const w2 = await Winix.restoreSession(blob, { fetch: proxyFetch() });
w2.hostState; // the object saved above
MemberMeaning
exportSession({ hostState, onProgress })Saves the root filesystem as a .winix Blob. Queued like exec. hostState is plain JSON that is stored with the session and returned as hostState when it is restored.
Winix.restoreSession(input, { onProgress, ...bootOptions })Validates the archive, boots a new machine, and restores the filesystem. Returns the new instance. Your existing instances are not changed.
hostState (getter)A copy of the host state restored with this session, or null.

A session saves files only. Running programs, shell variables, terminal state, and editor buffers are not saved. Stop background writers and save editors before exporting. The filesystem is not frozen during the save: the save checks it before and after reading and fails if anything changed. Session files can contain secrets such as .env files and keys.

A session can only be restored into the same build of winix. Restoring checks the kernel and initramfs checksums and refuses a different build.

Lifecycle

MethodMeaning
reset()Replaces the machine with a freshly booted one. The filesystem is lost.
dispose()Stops the machine. Later calls fail with Winix disposed.

Network

The guest has no sockets. Every HTTP request it makes is passed to the fetch function you give to boot(). That function decides what happens to the request: it can forward it, log it, or refuse it. Requests are made with the page's origin and browser credentials, so treat that function as a security boundary.

import { Winix } from './winix.js';

const allowed = ['example.com', 'api.example.org'];
const w = await Winix.boot({
  fetch: (url, init) => {
    console.log('sandbox request:', init?.method, url);             // log
    if (!allowed.includes(new URL(url).hostname)) {
      return new Response('blocked by page policy', { status: 403 }); // block
    }
    return fetch(url, init);                                          // allow
  },
});

proxyFetch(prefix) is a ready-made fetch for cross-origin requests. It sends them to prefix + encodeURIComponent(url), which by default is /__proxy?url=. That route exists only on the development server. The GitHub Pages site has no proxy, so cross-origin requests fail there. To run your own, point proxyFetch at it, for example proxyFetch('https://proxy.example/?url='). A proxy that forwards any URL is an open relay, so restrict it to the hosts you need.

curl

Inside the guest, curl performs HTTP and HTTPS requests through the fetch function above. The flags it accepts:

FlagsMeaning
-s, --silent / -S, --show-errorHide progress; show errors when silent.
-f, --fail / --fail-with-bodyExit 22 on HTTP status 400 and above (the second also prints the body).
-i, --include / -I, --headPrint response headers, or send only a HEAD request.
-N, --no-bufferAccepted for compatibility. Response bodies are already written as they arrive, so streaming (for example server-sent events) works without it.
-o FILE, --output FILE / -O, --remote-nameWrite the body to a file, or to a file named after the URL.
-X METHOD, --request METHODHTTP method.
-H HEADER, --header HEADERAdd a request header.
-d DATA, --data, --data-raw, --data-binaryRequest body. @file reads a file, @- reads stdin (not with --data-raw).
-A UA, --user-agent UASet the User-Agent header.
-w FORMAT, --write-out FORMATPrint after the transfer. Supports %{http_code} and \n.
-L, -k, --location, --compressedAccepted. Redirects are always followed.

Anything else is an error. Sockets, ssh, nc, and ping are not available in this release.

Session format

A .winix file is gzip-compressed. Once decompressed, it holds the magic string WINIXS01, a 32-bit little-endian manifest length, the SHA-256 of the manifest, a JSON manifest, and then the bytes of every regular file in manifest order. Checksums detect corruption; they do not prove where a file came from, so restore only archives you trust. Nothing in the archive runs as code: it is validated before any machine starts.

Not yet

These are planned but not part of this release: network sockets inside the guest, w.connect(), and w.ping(). Raw networking needs a kernel build with networking enabled and a process outside the browser to carry the traffic.