Managing cached NARs¶
Gradient caches hold many individual NARs. This page covers listing, inspecting, deleting, and uploading them through the CLI or the web UI.
CLI¶
gradient cache nar list <cache> [--hash <prefix>] [--package <substring>] \
[--sort created_at|nar_size|last_fetched_at] [--order asc|desc] \
[--page N] [--per-page N]
gradient cache nar show <cache> <hash>
gradient cache nar delete <cache> <hash> [-y]
gradient cache nar stats <cache>
gradient cache upload --nar-file <file.nar> --narinfo <file.narinfo> <cache>
gradient cache upload [--no-closure] <store-path>... <cache> # nix feature only
gradient cache nar list is paginated. Default page size is 50, max 200.
gradient cache nar delete prompts for confirmation unless -y/--yes is
passed. In --json mode, --yes is mandatory (no interactive prompt is
possible).
Uploading NARs¶
gradient cache upload pushes a NAR into a cache. Requires the writeStore
permission on the target cache.
No-nix mode (always available)¶
Upload a pre-dumped NAR file together with its narinfo metadata. No local Nix installation is required.
--narinfo points to a standard .narinfo file. The CLI parses it for
store path, NAR hash, NAR size, and references before submitting.
Nix mode (requires the nix Cargo feature)¶
When the CLI is built with the nix feature, store paths can be uploaded
directly from the local Nix daemon. Each path is resolved via harmonia,
NAR-dumped, zstd-compressed, and uploaded in one step.
# Upload a store path together with its full runtime closure (default)
gradient cache upload /nix/store/abc123-hello-2.12.1 my-cache
# Upload only the listed paths, skipping their runtime closure
gradient cache upload --no-closure /nix/store/abc123-hello-2.12.1 my-cache
By default each given path's full runtime reference closure is walked and every
reachable path is uploaded in dependency order. Pass --no-closure to upload
only the paths named on the command line.
Size cap¶
The server enforces a maximum upload size per NAR. The default is 512 MiB
and is controlled by the GRADIENT_MAX_NAR_UPLOAD_SIZE environment variable
on the server.
The bundled reverse proxy (nginx/Caddy) caps each HTTP request body at 100 MiB. NARs larger than that still upload because the CLI splits them across multiple chunked requests (see below); no single request exceeds the cap.
Backend endpoints¶
POST /api/v1/caches/{cache}/nars- single-shot multipart form with anarinfoJSON part and anarbinary part. Suitable for NARs under the reverse proxy's 100 MiB request limit.PUT /api/v1/caches/{cache}/nars/{hash}/chunk?offset=N- append one NAR slice to a server-side staging file. The CLI uses this to upload larger NARs in 32 MiB chunks so no single request exceeds the proxy limit.POST /api/v1/caches/{cache}/nars/{hash}/finalize- validate the fully staged NAR against its narinfo and ingest it.
The CLI automatically chunks; you do not call these endpoints by hand.
Web UI¶
Open the cache page (/caches/<name>) and click NARs in the header. The
page supports filtering by hash prefix and package substring, sorting by
created, size, or last fetched, and per-row delete (requires edit access to
the cache).
Ref-counted deletion¶
When a NAR is shared across multiple caches, deleting it from one cache only removes that cache's signature row. The underlying NAR blob stays alive and the other caches keep serving it. When the last cache holding a NAR deletes it:
- The signature row is removed (sync, in the request's DB transaction).
- The
cached_pathrow is deleted in the same transaction. - Any matching
derivation_output.is_cachedrows are set tofalse. - The NAR blob is removed from object storage asynchronously, after the HTTP response.
This mirrors how nix's own garbage collector handles paths with multiple references.
Permissions¶
- List / show / stats / available: anyone who can view the cache. Public caches are open; private caches require an API key or session belonging to the cache owner.
- Delete: the cache owner only. Matches existing
PATCH /caches/{cache}semantics. - Upload (
writeStore): callers must hold thewriteStorecache permission. Returns403otherwise.