GitHub - braamix/core: Browser Runtime As A Machine

GitHub

7 min read Original article ↗

An operating system that runs in a browser tab.

Launch Braam

braamix.github.io — a whole system in under a megabyte, and it is running before you have finished reading this line. Nothing to install, nobody to sign up with, no server anywhere: the page downloads a kernel and gives you a prompt. Your files stay in the browser and are still there tomorrow. It works on a phone.

Braam is a small command-line system: a kernel, a filesystem, a terminal, a shell, fifty-six programs and a package manager. It is written from scratch in C++20 and compiled to WebAssembly, which browsers run at close to native speed. The whole system is a few static files, so any web host can serve it.

Nothing is borrowed — no C library, no Emscripten, no xterm.js. Every program is a separate WebAssembly file running in a sandbox of its own, and none of them lives inside the kernel.

Open the page and there is a prompt:

$ ls /bin                            # every program, one wasm file each
$ echo hello > notes                 # your files survive a reload
$ curl https://example.com | less    # a download, into a pager
$ edit notes                         # ^S saves, ^Q quits
$ spin &                             # a program that loops forever
$ kill %1                            # killed anyway

It is not a Unix clone: no POSIX layer, no fork, and no C library under the system. The terminal is a grid of cells a program fills directly; it also parses the escape sequences a guest sends it (doc/ANSI_Escape_Codes.md), but nothing native uses them. Giving all that up makes the system far smaller and lets every part use what the browser already provides. A program ported from elsewhere may opt into a compatibility kit — doc/Compat.md — but a port is still a rewrite, and nothing in this tree links it.

Three ideas

Programs pause instead of blocking. Anything that would wait pauses, and the browser continues it when the answer arrives. So there are no threads.

The screen is a grid of cells, not a stream of bytes. A colour is a field in a cell and moving the cursor is indexing an array, so there is nothing to parse.

JavaScript never hands data straight back. Answers arrive later through one callback, which keeps the boundary small enough to check by eye.

What is in it

A shell. A Bourne shell after v7: variables, functions, if, loops, case, globbing, command substitution, pipes, redirection, background jobs, and line editing with history. Tab completes a file, a command or a variable, and a second Tab lists the choices. ^C stops whatever is running and gives the prompt back. doc/Shell.md is the manual.

The usual toolbox. grep, find, sort, uniq, wc, head, tail, cut, tr, tee, xargs, seq, du, df, cmp and diff, so a pipeline can be written here rather than somewhere else and pasted in.

A filesystem. Everything lives in the browser's private storage and survives a reload; /tmp is emptied at boot. /bin and /etc come from an archive downloaded with the kernel, and are replaced whenever a new version is opened. /proc shows what is running, and /dev has null, zero, random and urandom. Where the browser will not store files at all, the system says so and stops.

Access to the browser. curl fetches a URL, chat talks over a WebSocket, fimport and fexport move files in and out, and pbcopy and pbpaste reach the clipboard.

Full-screen programs. less and edit draw into a grid of their own and send only the part that changed. ^C still reaches them. A program can also open one of the page's other terminals and paint there — edit -S starts on one screen and edits on another.

A usage block in every program. -h asks one what it takes, and asking is not a mistake: the block goes to the screen and the status is 0.

Isolated processes. Each command runs in a worker of its own, with its own memory and its own open files. One stuck in a loop is killed outright, without having to cooperate — spin exists to show that. The shell is one of these programs, not part of the kernel.

A package manager. pkg installs software that did not ship with the system. A repository is just files on a web server: a signed index and one zip per package, checked against keys that ship inside the boot archive. An install is committed by renaming one symbolic link, so a tab that dies partway has installed nothing. The system ships pointed at https://braamix.github.io, and doc/Package_Formats.md §10 explains running one of your own.

An embedding API. web/braam.js puts a terminal on any web page with mount({ canvas }), and web/embed.html is a working example. web/dual.html splits a window between two screens of one kernel — a shell and a ^C of its own on each, over one filesystem — and web/quad.html is four, which is as many terminals as there can be. A site that boots into one program rather than a prompt names its path in /etc/init, one line in its own boot archive.

Building

You need a clang that can target wasm32, plus CMake, make and Node. On macOS that is brew install llvm lld; on Debian or Ubuntu, apt install clang lld llvm.

make            # build the kernel, the programs and the tests
make run        # run the tests
make serve      # serve the site and open it in a browser
make install    # install the SDK, to /usr/local or ~/.local
make release    # pack the site and the SDK as build/*.zip
make clean

The build leaves a complete website in build/web/. It needs no server program and no special headers, so copying that directory to a web host is a deployment.

Writing a program

Every command is a wasm file, and building one needs nothing private to this repository — make install puts an SDK under /usr/local or ~/.local.

#include "proc/file.h"

Task<i32> proc_main(Args)
{
    co_await write_out("Hello, world!\n");
    co_return 0;
}

write_out is the buffered stream File::stdout(), flushed when the program exits.

find_package(braam REQUIRED)
braam_add_program(NAME hello SOURCES hello.cpp)

A program does not have to be part of the system to run: bring the file in with the browser's file picker and run /import/hello.wasm, or curl it into /home and run it there. doc/Programming_Manual.md is the guide and examples/hello/ is the worked example.

Layout

Directory What is in it
src/kernel/ coroutines, allocator, scheduler, screen, the JavaScript boundary
src/fs/ paths, the mount table, the filesystem over browser storage
src/svc/ URLs, WebSockets, clipboard, file transfer, clock
src/ui/ the layout layer over the grid of cells
src/user/ starting programs, system calls, the console, pipes, /proc, boot
src/proc/ the runtime every program carries
src/compat/ the opt-in kit a ported program may link
src/cmd/ one file per program; sh/ and pkg/ have directories
web/ the page, the workers, the renderer, the browser side of everything
rootfs/ what the boot archive carries
tools/ the build's scripts, and the ones a package publisher signs with
test/ the tests, and a simulated browser to run them against
doc/ the specification, the manuals and the release notes

Documentation

Status

Version 0.9. Everything above works and the tests pass. A tablet works too: tap to type, drag to select, with a row of buttons for the keys a touch keyboard does not have.

Some things are missing on purpose: no bg and no ^Z, no re-wrapping when the window is resized, one program on the screen at a time, no file permissions and no CPU limits — a program can be killed, but not slowed down.

License

MIT. See LICENSE.