nova-cache 0.12.0.0 → 0.12.0.1
raw patch · 3 files changed
+29/−22 lines, 3 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
Files
- CHANGELOG.md +4/−0
- README.md +19/−14
- nova-cache.cabal +6/−8
CHANGELOG.md view
@@ -1,5 +1,9 @@ # Changelog +## 0.12.0.1 - 2026-11-05++- **The README in the package describes 0.12: its version bounds, the server's required `CACHE_API_KEY`, and which codecs are pure.** The README in the 0.12.0.0 tarball, the one Hackage shows, gave bounds that exclude 0.12 itself and a server command that exits at once, since the server refuses to start without `CACHE_API_KEY` unless given `--allow-open-writes`. It also called the WAI server optional, where `NovaCache.Server` is always part of the library and only the executable sits behind `-f server`, and counted one-shot xz decompression and zstd compression among the `IO` operations, where both are pure. The test command now passes `--enable-tests`, as CI's does.+ ## 0.12.0.0 - 2026-11-01 - **`nova-cache-server reclaim-nars` lists, and with `--delete` removes, the NARs no narinfo references.** A client uploads a path's NAR before its narinfo, as upstream's `BinaryCacheStore::addToStoreCommon` does, so a NAR whose narinfo the server refuses, or whose push dies before the narinfo, stayed under `nar/` for good: the server has no route that deletes, and gains none. The subcommand runs on the host against the store directory. A narinfo names its NAR by its `URL` alone, as upstream reads it (`getFile(info->url)`), with the compression suffix part of the name. Three rules guard a run. A NAR is eligible only once it is two weeks old by its modification time (`--min-age-hours` overrides), because a NAR is unreferenced until its push sends the narinfo and nothing bounds how long a push takes; git's `gc.pruneExpire` gives unreferenced objects the same two weeks against the same race. A narinfo that cannot be read, parsed, or resolved to `nar/<file>` blocks the whole run instead of being skipped, since the NAR it names would otherwise look unreferenced. And a store holding NARs and no narinfo at all blocks it too, since one that lost its `narinfo/` directory, which the server recreates empty when it starts, looks the same as one whose every NAR is unreferenced. A run is not atomic with the server, so it belongs on a cache with no push in progress: a narinfo that arrives during a run for a NAR past the minimum age, as upstream's `nix copy` can send after skipping a NAR already present, loses that NAR. Upstream ships no garbage collection for binary caches, so there is no behavior to match beyond the URL. The library gains `NovaCache.Reclaim` (the pure selection and the store scan) and, in `NovaCache.Store`, `listNarFiles`, `listNarInfoFiles`, `removeNar` and `fileStoreAt`, a constructor that creates no directories; `time` joins the dependencies.
README.md view
@@ -6,26 +6,28 @@ nova-cache implements the Nix binary cache protocol in Haskell: nix-base32, NAR serialization (whole-tree and streaming), narinfo parsing, store paths,-Ed25519 signing and upload validation, and an optional WAI cache server.+Ed25519 signing and upload validation, a WAI application for a cache server,+and an optional server executable. Bounded xz, zstd and bzip2 codecs are the public `nova-cache:xz`, `nova-cache:zstandard` and `nova-cache:bzip2` sublibraries, and the zstd codec also compresses for uploads. -Parsing, formatting, hashing, signing and validation are pure. Reading and-writing trees, hashing files, the codecs, the store and the server run in-`IO`. nova-cache is the protocol layer under+Parsing, formatting, hashing, signing, validation, one-shot xz decompression+and zstd compression are pure. Reading and writing trees, hashing files, the+streaming codecs, zstd and bzip2 decompression, the store and the server run+in `IO`. nova-cache is the protocol layer under [nova-nix](https://github.com/Novavero-AI/nova-nix). ## Installation ```cabal-build-depends: nova-cache >= 0.11 && < 0.12+build-depends: nova-cache >= 0.12 && < 0.13 ``` The codecs are separate sublibraries, named alongside the main library: ```cabal-build-depends: nova-cache:{nova-cache, xz, zstandard} >= 0.11 && < 0.12+build-depends: nova-cache:{nova-cache, xz, zstandard} >= 0.12 && < 0.13 ``` libbz2 and libzstd are bundled. liblzma comes from the `xz` package, which@@ -50,7 +52,8 @@ -- Parse a narinfo and sign it case (parseNarInfo raw, parseSecretKey "mykey:base64...") of (Right ni, Right sk) -> print (sign sk ni) -- Right "mykey:<base64 sig>"- _ -> error "parse failed"+ (Left err, _) -> putStrLn err+ (_, Left err) -> putStrLn err ``` ```haskell@@ -83,9 +86,12 @@ ## Server ```bash-cabal run --flag server nova-cache-server -- --port 5000 --store ./nix-cache+CACHE_API_KEY=change-me cabal run --flag server nova-cache-server -- --port 5000 --store ./nix-cache ``` +Without `CACHE_API_KEY` the server refuses to start. `--allow-open-writes` runs+it with unauthenticated writes, for local testing only.+ The protocol lives in the `NovaCache.Server` library module as a WAI `Application`, so the cache can be embedded in another server with its own root page. The bundled executable is one such embedding.@@ -147,11 +153,10 @@ ### Public cache -A public instance runs at `cache.novavero.ai`. It serves store paths that-nova-nix builds, which today means its Windows MinGW-w64 toolchain seed-([nova-nix#207](https://github.com/Novavero-AI/nova-nix/issues/207)). To use-it from Nix, add it to `/etc/nix/nix.conf`. On a multi-user install, Nix-ignores substituters set by users it does not trust.+A public instance runs at `cache.novavero.ai`. It serves the closure of+nova-nix's Windows `hello` recipe, which nova-nix's CI publishes on every push+to its main branch. To use it from Nix, add it to `/etc/nix/nix.conf`. On a+multi-user install, Nix ignores substituters set by users it does not trust. ``` extra-substituters = https://cache.novavero.ai@@ -165,7 +170,7 @@ ```bash cabal update cabal build all -f server --enable-tests --ghc-options="-Werror"-cabal test -f server --ghc-options="-Werror"+cabal test -f server --enable-tests --ghc-options="-Werror" ``` ## License
nova-cache.cabal view
@@ -4,7 +4,7 @@ -- 3.0 an in-package component name shadowed its external namesake -- (the hazard the zstandard library's name dodges, documented there). name: nova-cache-version: 0.12.0.0+version: 0.12.0.1 synopsis: Nix binary cache protocol: NAR, narinfo, signing and a WAI server description: The Nix binary cache protocol in Haskell: nix-base32, NAR serialization@@ -84,13 +84,11 @@ -Wincomplete-record-updates -Wincomplete-uni-patterns --- The bounded xz decoder as a solver-visible opt-in: a consumer that--- substitutes foreign caches writes build-depends: nova-cache:xz and--- gets the module; everyone else never builds the xz package's--- bundled liblzma. This replaces the manual xz flag - a dependency's flag--- cannot be set from a consumer's .cabal file, which forced mirror--- flags and matching constraints downstream (nova-nix#27) - while--- keeping the 0.5.0.0 lesson: xz stays out of the default install.+-- The bounded xz decoder as a public sublibrary: a consumer that+-- substitutes from xz caches depends on nova-cache:xz, and nobody else+-- builds the xz package's bundled liblzma. A sublibrary rather than a+-- flag, because a dependency's flag cannot be set from a consumer's+-- .cabal file. library xz visibility: public exposed-modules: NovaCache.Xz