Any Nix package, live in your browser

πŸš€ Explore this awesome post from Hacker News πŸ“–

πŸ“‚ **Category**:

πŸ’‘ **What You’ll Learn**:

tl;dr Try it at https://trynix.dev. Click hello, or python 3.6.2 from 2017, or two eras of hello at once, or a package that exists in no public cache. A Linux machine boots in the tab and you get a shell with those Nix packages on PATH.

This is my magnum opus of Nix work.

I knew all the ideas I have been creating were building blogs for something greater: nixpkgs-multiverse indexed every version of every package nixpkgs ever shipped, grail taught it version ranges and omniflake allowed adding over sixteen thousand flakes from a single input.

The crazy insight I had lately was the craziness of the β€œfast-mode” of the nixmultiverse.com, which lets you skip evaluation and go straight to the store path.
This lets you leverage the amazingness of Nix without having to deal with
the complexity of evaluation and building. You can just ask for a package and get the exact store path that Hydra built for it, at any version it ever had.

If we have the produced binaries, we can run them. And if we can run them, we can run any of them, in a browser tab, with nothing installed on the host machine.

Welcome to trynix, a browser-based Nix package runner. You can browse the complete history of nixpkgs, over 310,083 package versions, and run any of them11It is a serial console, so nothing graphical. The machine boots to a shell, and you can run any command-line program in the store.Β  in a Linux machine that boots in your tab. It is a Nix store in memory, a Linux kernel in WebAssembly, and a terminal emulator in the page.

This is bonkers! 🀯
We can boot the VM with the store-path closure within seconds. Nothing is pre-installed: search, pick a version, boot, run it.

The craziest part? We are not restricted to the public cache. You can share a store path you built yourself, and anyone can boot it in their browser tab. The only requirement is that the cache is served with access-control-allow-origin: *, which GitHub Pages does for free22I was a little surprised to learn that GitHub Pages can work as a binary cache. It is just a static file server, and it serves access-control-allow-origin: * on every file. That is all that is needed to make a Nix store path available to trynix.Β , so does Cachix and obviously
cache.nixos.org as well.

In a unbelievable twist of fate, I had actually requested 5 years ago for cache.nixos.org to serve access-control-allow-origin: * via issue#156 to make it possible to query the cache from an OpenAPI specification I had implemented. Thank you universe. πŸ™

This link boots a VM with a store-path served from Github Pages of a modified GNU hello.
This is a store path that does not exist on cache.nixos.org and yet it boots in your browser tab.

alt text

Β§Making the pieces fit

Since we can access store-paths from caches that serve access-control-allow-origin: *, that makes the browser a legitimate Nix client.

The missing piece the browser lacked was somewhere to run the binaries since they store-paths are either x86-64 or aarch64 ELF executables.

Standing on the shoulders of giants, we can run a Linux kernel in WebAssembly. This means we can boot a real x86_64 kernel inside our browser tab. Give that kernel a filesystem containing a Nix store. All that’s left knowing which store-paths to fetch, which we beautifully solved with nixpkgs-multiverse. 🀌

trynix cluster_browser your browser tab page the page mv nixpkgs-multiverse attr + version β†’ store path page->mv which path? cache cache.nixos.org narinfo + NARs page->cache closure store nix store in memory vm qemu-wasm x86_64 Linux store->vm 9p term your shell vm->term cache->store unpacked

I have to keep reminding myself: there is no server in the above picture, the web-page is purely static files and everything else is a publicly accessible cache. It is a virtual machine that exists only inside your tab. The ultimate embodiment of Erase your darlings.

Since this is Nix, we get the simplicity of managing multiple versions of the same package. You can boot two versions of hello in one machine, and they will not conflict because each binary names its own dependencies by absolute path (RUNPATH) down to the loader and libc.

Once the VM is already started, you can add more store-paths to it while it’s running. This is no different than adding more paths to your own /nix/store on your laptop. No reboot, or dnf install, or apt-get install, the site fetches the closure and adds it to the store.

Β§It has to feel instant

Booting a kernel under emulation is slow, and despite the amazingness of the idea, no one would use it if it took 30 seconds to get a shell.

The site employs some neat tricks to make it feel instant. The site pre-fetches the engine and the VM snapshot in the background, so by the time you click a link, you have already downloaded it.

The site also never boots the VM from scratch. It resumes. A machine is booted once, ahead of time, on a native build of the same QEMU, and paused at the moment before it mounts the store which is then saved to a snapshot.

Subsequent visits to the site have the engine and snapshot already in the browser cache, so the only thing that has to be fetched is the closure of the store-path you asked for. This makes each subsequent visit feel much faster.

1980-01-01T00:00:00+00:00 image/svg+xml Matplotlib v3.10.5, https://matplotlib.org/

0 2 4 6 8 python3 ripgrep hello 7.5 4.3 4.2 3.5 1.7 1.5 seconds to a shell visit first visit revisit

I have to give a lot of credit to LLMs here for helping find a lot of the performance opportunities and bottlenecks. What first started as a β€œneat idea” turned into an incredibly usable project with their help.

Despite all the performance work, it is still not instant. It is fast enough to be usable, but it is not instant. Execution of a binary is still slow, because it is running under emulation. The first time you run a binary, it is translated from x86_64 to WebAssembly and that takes time. Subsequent runs are faster, because the translation is cached in memory.

Lastly, we have an upper-bound on the size of the closure we can fetch. The whole closure has to fit in tab memory, which is set to a hard limit of ~1.5GiB as of now and WebAssembly has a hard limit of 4GiB as it is a 32-bit address space.

Β§More than a parlor trick

The demo is clearly fun and impressive, but is it more than a parlor trick? I have been thinking of endless ideas of ways in which this could be a new way to use and leverage Nix.

Reviewing a pull request by using the software. If your CI already pushes to a cache, like Cachix, and if you use Nix, then a PR has produced real artifacts by the time a human looks at it. A bot can leave a link that boots exactly those artifacts. The reviewer does not clone, does not build, does not trust a screenshot. They click, and can immediately test out the software. β€œDoes this fix the bug?” stops being a thought experiment.

Agent Artifacts. Agents can produce Nix store paths as artifacts, and those artifacts can be shared with humans or other agents. A bot can produce a store path, and another bot can boot it in a browser tab and run tests against it.

Bug reports that carry their own environment. β€œWorks on my machine” is a URL now for reproduction.

Documentation you can run. A tutorial that names a tool version can link a shell with that exact version on PATH, pinned forever, with no install step standing between a reader and the first command.

Archaeology. You can run historic versions of software and explore their behavior. You can run a version of Python from 2017 and see what it does, or a version of hello from 2005 and see how it differs from today. This was already possible with Nix, but now you can do it in the browser.

The source is at github.com/fzakaria/trynix. The Nix cache has quietly served an open CORS header for years, waiting to be abused used. Go boot something old.

πŸ”₯ **What’s your take?**
Share your thoughts in the comments below!

#️⃣ **#Nix #package #live #browser**

πŸ•’ **Posted on**: 1788715236

🌟 **Want more?** Click here for more info! 🌟

By

Leave a Reply

Your email address will not be published. Required fields are marked *