# Lifo reimplements a Unix shell on top of the browser

> Lifo is a TypeScript reimplementation of a Linux-like operating system where the JavaScript runtime acts as the kernel and browser APIs stand in for system calls, with 60 plus commands, a bash-like shell and an IndexedDB-backed virtual filesystem. It runs in a browser tab, a Node process or a serverless function, and it is a teaching and prototyping system rather than a Unix replacement.

**lifo-sh/lifo** — A browser-native operating system. Unix/Linux reimagined where the browser IS the kernel and Web APIs ARE syscalls. 60+ commands, bash-like shell, virtual filesystem, and IndexedDB persistence.

- Repository: https://github.com/lifo-sh/lifo
- Website: https://lifo.sh
- Stars: 543 · Forks: 37
- Language: TypeScript
- License: not declared
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/lifo-sh-lifo

## The JavaScript runtime is the kernel

Lifo is described as a tiny Linux-like operating system written in TypeScript that runs anywhere JavaScript does, in a browser tab, a Node.js process or a serverless function. The framing in the README is explicit that this is not a virtual machine and not an emulator, but a clean-room reimplementation of Unix in TypeScript where the JS runtime is the kernel and its APIs are the system calls. The layered diagram in the README makes the architecture legible from the top down: a terminal UI in xterm.js with the Tokyo Night theme, a bash-like shell interpreter, a command registry that acts as a PATH, then a row of coreutils, network commands and user packages where each command is an async function, a Node.js compatibility layer of thin wrappers over OS APIs, the virtual filesystem with in-memory and IndexedDB persistence, a kernel API layer of unified browser API wrappers, and at the bottom the browser APIs themselves including fetch, streams and OPFS. Every layer above the browser is written in the same language and the same process, which is the whole trick.

## The virtual filesystem and its POSIX pretensions

The kernel section describes a virtual filesystem built as a synchronous in-memory INode tree with what the README calls full POSIX-like semantics: read, write, stat, mkdir, symlinks, hard links and permissions. Persistence is IndexedDB-backed, with serialization and deserialization of the entire INode tree. Above the ordinary directories sit virtual providers, so /proc exposes system information such as uptime, meminfo, cpuinfo and version, and /dev provides device files for null, zero, random and urandom. The hierarchy in the README lays out the tree with home/user as $HOME, tmp as in-memory temporary space, etc for configuration, var/log for logs, usr/bin for installed package binaries, the two virtual directories, and bin for the core commands. The honest caveat is that POSIX-like is doing a lot of work in that sentence. The semantics are a reimplementation, not the behaviour of a POSIX kernel, and a program written for a real shell will find the differences.

## The shell is a real parser, not a command splitter

The shell section describes a full lexer, parser and interpreter pipeline that produces an AST, and that single phrase explains why the feature list is as long as it is. From an AST you get pipes such as ls | grep foo | wc -l, redirects including >, >>, <, 2> and &>, the logical operators && and ||, sequences with ;, and background execution with &. Expansion covers variables in both $VAR and ${VAR:-default} forms, command substitution with $(...), glob patterns including the single-star and recursive double-star forms, tilde expansion, and brace expansion with {a,b,c}. On top of that sit tab completion for commands, files and directories, command history with reverse search, job control through Ctrl+C, fg, bg and jobs, and builtins including cd, pwd, export, alias, source, read, test and echo. This is the part of Lifo that most closely tracks a real shell, and it is where the effort clearly went.

## Sixty plus commands, each an async function

The command table is organised into six categories and each command is an async function registered in an ES module command map. Filesystem commands run to twenty entries, including ls, cat, cp, mv, rm, mkdir, rmdir, touch, ln, stat, find, tree, du, df, chmod, file, basename, dirname, realpath and mktemp. Text processing covers grep, sed, awk, head, tail, sort, uniq, wc, cut, tr, diff, nl and rev. I/O adds printf, tee, xargs and yes, the network category has curl, wget, ping and dig, and the system category runs to ps, top, kill, env, uname, whoami, hostname, uptime, free, date, cal, bc, sleep, watch, which, man and help. Archives are tar, gzip, gunzip, zip and unzip. The runtime category is the interesting one, with a node command that runs JavaScript through the Node compatibility layer and a pkg command that acts as the package manager. Since these are async functions, a command can await, which is not something a synchronous POSIX tool can do.

## Fifteen Node modules shimmed onto browser APIs

The Node.js compatibility layer lets you run JavaScript files with node script.js or node -e "code" inside Lifo, and the README says it maps fifteen Node.js standard library modules to browser APIs. The list is fs, path, events, buffer, util, os, process, http, child_process, stream, url, timers, crypto and console. The mapping is where the abstraction leaks. child_process in particular has no browser equivalent, and http has to be served by fetch, so what you get is a subset shaped to fit, not a port. The same applies to crypto and to fs, where the file operations land on the virtual filesystem rather than on a disk. The layer is best understood as an adapter that lets familiar scripts start working, and as the exact place where a script written for Node will behave differently once it runs inside Lifo.

## Getting it running and the package manager

The project is a pnpm workspace, and the getting started steps are short:

```bash
pnpm install
pnpm dev
```

After that you open http://localhost:5173 in a modern browser and land in a working terminal. The other scripts are the usual three:

```bash
pnpm build        # Production build
pnpm test         # Run test suite
pnpm typecheck    # Type check without emitting
```

Packages are managed from inside the shell with a pkg command that installs from a URL:

```bash
pkg install <url>    # Install a package from URL
pkg remove <name>    # Uninstall a package
pkg list             # List installed packages
pkg info <name>      # Show package details
```

The root package.json shows the workspace shape, with packages/* and apps/* as the workspaces, and it lists a build script that compiles a dozen separate packages in sequence, including @lifo-sh/core, a UI package, a CLI package, and package plugins named for git, ffmpeg, fastfetch, nano, less, vi, cal, bc and man. Those plugin names are a good hint at the direction of travel, since they cover an editor, a pager, a calculator and a manual reader. The root package is still named rapidos, which suggests the project was renamed and the root manifest has not caught up.

## Where Lifo is the wrong tool

Three cases, and the first is the important one. Lifo is not a POSIX environment, so shell scripts written for a real system will encounter differences in the corners: no true process isolation between commands since they are async functions in one runtime, no signals beyond what the shell fakes, no real file permissions enforced by a kernel, and no meaningful difference between a command and a library call. The second case is anything requiring real isolation, since the sandbox is a nice feature for untrusted code but the security boundary is the browser's own, and a shell that has curl and pkg install can fetch and run code from a URL. The third is production tooling, because a project with no GitHub releases, a ROADMAP.md and a UI_ROADMAP.md in the tree, and a plan file, is signalling where it is in its life. Against a hosted Linux container, Lifo's difference in approach is that there is no container to run, no image to pull and no cost per session, and everything lives in the tab or the function.

## Conclusion

Lifo is a good fit for a developer who wants to teach Unix concepts, prototype a CLI that will run in a browser, or give a sandbox to code that has no business touching a real filesystem, since the whole thing is a self-contained TypeScript program with no host privileges involved. It is a poor fit for production tooling, for anything that needs real POSIX semantics or real process isolation, and for a team that needs a stable published package, because the repository publishes no releases and the top-level package is still named rapidos. Check three things before you build on it: that the browser targets in the README, Chrome 110 and newer, Firefox 110 and newer and Safari 16.4 and newer, are a floor you can live with, that you understand the persistence model, since the filesystem is in memory with the whole INode tree serialized to IndexedDB, and that the Node compatibility layer is the boundary you are willing to work inside, because it maps fifteen standard library modules onto browser APIs and everything beyond that is a shim rather than Node itself.

## FAQ

### What is Lifo and how is it different from a virtual machine?

Lifo is a TypeScript reimplementation of a Linux-like operating system that runs in a browser tab, a Node.js process or a serverless function. The README says it is not a VM and not an emulator, but a clean-room reimplementation of Unix in TypeScript where the JavaScript runtime is the kernel and its APIs are the system calls.

### How do I run Lifo locally?

Run pnpm install and then pnpm dev, and open http://localhost:5173 in a modern browser to reach the terminal. Other scripts are pnpm build for a production build, pnpm test for the test suite and pnpm typecheck for a type check without emitting.

### How does Lifo store files?

The virtual filesystem is a synchronous in-memory INode tree with read, write, stat, mkdir, symlinks, hard links and permissions, and persistence is IndexedDB-backed with serialization of the entire tree. The /proc and /dev directories are virtual providers for system information and device files.

### Which Node.js modules can I use inside Lifo?

The compatibility layer maps fifteen standard library modules onto browser APIs: fs, path, events, buffer, util, os, process, http, child_process, stream, url, timers, crypto and console. You run JavaScript with node script.js or node -e.

### What browser does Lifo need?

The README lists the target as Chrome 110 and newer, Firefox 110 and newer, and Safari 16.4 and newer. The terminal is rendered with xterm.js and its WebGL addon, and the project uses Vite for builds and Vitest for testing.

## Sources

- [Issues](https://github.com/lifo-sh/lifo/issues)
- [lifo-sh/lifo on GitHub](https://github.com/lifo-sh/lifo)
- [Project website](https://lifo.sh)
- [README](https://github.com/lifo-sh/lifo/blob/main/README.md)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/lifo-sh-lifo
