Back to Rivet

Building the World's Fastest Package Manager with agentOS

website/src/content/posts/2026-07-07-worlds-fastest-package-manager/page.mdx

2.3.319.7 KB
Original Source

We built agentOS as a lightweight alternative to sandboxes. Because it runs Linux on WebAssembly, it cold-starts up to 516× faster than a sandbox, and it's light enough to run inside your own backend in userspace, with no external sandboxing infrastructure.

But once starts are that cheap, everything else on the setup path becomes the bottleneck, starting with installing packages. So today we introduced the agentOS Package Registry, which meant installing packages without the overhead you'd expect from apt, apk, Homebrew, and the rest.

The status quo for package managers

The best known package managers all do roughly the same thing on install.

Take apt, for example. Installing git means dpkg has to:

  1. Unpack the .deb and write its files onto the disk (/usr/bin/git, /usr/lib/git-core/, /usr/share/)
  2. Record the install in its package database at /var/lib/dpkg/
  3. Run the package's post-install scripts

We benchmarked how long that actually takes. Installing git offline takes 0.25s on apk, 0.48s on pacman, 0.87s on apt, and 2.11s on Homebrew.

[Source]

For your dev machine, these times are fine.

But agentOS VMs need to start instantly, so running shell scripts and filesystem calls for every single package is unacceptable.

Fast installs with 0 filesystem writes

The premise behind agentOS's package performance is that installing a package writes nothing to the filesystem and runs no scripts. There's no extraction, no file copies, and no post-install step, so the install is effectively free.

<svg viewBox="0 0 700 240" role="img" aria-label="Installing git with apt means extract, write files, record in the package database, and run post-install triggers, taking 0.87s. With agentOS, you point at the .aospkg and read its manifest, taking 130µs." style="width:100%;max-width:700px;height:auto;display:block;margin:2.5rem auto;font-family:system-ui,sans-serif"> <defs> <marker id="pm-ar" viewBox="0 0 10 10" refX="8.5" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"> <path d="M0 0 L10 5 L0 10 z" fill="#1b1916"/> </marker> </defs> <text x="60" y="48" font-size="12" font-weight="600" fill="#8a8578" letter-spacing="0.12em">apt</text> <g fill="#ffffff" stroke="#1b1916" stroke-width="1.4"> <rect x="60" y="62" width="100" height="44" rx="7"/> <rect x="182" y="62" width="100" height="44" rx="7"/> <rect x="304" y="62" width="110" height="44" rx="7"/> <rect x="436" y="62" width="110" height="44" rx="7"/> </g> <g font-size="12" fill="#1b1916" text-anchor="middle"> <text x="110" y="88">extract</text> <text x="232" y="88">write files</text> <text x="359" y="88">record in db</text> <text x="491" y="83">post-</text> <text x="491" y="97">install</text> </g> <g stroke="#1b1916" stroke-width="1.4"> <line x1="160" y1="84" x2="182" y2="84" marker-end="url(#pm-ar)"/> <line x1="282" y1="84" x2="304" y2="84" marker-end="url(#pm-ar)"/> <line x1="414" y1="84" x2="436" y2="84" marker-end="url(#pm-ar)"/> <line x1="546" y1="84" x2="568" y2="84" marker-end="url(#pm-ar)"/> </g> <rect x="568" y="62" width="100" height="44" rx="7" fill="#fbe9e0" stroke="#D63E00" stroke-width="1.4"/> <text x="618" y="89" text-anchor="middle" font-size="13" font-weight="700" fill="#a33000">0.87s</text> <text x="60" y="164" font-size="12" font-weight="600" fill="#8a8578" letter-spacing="0.12em">agentOS</text> <g fill="#ffffff" stroke="#1b1916" stroke-width="1.4"> <rect x="60" y="178" width="150" height="44" rx="7"/> <rect x="232" y="178" width="120" height="44" rx="7"/> </g> <g font-size="12" fill="#1b1916" text-anchor="middle"> <text x="135" y="205">point at .aospkg</text> <text x="292" y="205">read manifest</text> </g> <line x1="210" y1="200" x2="232" y2="200" stroke="#1b1916" stroke-width="1.4" marker-end="url(#pm-ar)"/> <line x1="352" y1="200" x2="374" y2="200" stroke="#1b1916" stroke-width="1.4" marker-end="url(#pm-ar)"/> <rect x="374" y="178" width="96" height="44" rx="7" fill="#e7ece7" stroke="#2E4034" stroke-width="1.4"/> <text x="422" y="205" text-anchor="middle" font-size="14" font-weight="700" fill="#2E4034">130µs</text> </svg>

Two things make this possible: a package format designed to be read in place, and a virtual filesystem that serves those reads without ever unpacking anything.

The agentOS Package Binary Format

First, a prerequisite. An agentOS package is nothing more than a single binary file, a .aospkg. It's a deliberately simple container built to be read in place, with no unpacking step: a header, the package manifest, an index of where every file sits, and an uncompressed tar of the files themselves.

<svg viewBox="0 0 760 150" role="img" aria-label="An .aospkg file is a 16-byte header followed by three chunks: the manifest with package metadata, the index mapping each file path to an offset and length, and mount.tar, an uncompressed tar of the package files that is mmapped in place." style="width:100%;max-width:720px;height:auto;display:block;margin:2.5rem auto;font-family:system-ui,sans-serif"> <text x="30" y="30" font-size="11" font-weight="600" fill="#8a8578" letter-spacing="0.1em">git-2.54.aospkg</text> <g stroke="#1b1916" stroke-width="1.4"> <rect x="30" y="42" width="70" height="58" fill="#faf8f3"/> <rect x="100" y="42" width="150" height="58" fill="#ffffff"/> <rect x="250" y="42" width="150" height="58" fill="#ffffff"/> <rect x="400" y="42" width="330" height="58" fill="#e7ece7" stroke="#2E4034" stroke-width="2"/> </g> <g text-anchor="middle"> <text x="65" y="76" font-size="9" fill="#56524a">header</text> <text x="175" y="76" font-size="11.5" font-family="ui-monospace,monospace" fill="#1b1916">manifest</text> <text x="325" y="76" font-size="11.5" font-family="ui-monospace,monospace" fill="#1b1916">index</text> <text x="565" y="76" font-size="11.5" font-family="ui-monospace,monospace" fill="#2E4034">mount.tar</text> </g> <g text-anchor="middle" font-size="9.5"> <text x="65" y="122" fill="#94908a">16 bytes</text> <text x="175" y="122" fill="#1b1916">package metadata</text> <text x="175" y="136" fill="#94908a">read once at install</text> <text x="325" y="122" fill="#1b1916">path → offset + length</text> <text x="325" y="136" fill="#94908a">O(log n) lookup, no scan</text> <text x="565" y="122" fill="#1b1916">uncompressed file bytes</text> <text x="565" y="136" fill="#94908a">mmap'd in place, never extracted</text> </g> </svg>

On startup, installing a package means agentOS reads the manifest and loads the index, and nothing else. The index already ships sorted by path, so it's ready for binary-search lookups without any processing, and nothing is written to disk. The whole thing costs 130µs, effectively free.

Reading a file that was never written

So how do you run a package if it was never written to disk?

This is where agentOS's powerful filesystem comes in. It supports flexible configurations for mounts, overlays, and virtual filesystems (the most relevant to this post).

A virtual filesystem (VFS) is a filesystem whose files don't actually live on disk.

Instead, its filesystem operations (read, write, list, etc) map to arbitrary hooks in your code that return "virtual" files. Each read runs your hook, which returns whatever bytes it wants.

For example, a simple VFS could map a read of /hello/{name}.txt to Hello, {name}!, so reading /hello/world.txt returns Hello, world! without touching the disk.

<svg viewBox="0 0 760 142" role="img" aria-label="A read of /hello/world.txt runs the VFS hook (your code), which returns the bytes Hello, world! without ever touching the disk." style="width:100%;max-width:720px;height:auto;display:block;margin:2.5rem auto;font-family:system-ui,sans-serif"> <defs> <marker id="vfs-ar" viewBox="0 0 10 10" refX="8.5" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"> <path d="M0 0 L10 5 L0 10 z" fill="#1b1916"/> </marker> </defs> <rect x="20" y="66" width="212" height="56" rx="7" fill="#ffffff" stroke="#1b1916" stroke-width="1.4"/> <text x="126" y="90" text-anchor="middle" font-size="9.5" fill="#8a8578" letter-spacing="0.1em">READ</text> <text x="126" y="108" text-anchor="middle" font-size="11.5" font-family="ui-monospace,monospace" fill="#1b1916">read("/hello/world.txt")</text> <line x1="232" y1="94" x2="266" y2="94" stroke="#1b1916" stroke-width="1.4" marker-end="url(#vfs-ar)"/> <rect x="266" y="66" width="238" height="56" rx="7" fill="#ffffff" stroke="#1b1916" stroke-width="1.4"/> <text x="385" y="90" text-anchor="middle" font-size="9.5" fill="#8a8578" letter-spacing="0.1em">VFS HOOK · YOUR CODE</text> <text x="385" y="108" text-anchor="middle" font-size="11.5" font-family="ui-monospace,monospace" fill="#1b1916">return "Hello, " + name</text> <line x1="504" y1="94" x2="538" y2="94" stroke="#1b1916" stroke-width="1.4" marker-end="url(#vfs-ar)"/> <rect x="538" y="66" width="202" height="56" rx="7" fill="#e7ece7" stroke="#2E4034" stroke-width="1.4"/> <text x="639" y="90" text-anchor="middle" font-size="9.5" fill="#5f7060" letter-spacing="0.1em">VIRTUAL FILE</text> <text x="639" y="108" text-anchor="middle" font-size="12" font-family="ui-monospace,monospace" fill="#2E4034">Hello, world!</text> </svg>

The VFS behind agentOS packages

In the case of the agentOS package manager, it leverages VFS in order to completely remove the install phase from packages altogether. Instead, when the OS reads a package, it's being served by a VFS.

Concretely, git's files live under /opt/agentos/pkgs/git/<version>/. Reading one looks exactly like the /hello example above, only now the bytes come from the package itself:

<svg viewBox="0 0 760 142" role="img" aria-label="A read of /opt/agentos/pkgs/git/2.54/bin/git is routed to the agentOS package VFS, which returns the git binary's bytes straight from the package." style="width:100%;max-width:720px;height:auto;display:block;margin:2.5rem auto;font-family:system-ui,sans-serif"> <defs> <marker id="pvfs-ar" viewBox="0 0 10 10" refX="8.5" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"> <path d="M0 0 L10 5 L0 10 z" fill="#1b1916"/> </marker> </defs> <rect x="8" y="66" width="304" height="56" rx="7" fill="#ffffff" stroke="#1b1916" stroke-width="1.4"/> <text x="160" y="90" text-anchor="middle" font-size="9.5" fill="#8a8578" letter-spacing="0.1em">READ</text> <text x="160" y="108" text-anchor="middle" font-size="10" font-family="ui-monospace,monospace" fill="#1b1916">read("/opt/agentos/pkgs/git/2.54/bin/git")</text> <line x1="312" y1="94" x2="336" y2="94" stroke="#1b1916" stroke-width="1.4" marker-end="url(#pvfs-ar)"/> <rect x="336" y="66" width="206" height="56" rx="7" fill="#ffffff" stroke="#1b1916" stroke-width="1.4"/> <text x="439" y="90" text-anchor="middle" font-size="9" fill="#8a8578" letter-spacing="0.08em">AGENTOS PACKAGE VFS</text> <text x="439" y="108" text-anchor="middle" font-size="10" fill="#1b1916">returns the file's bytes</text> <line x1="542" y1="94" x2="566" y2="94" stroke="#1b1916" stroke-width="1.4" marker-end="url(#pvfs-ar)"/> <rect x="566" y="66" width="186" height="56" rx="7" fill="#e7ece7" stroke="#2E4034" stroke-width="1.4"/> <text x="659" y="90" text-anchor="middle" font-size="9.5" fill="#5f7060" letter-spacing="0.1em">VIRTUAL FILE</text> <text x="659" y="108" text-anchor="middle" font-size="11" fill="#2E4034">the git binary</text> </svg>

With apt, installing git writes real files to /usr/bin, /usr/lib, and /usr/share, then runs post-install scripts. With agentOS, installing git writes nothing to disk. The file only appears when you read it, served on demand by the package VFS.

This means that you can setup an agentOS VM with packages preinstalled or install packages on the fly without any latency.

Reading a package that was never unpacked

But here's the catch. We just said installing a package does zero filesystem writes. If that's true, how do we take an archive downloaded from the internet and expose it as a real directory on the VM's filesystem?

Say you have a .aospkg for something bigger, like a coding agent that bundles a whole Node app:

bin/
    agent
dist/
    index.js
node_modules/
    @modelcontextprotocol/sdk/
    zod/
    typescript/
    ...              # thousands of files

Most mainstream package managers extract an archive and write all the files to the host. Even if you have the package pre-downloaded, it requires expensive filesystem operations to actually turn that archive into a readable format on the host.

That means unpacking the .aospkg and writing every one of those files to disk, potentially thousands of tiny writes:

  • /opt/agentos/pkgs/agent/<version>/dist/index.js
  • /opt/agentos/pkgs/agent/<version>/node_modules/zod/lib/index.js
  • ... and thousands more

agentOS takes a different approach:

Instead of extracting the tar and writing the files to the host, in agentOS the .tar is mounted directly as a filesystem.

This means that nothing needs to happen when you install a package, no matter how many files it has.

Reading a file works the same regardless of package size. When the user runs the agent:

  1. The OS reads /opt/agentos/pkgs/agent/<version>/bin/agent.
  2. agentOS returns the data directly from within the .aospkg binary file itself, without extracting to the filesystem.

This magic is made possible by a trick called mmap: it lets you map a portion of a file straight into memory.

Remember the index from the .aospkg format? It already records exactly where every file sits inside mount.tar. So to read ./bin/agent, agentOS can:

  1. Read the index to find what byte range of mount.tar holds bin/agent: offset 1536, length 4821.
  2. mmap those 4821 bytes at offset 1536.
  3. Return them to the OS.
<svg viewBox="0 0 780 250" role="img" aria-label="Reading the agent: the VFS looks up bin/agent in the .aospkg package index to get offset 1536 and length 4821, then mmaps that byte range straight out of the mount.tar chunk." style="width:100%;max-width:740px;height:auto;display:block;margin:2.5rem auto;font-family:system-ui,sans-serif"> <defs> <marker id="rp-ar" viewBox="0 0 10 10" refX="8.5" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"> <path d="M0 0 L10 5 L0 10 z" fill="#1b1916"/> </marker> <marker id="rp-map" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"> <path d="M0 0 L10 5 L0 10 z" fill="#2E4034"/> </marker> </defs> <g fill="#ffffff" stroke="#1b1916" stroke-width="1.4"> <rect x="40" y="24" width="132" height="46" rx="7"/> <rect x="182" y="24" width="132" height="46" rx="7"/> <rect x="324" y="24" width="132" height="46" rx="7"/> <rect x="466" y="24" width="132" height="46" rx="7"/> <rect x="608" y="24" width="132" height="46" rx="7" fill="#e7ece7" stroke="#2E4034"/> </g> <g text-anchor="middle" fill="#1b1916"> <text x="106" y="52" font-size="12" font-family="ui-monospace,monospace">exec "agent"</text> <text x="248" y="45" font-size="11">read the file</text> <text x="248" y="60" font-size="9" fill="#56524a">/opt/agentos/…/agent</text> <text x="390" y="45" font-size="11">VFS finds it</text> <text x="390" y="60" font-size="9" fill="#56524a">in the package index</text> <text x="532" y="52" font-size="10.5" font-family="ui-monospace,monospace">mmap(1536, 4821)</text> <text x="674" y="45" font-size="9" fill="#5f7060" letter-spacing="0.1em">VIRTUAL FILE</text> <text x="674" y="60" font-size="10" fill="#2E4034">the launcher</text> </g> <g stroke="#1b1916" stroke-width="1.4"> <line x1="172" y1="47" x2="182" y2="47" marker-end="url(#rp-ar)"/> <line x1="314" y1="47" x2="324" y2="47" marker-end="url(#rp-ar)"/> <line x1="456" y1="47" x2="466" y2="47" marker-end="url(#rp-ar)"/> <line x1="598" y1="47" x2="608" y2="47" marker-end="url(#rp-ar)"/> </g> <text x="40" y="150" font-size="11" font-weight="600" fill="#8a8578" letter-spacing="0.1em">agent-1.0.aospkg</text> <g stroke="#1b1916" stroke-width="1.3"> <rect x="40" y="162" width="44" height="58" fill="#faf8f3"/> <rect x="84" y="162" width="104" height="58" fill="#faf8f3"/> <rect x="188" y="162" width="132" height="58" fill="#e7ece7" stroke="#2E4034" stroke-width="2"/> <rect x="320" y="162" width="420" height="58" fill="#ffffff"/> <rect x="332" y="171" width="150" height="40" fill="#e7ece7" stroke="#2E4034" stroke-width="1.4"/> </g> <g text-anchor="middle"> <text x="62" y="196" font-size="8.5" fill="#56524a">header</text> <text x="136" y="196" font-size="9" font-family="ui-monospace,monospace" fill="#1b1916">manifest</text> <text x="254" y="188" font-size="10" font-family="ui-monospace,monospace" fill="#2E4034">index</text> <text x="254" y="202" font-size="8" fill="#5f7060">the TOC</text> <text x="407" y="188" font-size="9.5" font-family="ui-monospace,monospace" fill="#2E4034">bin/agent</text> <text x="407" y="202" font-size="8" fill="#5f7060">offset 1536 · length 4821</text> <text x="632" y="184" font-size="9" font-family="ui-monospace,monospace" fill="#1b1916">mount.tar</text> <text x="632" y="199" font-size="8" fill="#56524a">node_modules/ · thousands</text> </g> <line x1="500" y1="73" x2="338" y2="169" stroke="#2E4034" stroke-width="1.3" stroke-dasharray="4 3" marker-end="url(#rp-map)"/> <line x1="566" y1="73" x2="476" y2="169" stroke="#2E4034" stroke-width="1.3" stroke-dasharray="4 3" marker-end="url(#rp-map)"/> </svg>

This enables agentOS to take a software package and "install" it on the operating system with no tar extractions, no filesystem writes, and no scripts. Just a path to the .aospkg.

Why not snapshotting?

Most sandbox providers recommend working around this by:

  1. Provision a sandbox
  2. Install required packages (slow)
  3. Snapshot sandbox
  4. Create a new sandbox from that snapshot (faster)
  5. Repeat when you need to change packages

This is a similar strategy to using a Dockerfile to pre-install packages.

However, the nature of agents is they're great at morphing their environment to their needs, including managing packages.

Snapshotting gives you a rigid base VM to work off of that requires an expensive & complicated operation to update.

agentOS Package Manager encourages composition, where packages can be installed cheaply without requiring a snapshot.

Putting it all together

When you put it all together, it provides a clean, simple way to install packages without overhead:

typescript
import { agentOS, setup } from "@rivet-dev/agentos";
import pi from "@agentos-software/pi";
import git from "@agentos-software/git";

// Each import is just a path to a package's .aospkg on disk.
console.log(pi.packagePath);
// => /app/node_modules/@agentos-software/pi/package.aospkg

// Listing packages here "installs" them with zero I/O.
const vm = agentOS({
  software: [pi, git],
});

export const registry = setup({ use: { vm } });
registry.start();

Get started