Compile each Haskell module as its own content-addressed Nix derivation, so changing one module rebuilds one module, not the world, and everyone who shares a cache shares the compiled results.
Warning
Status: early prototype. Sandstone builds the bundled example project end to end, but it relies on unreleased Nix features and a work-in-progress fork of hnix-store, assumes Linux (x86_64-linux), and still has sharp edges throughout. It is ready for experimentation and contribution, not for production builds.
Nix builds a Haskell package as a single derivation: GHC compiles the whole package at once, and the smallest source change invalidates the entire build. Cabal's incremental rebuilds soften this locally, but that work is invisible to Nix and unshared between machines and CI.
Sandstone makes the unit of caching a module rather than a package. Each module becomes its own derivation, so:
- changing one module rebuilds only that module and the modules that depend on it;
- independent modules compile in parallel across all your cores;
- compiled modules are content-addressed and cached, so a build can reuse modules already compiled by anyone who shares your binary cache.
Sandstone is glue between two existing mechanisms:
- GHC's
-Mdependency dump produces the inter-module dependency graph. - Nix dynamic derivations let a build produce a derivation, so the per-module graph can be generated on the fly and then built.
The pipeline, from a source tree to a linked binary:
- Run
ghc -Mto emit a Makefile describing each module's.o/.hidependencies. - Parse that Makefile into a dependency graph (
Sandstone.GhcMakefile). - Walk the graph and emit, as a dynamic derivation:
- one compilation derivation per module, content-addressed, with separate
objectandinterfaceoutputs, each depending only on the interfaces of the modules it imports; - one link derivation that combines the object files into the final executable.
- one compilation derivation per module, content-addressed, with separate
- Hand the derivation graph to Nix, which builds and caches it.
hs-boot files, mutually-recursive modules, and nested module hierarchies are all handled. See example/ for a small project that exercises each case.
Sandstone needs a build of Nix with dynamic derivations and several experimental features enabled: ca-derivations, dynamic-derivations, recursive-nix, and nix-command. The repository pins a suitable Nix (dep/nix) and the hnix-store fork it builds against (dep/hnix-store), so you do not have to assemble them yourself.
Build the pinned Nix, then compile the bundled example project module-by-module:
# 1. Build the pinned Nix that supports dynamic derivations.
nix-build -A nix
# 2. Compile and link example/ through Sandstone, into an alternate store at /tmp/sand.
out=$(./result/bin/nix build -f . dyn-drvs-test-res \
--store /tmp/sand \
--substituters https://cache.nixos.org \
--extra-experimental-features 'ca-derivations dynamic-derivations recursive-nix nix-command' \
-L -v --print-out-paths)
# 3. The build used the /tmp/sand store, so prefix it to run the resulting binary.
/tmp/sand"$out"dyn-drvs-test-res (defined in default.nix) runs Sandstone inside a Nix build under recursive-nix: it generates the per-module derivation graph for example/, emits it as a dynamic derivation, and Nix then builds that into a runnable executable.
src/Sandstone/: the library. Makefile parsing and the module graph (GhcMakefile), derivation generation (WriteDerivation), the Nix CLI backend (NixCLI), and accumulating errors (Error).src-bin/: two demos.demo-cadrives a build from the outside against a local store;demo-dyn-drvruns inside a Nix build and is the body of the dynamic derivation.example/: a small Haskell project used as a build fixture (nested modules and anhs-bootcycle included).dep/: pinned dependencies as nix-thunk thunks (nixpkgs,nix, and thehnix-storefork).default.nix/release.nix: Nix entry points and thedyn-drvs-testwiring.
Sandstone is built and maintained by Obsidian Systems. We provide frontier engineering for high-assurance systems: we build production software in Haskell and Nix, and we're long-time stewards of open-source tooling like Obelisk, Reflex, and nix-thunk. We also contribute to Nix itself and to hnix-store, both of which Sandstone builds on.
If you're working on build systems, Nix, or Haskell and want a partner to help design, build, or ship it, we'd love to hear from you.
- Website: https://obsidian.systems
- Blog: https://blog.obsidian.systems
- GitHub: https://github.com/obsidiansystems
Sandstone is released under the BSD-3-Clause License, © 2025 Obsidian Systems LLC.