Lifo reimplements a Unix shell on top of the browser
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.
At a glance
- What is it?
- 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.
- Who is it for?
- 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.
- Can I use it commercially?
- Not without permission. GitHub finds no licence file in the repository, and without a licence all rights are reserved by default: you may read the code but not reuse it. Check the README, or ask the authors, before using it.
- Is it still maintained?
- Yes. The repository last received commits 5 days ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
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:
pnpm install
pnpm devAfter that you open http://localhost:5173 in a modern browser and land in a working terminal. The other scripts are the usual three:
pnpm build # Production build
pnpm test # Run test suite
pnpm typecheck # Type check without emittingPackages are managed from inside the shell with a pkg command that installs from a URL:
pkg install <url> # Install a package from URL
pkg remove <name> # Uninstall a package
pkg list # List installed packages
pkg info <name> # Show package detailsThe 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.
Editorial 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.
Frequently asked questions
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.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/lifo-sh-lifo)Was this useful?