Nix Is Right. Its Cost Is Not.

The case for composition-addressed systems.

A Confession

I have spent over a decade inside the Nix model. I have advocated for it, built on it, and defended it in rooms where it was not popular. The work I've published on atoms exists because I believe the core of that model is one of the genuinely important ideas in software.

So understand that what follows is not a drive-by. It's aimed at my own house.

Here it is: Nix is right about everything except the thing everyone thinks defines it. The store, /nix/store/<hash>-name, the signature move, the thing in the first paragraph of every Nix pitch, is not the insight. It's the cost. And the cost is so much higher than it needs to be that it has functionally capped the model's reach for twenty years.

This post makes three claims:

  1. The store path is five different mechanisms wearing one trench coat, and the overload is where all the pain comes from.
  2. One of those five, embedding hashes inside artifacts, makes the one thing Nix has wanted for a decade, true content addressing, impossible. Not hard. Impossible.
  3. You can keep every guarantee that matters, drop the store entirely, build upstream software with upstream's own unmodified build process, and nearly every piece required already exists in production today.

If those claims hold, the conclusion writes itself: there is a system strictly simpler and strictly more capable than Nix sitting in plain sight, waiting for someone to assemble it. Let me take each in turn.

Five Jobs, One Trench Coat

What does /nix/store/x7q2…-openssl-3.0.12 actually do? Pull it apart and you find five distinct jobs:

# Job What it means
1 Storage key Where the bytes live on disk
2 Build-time binding The compiler finds dependencies at these exact paths
3 Runtime binding RPATHs and shebangs embed these exact paths into binaries, forever
4 Closure discovery Nix finds runtime deps by grepping your build output for hash strings
5 Co-installation Unique prefixes never collide, so twenty glibcs coexist

Five jobs, one string. Rich Hickey has a word for this: complected. Braided together such that you cannot touch one strand without pulling the other four.

Job #3 is the expensive one, and you can watch it happen. Pick any binary in the store and ask what it links against:

$ patchelf --print-rpath $(command -v openssl)
/nix/store/x7q2…-openssl-3.0.12/lib:/nix/store/1a4…-glibc-2.39/lib

Those are not search hints. They are absolute paths, burned into the binary at build time. The binary does not depend on openssl; it depends on openssl living at exactly /nix/store/x7q2…. Change one byte of glibc and its hash changes, so its path changes, so every rpath pointing at it is now wrong, so everything downstream rebuilds. Not because any behavior changed, but because a string embedded in a binary did.

This is also why Nix must wage a permanent war against upstream build systems: patchelf, wrapper scripts, cc-wrapper, a stdenv of heroic and unknowable complexity, thousands of patches whose only purpose is to coerce software into linking against paths no upstream developer has ever heard of. nixpkgs is a magnificent engineering artifact, but a huge fraction of it exists to fight the store, not to package software.

And yes, job #4 is really how it works. The runtime closure of your artifact, the complete set of things it needs at run time, is computed by scanning its bytes for things that look like store hashes. It is over-approximate (any textual mention counts), it is under-approximate (compress or encode a reference and it vanishes), and it is the load-bearing mechanism underneath every Nix deployment on earth. We'll come back to this.

The Impossibility Result

Now the finding that reframed everything for me.

Nix has wanted content addressing, naming artifacts by what they are instead of by the recipe that made them, for over a decade. Eelco's thesis calls it the intensional model, and it's been the grail ever since: cache hits when different recipes produce identical outputs, early cutoff on rebuild cascades, trustless binary sharing. RFC 62 was proposed in 2019. It is 2026, and ca-derivations is still experimental, still being wrestled with by some of the best engineers in the ecosystem.

It is not a staffing problem. It's a math problem.

A cryptographic hash has no accessible fixed point. An artifact cannot contain its own hash: to know the name you must first fix the bytes, but the bytes want to contain the name. And an artifact that embeds pointers to other artifacts' hashes cannot be named until every pointer is resolved, which means self-references and reference cycles must be detected, normalized, and rewritten, which changes the bytes, which changes the hash, which breaks the signatures. This is why the RFC 62 work drowns in "hash rewriting" and "self-reference normalization" machinery.

Read that again with the five jobs in mind. Job #3, embedding hashes in artifacts, is precisely the thing that makes content addressing unreachable by any clean path. ca-derivations doesn't disprove that; it demonstrates it. What its machinery addresses is not your artifact but a normalized shadow of it, self-references stripped out for hashing and stamped back in afterward, and seven years of experimental status is the running price of that substitution. Nix's most famous mechanism and Nix's most wanted feature are mutually exclusive. No amount of engineering fixes a contradiction. It has to be dissolved.

Now run the logic in reverse. An artifact that embeds only names, libssl.so.3, the way upstream already writes software, has a stable hash the moment the build finishes. Nothing to rewrite. Nothing to normalize. The pointer information doesn't vanish; it moves into a separate object that maps names to hashes. The obstruction doesn't get solved. It evaporates.

And this is a choice Nix made, not a law it obeys. Its own content-addressing effort accepts the obstruction and fights it; Spack sidesteps it entirely, hash-addressing every package while keeping the binaries relocatable by leaving the install path out of the hash. The fusion Nix treats as fundamental is optional.

What Nix Actually Got Right

If the store isn't the point, what is? Strip away the store, the language, even the derivation format, and the irreducible core of Nix is this:

Purely functional composition into a cryptographically verified closure.

A build is a pure function. Its inputs are named by hash, completely and exhaustively; the Nix model, Guix included, is the only exhaustive dependency manager there is. Its output is therefore trustworthy in a way no apt install can ever be. The sandbox enforces the purity; the hashes make the closure unforgeable; composition of verified pieces yields a verified whole.

That is the treasure. And notice: nothing in that sentence mentions /nix/store. The hermeticity was always the sandbox's doing, deny-by-default, only declared inputs visible. The store path contributed binding, not isolation. Which raises the question I kept circling: what if the sandbox showed the build a plain, boring, Filesystem Hierarchy Standard (FHS) world, the /usr/lib, /usr/include layout every upstream build system has expected since the dawn of Unix, but synthesized that world, file by file, from content-addressed inputs?

The guarantee is identical: the build can only read what the closure declares, and the kernel enforces it. But now ./configure && make just works. No patchelf, no wrappers, no stdenv. And nothing to scan or rewrite afterward, because the binary embeds only the name libssl.so.3, resolved by the mount the same way it resolves on any other Linux system. The absolute path never enters the artifact, so it never has to be found and patched back out.

This is not hypothetical. OSTree ships a content-addressed object store today whose deployed files sit at ordinary FHS paths, and it never rewrites an rpath, because nothing it builds embeds a store hash to begin with. The scan-and-rewrite problem was never a property of content addressing. It was a property of putting the hash inside the artifact.

Network access? The same answer Nix already gives: fetching is separated from building. A recording proxy content-addresses everything a build fetches on first run; forever after, the "network" is a replay of pinned bytes, and anything off-script fails closed. Most ecosystems have already converged on fetch/build separation anyway, through vendoring, offline flags, and lockfiles. We just enforce it at the boundary.

Determinism? It was never the store path's doing. Build identity here is input-addressed, keyed on the atom's closure and toolchain exactly as a Nix derivation is keyed on its inputs, so you get one cached, signed result per build intent whether or not upstream's build is bit-for-bit reproducible. Reproducibility is the sandbox's job, and the residual nondeterminism upstream ships is the same orthogonal problem Nix already faces.

Three Primitives

Decomplect the trench coat and you get three primitives, each with a single job:

  1. A content-addressed store of files and trees (a real Merkle tree, not an opaque tarball), where bytes live. Solved several times over in production: git, OSTree, Bazel's remote-execution CAS, Fuchsia's blobfs.

  2. The composition, a small, signed, content-addressed manifest mapping conventional names to content hashes: /usr/lib/libssl.so.3 → blake3:9f2c…. This object is the closure, at any scale. It's what the build sandbox materializes, what the runtime mounts, what gets signed and shipped and diffed. It binds at whatever granularity you need, a single file or a whole tree, so an environment is usually a few dozen human-readable lines pinning package trees to FHS prefixes: something you read and diff, not an opaque image. When I said the pointer information "moves into a separate object," this is that object.

  3. An executor that materializes a composition cheaply and runs a process inside it. On Linux today: mount namespaces and composefs, one metadata image plus one content-addressed object directory, mounted as a single overlay, kernel-verified with fs-verity. If you ever tried to map store paths onto container layers and hit the overlayfs wall, know that this is O(1) layers no matter how many packages compose, and the kernel refuses tampered content at read time. That last part is a guarantee Nix does not give you: Nix verifies signatures when it downloads a path, and again on demand if you run nix store verify, but nothing re-checks the bytes on the way to execution. Tamper with a store file after the fact and it runs.

Storage, binding, execution: three clean jobs. The store path did the first two and left execution to the OS, then bolted two hacks onto the same string, closure discovery and co-installation. Pulled apart, each piece is boring, and boring is exactly what you want underneath a trust system.

That leaves jobs #4 and #5 from the trench coat. Co-installation (#5) falls out for free: conflicts can only exist within one composition, where they're real ABI questions, not filesystem accidents. And because every composition draws its files from the one content-addressed store, sharing is the rule, not the exception: two applications that pin the same openssl point at the same verified blob on disk, where a per-app container would ship its own opaque copy. Co-installation without the container tax. Closure discovery (#4), the hash-grep, is the one real debt. Its replacement has existed in production for decades. RPM and Debian extract dependencies structurally, reading the actual DT_NEEDED entries, the actual undefined symbol tables, the actual soname records, rather than grepping for magic strings. We take that lineage and upgrade it: hash the extracted interface itself. Which unlocks something Nix cannot do at any price.

Lego, Not Marble

Look at how each tool names a dependency. A Docker image pins the whole filesystem: one opaque blob, take it or leave it. A Debian package pins a version range, "libssl3, version ≥ 3.0," and trust the maintainer. A Nix derivation pins exact bytes, built against this glibc, down to the commit. Three granularities, all of them wrong. One too coarse to share anything, one too loose to verify anything, one so tight that a single byte rebuilds the world. Each misses the thing a binary actually depends on, which is none of the above: an interface. These symbols, these versions, this ABI surface.

In the model I'm describing, every built artifact gets an interface manifest, mechanically extracted: what it provides (soname, exported symbols, hashed into an interface digest) and what it requires (needed symbols, per dependency name). The crucial property: the manifest is binding-free. It names no other artifact. It's a pure fact about one tree of bytes.

Bindings live only in compositions. So when a security patch lands for OpenSSL: build the new one, check that its provided interface still covers what every consumer needs, a set-inclusion check that emits a storable proof, then swap the pointer in the composition, recompute the root, and re-sign. Nothing rebuilds. The new root hash is exactly as unambiguous, exactly as verifiable, exactly as sure as a Nix world-rebuild, without paying the cascading rebuild cost that hash-tattooed binaries force. This is strict by default: an entry is an exact digest pin, and the guarantee is precisely Nix's. Accepting a substitute because its interface covers the need, rather than because its bytes match, is a deliberate opt-in, per composition, with the proof recorded.

If you know Nix or Guix, you're already objecting: we have this, it's called grafting. Guix can patch a vulnerable OpenSSL into thousands of dependents without rebuilding them, and nixpkgs has replaceDependencies to match. But look at how. Grafting walks every dependent and rewrites the old store hash to the new one in place, byte for byte, which only works if the replacement path is exactly the same length. Nixpkgs' own source admits the rewrite can "cause breakage" and "should be a short term solution"; one well-known community write-up is blunter: "somewhat evil/potentially unsound." That's not the Lego swap. That's the marble, re-carved in place with a chisel, hoping the new name has the same number of letters as the old one. Grafting has to exist because the store fused identity, location, and dependency edge into one string, so "patch one library" must mean "rewrite that string everywhere it was stamped." Remove the fusion and the chisel goes with it. The composition swaps a pointer, and nothing downstream is touched, because nothing downstream ever named the path.

Environments stop being marble, carved once and shattered on every change. They become Lego. You keep the property Nix actually promised you, an environment whose root hash cannot change unless something inside it changes, and you stop paying for it in rebuilds.

And for interpreted ecosystems ("Python has no stable ABI," you say), the same algebra holds with different extractors. Wheels ship exact file manifests (RECORD), imports are statically parseable, and dynamic behavior is caught by observation, because in this architecture builds read their inputs through a filesystem daemon we control. Every file a build or test actually touches is logged, for free. Declared closure enforced by the sandbox; observed closure measured by the daemon; the delta is bloat you can prune. Nix cannot tell you which of its closure entries are actually used. This system knows.

Every Environment Is a Composition

Notice what the composition doesn't care about: scale. A package is a composition over one project's files. A dev shell is a composition over a toolchain. A home directory, a whole operating system, each is just a full root over some larger file set, signed and content-addressed the same way. Every package is already an environment, and most environments are a package with the walls pushed out.

This is the part NixOS users should care about most, because it's the part Nix makes hardest. Reproducible dev, home, and system configuration is built today by stacking a second layer on the store: the module system, a meta-layer that reimplements "environment" in pure Nix and carries much of nixpkgs' maintenance weight on its back. Here there is no meta-layer welded to the build. The environment is the primitive. You keep exactly what you wanted from Nix, a stateless cryptographic description of a package and of a whole system, and you assemble both from upstream's own artifacts instead of nixpkgs' patched re-creations of them. That isn't a bonus so much as the only way a system like this stays maintainable.

You might think that deletes the thing people actually love the module system for: composing configuration, merging options, layering defaults, overriding per host. It doesn't. Look at what configuration composition actually is. It is pure functions over data, producing a composition. Nothing in it touches how a package builds or how an environment is verified, and it never did; the module system only welded the two together so they looked inseparable. Pulled apart, configuration combination is an ordinary data-transformation problem with no privileged tie to Nix, and it belongs in a language built for it (Nickel, say), with real types and first-class merge semantics. Which dissolves the last reason to keep the Nix language at all: the thing you would have kept it for was never part of the substrate.

This is also where the name comes from. A composition is a Merkle object, a signed tree of name-to-digest bindings under one root hash. Content addressing names a blob by the hash of its content; composition addressing names an environment by the hash of its composition, its root. And an environment is just a composition of packages, so the thing the root commits to, the thing you address, is the composition. That is the name, and it says exactly what it is: content addressing, one level up, from blobs to whole environments.

I Deleted the Derivation Without Noticing

This whole model needs one thing at its root that nothing in the container world, the Bazel world, or the distro world provides: an unforgeable, signed, content-addressed statement of build intent. What sources, what dependencies, what claim of ownership, what chain of custody. The derivation is Nix's version of it, unsigned and store-coupled, but the right kind of object.

I've spent the past two years building exactly this object, and I did not understand what I had. It's called an atom: a signed snapshot of sources plus manifest plus lock, with cryptographic provenance chaining back to the origin repository. Claim, publish, verify, all offline, no registry required.

It is source-first in the most literal sense: an atom is bound directly to the git tree that is the source's own source of truth, the same exact tree, not a re-description of it in another language. That is where its equivalence to a Nix derivation comes from. What it adds is the discipline raw source never had: clean separation from the build details, and mandatory versioning. Every atom is versioned, and that is the part that makes real dependency resolution, SAT over version ranges, possible at all. Nix pins exact commits and calls it done; it has no version range to resolve. Closing that gap was the first problem I set out to solve, years before I understood where it led.

The lock file inside the atom resolves name-and-version-range declarations down to pinned, signed content pointers. That is the same shape as the composition, one layer up: version ranges contract to lock pins the way interface requirements contract to composition pins. One binding algebra, running at the source layer and the artifact layer both.

When I stripped every derived hash out of atom identity and keyed everything on names bound to signed pointers, I thought I was tidying up atom. I wasn't. I had removed the last thing that made a derivation necessary, and I didn't notice, because I was still thinking in derivations. That is the trap in a single frame: the model is good enough that even the person taking it apart mistakes the scaffolding for the structure. Atoms don't reimplement the derivation. They make it redundant. I couldn't see it from inside the Nix way of thinking, the same way of thinking that has the whole ecosystem digging, with enormous skill, in the wrong direction.

And this part is not a whiteboard sketch. A proof of concept already resolves and locks atoms end to end, with full SAT resolution over isolated, versioned git objects, which is the one component the whole design hangs on. The milestone the earlier essays called upcoming is proven; the properly abstracted implementation is what's being built now.

Eos, Properly Introduced

I've teased Eos as "the build engine" in prior posts and never given it real treatment. A full exposition is coming, but this argument needs its shape now, because the substrate reshapes it, dramatically for the better.

Eos is a build scheduler: it takes a DAG of pure build actions and decides what runs, where, and when, locally embedded or across a fleet, with caching at every level of the cryptographic chain (atom → version → revision → plan → output), so work is skipped at the highest level possible. Unusually for this genre of software, the scheduling discipline came before the code: the dispatch model is grounded in the classical scheduling literature, its fairness and starvation-freedom are model-checked in TLA+, its performance bound mechanized in Lean, and the whole thing validated in simulation against real-world build traces before we committed to an implementation. I'm allergic to research-grade architecture shipping as production infrastructure (Nix itself is the cautionary tale), so this time the proofs came first.

Under the original plan, Eos scheduled Nix derivations, which meant it also needed an evaluation pipeline: run the Nix language to discover the DAG, coordinate evaluators feeding builders, coalesce the phases. An entire subsystem.

In the substrate model, the DAG is just the atoms. The lock already is the dependency graph, signed, resolved, sitting in a file. No language to evaluate, no evaluator to schedule. Each node builds by handing the atom's closure to the executor and letting upstream's own build system do what it does, including its own internal parallelism, which is the part upstream actually optimized. Eos schedules the graph; make -j does the leaves. An entire layer of the architecture, the part I was least looking forward to building, deleted by a better model. The scheduling theory transfers untouched; it never cared what a node was, only that nodes are pure and the graph is a DAG.

The Snix Windfall

"Nearly every piece exists" was claim three, so let me be concrete about the biggest one. Snix, the community reimplementation of Nix in Rust, made an architectural decision somewhere along the way that I want to publicly applaud: they built their storage layer, castore, as a generic content-addressed blob and directory store. Real Merkle trees. Not NARs, not store-path-aware, deliberately and cleanly generic, with lazy on-demand materialization over FUSE and virtiofs.

They may have built it to serve /nix/store, but what they actually built is primitive #1 of this architecture, production-grade, sitting on a shelf.

It goes further. Their build protocol describes a build as a set of castore Merkle nodes (the inputs), mounted at a configurable location, a command, an environment, and expected outputs. The store-path assumptions are parameters, not axioms: the mount location and the store composition are configurable, not baked in. And their sandbox runs builds as rootless OCI containers with inputs lazily streamed from castore. Point that machinery at an FHS layout instead of /nix/store, and primitive #3 is most of the way built too. Even the observation channel falls out, because the FUSE daemon serving the inputs is the single unbypassable point every read already flows through.

castore also does content-defined chunking, deduplicating below the file level. In Nix, that kind of work is damage control: the store duplicates so aggressively, a fresh copy of the world every time a hash changes, that sub-file dedup is what keeps the cache from exploding. Here it isn't load-bearing. The composition model already keeps storage cheap, because swapping a pointer copies nothing downstream. So castore's dedup stops being a mitigation and becomes what it should have been all along: an additive saving on top of a baseline that was already small. Good engineering, finally allowed to be a bonus instead of a patch. And that Snix treated a generic blob store as urgent enough to build is itself a tell: the storage cost of the current model is not a theoretical objection; it's a load the system is already bending under.

I don't know if the Snix folks think of castore as the foundation of a post-store world. But credit where due: they decomplected their storage layer when they didn't strictly have to, and in doing so they may have built the most important component of a system bigger than the one they were reimplementing.

The Honest Ledger

So what actually remains to be built? Fewer things than I have fingers:

  • The composer, the composition object itself: format, merge rules, root computation, signing, and emission to composefs images. Small.
  • The interface analyzers, the ELF extractor (a Rust reimplementation of what rpm and deb have done for decades, plus interface hashing) and the Python extractor. This is the genuinely novel code, and it's pure functions over bytes: analyzable, testable, cacheable by input hash.
  • The fetch proxy, record and replay. Annoying (TLS), not hard.
  • The closure computer, resolving requirements against providers to a minimal justified runtime set. Set operations over manifests.
  • The FHS delta on the Snix executor, and the read-set logging in the FUSE layer. Small seams into existing code.

Every one of these has a production ancestor. None is research. The identity, trust, and locking layer, the part that doesn't exist anywhere else, the part everything hangs off, is atom. And atom is not a proposal: it's specified, formally modeled, demonstrated in a working proof of concept, and being built properly now.

The other column of the ledger is the things this model deletes: the Nix language (compositions are data, the only function is build, and even configuration composition needs nothing from it). nixpkgs and the module-system meta-layer stacked on it (upstream's build process is the packaging; the composition is the configuration). The patching war (patchelf, wrappers, stdenv, gone). The evaluation scheduler. The world-rebuild. The store-path grammar and its hash-modulo arcana. The NAR format. I have been asked what a "simple Nix" would look like for years, and I kept trying to answer by subtracting from Nix. Wrong direction. You get the simple system by keeping Nix's one true invariant, pure functions over a verified closure, and rebuilding outward from it with parts that each do one job.

A newcomer to Nix must learn a lazy functional language, derivations, the store grammar, stdenv lore, and a million-line package set before their mental model stops lying to them. A newcomer to this system must learn: an atom is signed sources with a lock; a build runs the project's own instructions in a box that contains exactly the lock; a composition maps names to hashes and is itself hashed; you can swap compatible parts and the root hash tracks it. Every surface they touch (FHS paths, ./configure, pip) is one they already know. That's not "simpler than Nix." That's legible to someone who has never heard of Nix.

Why Hasn't Anyone Built This?

It's the right question to ask, and the honest answer isn't that no one was clever enough. It's that the parts lived in different worlds that never met. Debian and rpm have extracted interfaces structurally for twenty-five years, but in a world with no cryptographic purity and no content addressing. Nix had both, and spent its cleverness in the other direction: it welded identity to the store path, then poured a decade into fighting the self-reference problem the weld created, RFC 62 and ca-derivations and grafts, instead of noticing the weld was the problem. Nobody stood at the intersection holding both halves at once.

And the cheap substrate that makes it practical barely existed until now. A generic content-addressed Merkle store you can lazily mount is only a few years old. A kernel that mounts a verified composition as a single overlay in milliseconds, composefs over fs-verity and EROFS, only became complete at the end of 2023, with kernel 6.6. Attempt this in 2018 and you would have had to build two of the three primitives yourself before writing a line of the actual idea.

The last piece is the one I spent years building for narrower reasons, version resolution chief among them: a signed, versioned, source-addressed statement of intent, decoupled from any store. What I could not see until now is that it is sufficient, that it replaces the derivation outright. Seeing that required the one move the ecosystem couldn't make, which was to stop thinking in terms of the store. The gap was never in anyone's ability. It was a gap in architectural thinking: five jobs overloaded onto one path kept the whole ecosystem reasoning in the wrong layer, and we assumed the store was load-bearing because it was complex. Only in pulling it apart, job by job, do you find the pillar was never carrying the weight.

What Happens Next

I'm not announcing a finished system today. I'm not announcing a hunch either, and the difference is worth being precise about.

The two hardest, riskiest pieces already exist. The atom lock is proven end to end in a proof of concept, SAT resolution and all. The Eos scheduler, the part most likely to be quietly unsound, had its discipline machine-checked and its performance bound mechanized before a line of production code was written. The storage and execution layers I didn't have to build at all: castore and composefs already exist, already verified, sitting on the shelf. What remains is mostly connective tissue and one new but bounded piece, the interface analyzers, and none of it is research.

The Eos proof was written against an abstract DAG of pure actions. It never knew whether a node was a derivation or an atom, so when this model reorganized the graph beneath it, the proof did not break; it applied to the new nodes unchanged. The simpler design deleted an entire layer and cost nothing already proven.

I'll be honest about the timeline of my own understanding. I have spent over a decade with a nagging sense that something in Nix was overcomplicated, and I could never name which part. I named it last night. So the truest answer to "what happens next" is that I am re-forming the architecture around this simpler model, and the re-forming is mostly subtraction. The design at the highest layers is, after years, essentially settled. What remains is to build it, in the open, in stages, and I would rather not do that alone.

The stages are deliberately un-heroic. Each one ships something usable on its own, and no stage is a bet on the next.

  1. The hermetic builder. An unmodified upstream package, curl or zlib or CPython, its own release tarball, its own ./configure && make, built inside an FHS world synthesized from content-addressed inputs, with a signed closure of every byte it read and a log of every byte it touched. No Nix anywhere in the room. This is the thing Nix cannot do, and it's first for a reason.
  2. Analyzers and the composer. Interface manifests extracted mechanically, the composition assembled and signed, and the swap that makes the case: an ABI-compatible OpenSSL moved into a live composition, the satisfaction proof recorded, a new root computed, nothing rebuilt.
  3. Eos on the atom DAG. The scheduler driving real builds across the graph, on the discipline already proven.
  4. The MVP. One command over a real project with real upstream dependencies: add, resolve, lock, build, analyze, compose, run.

The full architecture, the roadmap, and the formal groundwork are in the open at github.com/axiosoph/axios, as always. If any of this reads like something you've wanted for as long as I have, that's the point. I'm one person who spent a decade trying to renovate this house from the inside, and the rebuild is bigger than one person. Come argue with me. Come build a piece of it.

We've been calling this family of ideas "store-based systems," and I never liked the name. Now I can say why: it names the one part we're throwing away. The invariant was never the store. It was the composition. These are composition-addressed systems, and Nix proved the foundation they stand on: purely functional composition into a verified closure is the right model for trustworthy software. Then it spent twenty years paying a five-way tax on it. The tax is optional now. The pieces are on the table.

Time to compose them.

References