Skip to content

About

Official CLI for use with ekapkgs

Resources

Code of conduct

Stars

21 stars

Watchers

2 watching

Forks

Repository files navigation

ekapkgs-cli

A unified Nix CLI that replaces several standalone tools with a single binary. Two binaries: ekapkgs (client) and ekapkgs-serve (server).

ekapkgs combines the functionality of:

  • nix — build, run, shell, develop, flake operations with integrated caching
  • nixos-rebuild — ekapkgs system for system configuration management with auto-rollback
  • home-manager — ekapkgs home for per-user dotfiles, packages, and services
  • nix-index — ekapkgs search for packages, options, and file search with cached indexes
  • nix-prefetch / nix-init — ekapkgs template for generating Nix expressions with GitHub metadata fetching
  • nix copy / cachix — ekapkgs cache with a negotiated binary cache protocol (single round trip)
  • nix-env / devenv — ekapkgs env for per-directory dev environments with shell hook activation
  • nix registry — ekapkgs registry for flake registry management
  • syft / trivy — ekapkgs closure sbom for CycloneDX SBOM generation from Nix closures
  • nix-ld + FHS — ekapkgs fuse for on-demand /usr/lib shared library serving via FUSE

Why

The standard nix binary cache protocol requires ~3N HTTP requests for an N-path closure (HEAD + GET narinfo + GET NAR, per path). ekapkgs sends the full set of needed hashes to the server in one gRPC call and gets back a manifest of everything available, with a topologically-sorted download plan.

The server also supports certificate-based signing (key rotation without client config changes) and serves as a drop-in nix binary cache for backward compatibility.

Client

Build, run, shell

ekapkgs build nixpkgs#hello
ekapkgs run nixpkgs#hello
ekapkgs shell nixpkgs#hello nixpkgs#jq
ekapkgs develop                         # cache-aware nix develop
ekapkgs develop .#devShells.x86_64-linux.default

Cache management

ekapkgs cache add mycache http://localhost:8080    # register a named cache
ekapkgs cache list                                 # show configured caches
ekapkgs cache remove mycache                       # remove a cache entry

ekapkgs cache push mycache nixpkgs#hello           # push using cache name
ekapkgs cache pull mycache nixpkgs#firefox          # pre-fetch closure

ekapkgs cache auth login mycache --token TOKEN     # save push credentials
ekapkgs cache auth token mycache --admin-token SECRET  # mint token from server
ekapkgs cache auth status                          # show caches and auth status

The EKAPKGS_TOKEN environment variable can be used for push authentication, which is useful in nix post-build hooks with a credentials file.

Closure analysis

ekapkgs closure size nixpkgs#hello          # size breakdown by path
ekapkgs closure why-depends nixpkgs#hello nixpkgs#glibc
ekapkgs closure diff nixpkgs#hello nixpkgs#curl
ekapkgs closure sbom nixpkgs#hello          # CycloneDX 1.7 SBOM (runtime closure)
ekapkgs closure sbom nixpkgs#hello --buildtime        # include build-time deps
ekapkgs closure sbom nixpkgs#hello --format csv       # CSV output
ekapkgs closure sbom nixpkgs#hello -o sbom.cdx.json   # write to file
ekapkgs closure sbom-diff OLD NEW                      # diff two closures by package
ekapkgs closure sbom-diff OLD NEW --format json        # structured JSON diff
ekapkgs closure sbom-diff OLD NEW --format csv         # CSV diff

Package metadata (CPE, PURL, license, description, source URLs, position) is extracted via nix eval --apply with recursive dependency traversal, enriching the full closure. Multi-output packages are coalesced into single components. Each component includes nix:output_path and nix:position properties, source distribution external references, and is typed as application when meta.mainProgram is defined. For ekaos system closures, additional metadata (role, provenance) comes from the embedded package manifest.

sbom-diff compares closures by package name and reports version changes, added/removed packages, and metadata changes including new or resolved CVEs, license changes, and source provenance shifts. Each entry includes a PURL identifier (when available) for downstream tooling correlation.

Build log and dry run

ekapkgs log nixpkgs#hello                  # show build log
ekapkgs dry-run nixpkgs#hello              # build plan with cache breakdown

Store management

ekapkgs store gc                           # garbage collect
ekapkgs store gc --older-than 30d          # delete paths older than 30 days
ekapkgs store gc --dry-run                 # preview what would be deleted
ekapkgs store optimize                     # deduplicate via hardlinks
ekapkgs store verify --all                 # check store integrity
ekapkgs store verify --all --repair        # repair invalid paths

Flake introspection

ekapkgs flake show                         # colored output tree
ekapkgs flake metadata                     # input dependency tree with revisions
ekapkgs flake update-diff nixpkgs          # show closure diff before committing update

Flake registry

Manage flake registries (symbolic identifiers like nixpkgs that map to full flake URLs):

ekapkgs registry list                      # show all registry entries
ekapkgs registry add nixpkgs github:NixOS/nixpkgs  # add/replace entry
ekapkgs registry remove nixpkgs            # remove entry
ekapkgs registry pin nixpkgs               # pin to current revision
ekapkgs registry unpin nixpkgs             # remove pin (restore floating)
ekapkgs registry resolve nixpkgs           # show resolved URL

System management

Replaces nixos-rebuild for local system configuration:

ekapkgs system switch                      # build and activate
ekapkgs system boot                        # add boot entry, activate on reboot
ekapkgs system test                        # activate without updating boot entry
ekapkgs system build                       # build only, print store path
ekapkgs system list-generations            # list system generations
ekapkgs system rollback                    # roll back to previous generation
ekapkgs system rollback --dry-run          # preview rollback
ekapkgs system prune-boot-entries          # remove orphaned boot entries from ESP
ekapkgs system prune-boot-entries --gc     # garbage collect first, then prune
ekapkgs system prune-boot-entries --dry-run

If activation fails during system switch, the previous profile is automatically restored and re-activated.

Imperative system packages

Add packages to the system imperatively. Managed via ~/.config/ekapkgs/system-packages.toml with immediate install (via sudo) into a dedicated nix profile at /nix/var/nix/profiles/ekapkgs-system-packages:

ekapkgs system packages add htop vim         # add and install (requires sudo)
ekapkgs system packages remove htop          # remove from manifest and profile
ekapkgs system packages list                 # list installed packages
ekapkgs system packages export -o sys.toml   # export manifest
ekapkgs system packages import sys.toml      # import and install

Home configuration

Replaces home-manager for per-user dotfiles, packages, and environment:

ekapkgs home switch                        # build and activate home config
ekapkgs home build                         # build only, print store path
ekapkgs home generations                   # list home generations
ekapkgs home rollback                      # roll back to previous generation

If activation fails during home switch, the previous generation is automatically re-activated.

Home configuration is defined in the ekaos module system under users.users.<name> and built via system.build.home.

Imperative home packages

Add packages to your home without editing nix configuration. Managed via ~/.config/ekapkgs/home-packages.toml with immediate install into a dedicated nix profile at ~/.ekapkgs-packages:

ekapkgs home packages add alacritty ripgrep  # add and install immediately
ekapkgs home packages remove alacritty       # remove from manifest and profile
ekapkgs home packages list                   # list installed packages
ekapkgs home packages list --json            # machine-readable output
ekapkgs home packages export                 # print manifest to stdout
ekapkgs home packages export -o pkgs.toml    # export to file
ekapkgs home packages import pkgs.toml       # import and install
ekapkgs home packages import pkgs.toml --merge  # merge with existing

Packages can come from different flakes:

ekapkgs home packages add my-tool --flake github:user/repo

Directory environments

Per-directory package environments with automatic activation. Each directory can have a .ekapkgs-env.toml manifest specifying packages and flake dev shells.

When flake dev shells are configured, the shell hook spawns a child shell with the full dev environment (environment variables, compiler flags, pkg-config paths, etc.) pre-loaded from a cached nix print-dev-env render. Entering the directory pushes the child shell; leaving it pops back to the parent. The environment is pre-rendered on first use and re-rendered automatically when manifest or flake files change.

For packages-only manifests (no [[flakes]]), the hook uses lightweight PATH-only activation without spawning a child shell.

ekapkgs env init                           # create .ekapkgs-env.toml
ekapkgs env add jq ripgrep fd              # add packages
ekapkgs env remove fd                      # remove a package
ekapkgs env list                           # show packages and flakes
ekapkgs env reload                         # rebuild profile from manifest
ekapkgs env allow                          # trust this environment
ekapkgs env disallow                       # revoke trust

Composable flake dev shells

Multiple flakes can be composed into a single environment. Each can specify a dev shell attribute, pinned revision, and input overrides:

ekapkgs env flake-add .                    # add local flake
ekapkgs env flake-add github:user/repo --devshell python
ekapkgs env flake-add github:other/tools --rev abc123
ekapkgs env flake-add github:foo/bar --override-input nixpkgs=github:NixOS/nixpkgs/nixos-24.05
ekapkgs env flake-remove github:user/repo
ekapkgs env flake-pin github:other/tools   # pin to current revision
ekapkgs env flake-pin github:other/tools --rev deadbeef

Shell hooks

Add to your shell configuration for automatic activation on cd:

# ~/.bashrc
eval "$(ekapkgs env hook bash)"

# ~/.zshrc
eval "$(ekapkgs env hook zsh)"

# ~/.config/fish/config.fish
ekapkgs env hook fish | source

The hook automatically activates when you enter a directory with a trusted .ekapkgs-env.toml, deactivates when you leave, and reloads when flake.nix, flake.lock, or the manifest changes.

Manifest format

version = 1
flake = "nixpkgs"

[[packages]]
name = "jq"

[[packages]]
name = "my-tool"
flake = "github:user/repo"

[[flakes]]
ref_ = "."
devshell = "default"

[[flakes]]
ref_ = "github:other/tools"
devshell = "default"
rev = "abc123def456"

[flakes.inputs]
nixpkgs = "github:NixOS/nixpkgs/nixos-24.05"

Package templates

Generate core-pkgs-compatible Nix package expressions from templates. Supports auto-detection from source files and GitHub URL metadata fetching with source hash prefetching.

ekapkgs template rust --stdout                 # print Rust template to stdout
ekapkgs template python default.nix            # write Python template to file
ekapkgs template auto                          # auto-detect from source files
ekapkgs template go --from-url github.com/user/repo   # fetch metadata from GitHub
ekapkgs template cmake --pname mylib --version 1.0.0  # override metadata
ekapkgs template meson --license lgpl21Plus            # set license

Available templates: stdenv, cmake, meson, rust, go, python, auto.

Generated expressions use core-pkgs conventions: finalAttrs pattern, tag instead of rev in fetchers, cmakeEntries/mesonEntries for structured build options, pyproject = true with build-system for Python, and no maintainers in meta.

Search

Search packages, configuration options, or files. Indexes are cached locally as ZSTD-compressed JSON and auto-generated on first use.

ekapkgs search packages hello              # search by name/description
ekapkgs search packages hello --json       # machine-readable output
ekapkgs search options boot.loader         # search configuration options
ekapkgs search files bin/hello             # find which package provides a file
ekapkgs search update                      # regenerate indexes
ekapkgs search update --remote https://...  # download pre-built indexes

On-demand shared libraries (FUSE)

Mount a FUSE filesystem at /usr/lib to serve shared libraries on-demand from a Nix binary cache. Designed for use with nix-ld so unpatched binaries with /usr/lib on their RPATH can transparently load Nix-provided .so files without pre-installing packages.

When a process opens a library (e.g. libxml2.so.2), the FUSE layer resolves the soname to a Nix package via a CI-generated index, downloads the package if needed, and serves the file from /nix/store.

sudo ekapkgs fuse mount                    # mount at /usr/lib (requires root)
sudo ekapkgs fuse mount --foreground       # stay in foreground (for systemd)
sudo ekapkgs fuse mount --mountpoint /opt/lib  # custom mount point
ekapkgs fuse status                        # check if mounted
sudo ekapkgs fuse unmount                  # unmount

Requires a soname index (sonames.json.zst) available via index_url in the client config. Transitive dependencies of Nix-built libraries use per-DSO resolution caches and do not hit /usr/lib.

Remote deployment

ekapkgs deploy .#nixosConfigurations.prod --target-host prod-server
ekapkgs deploy .#nixosConfigurations.prod --target-host prod-server --mode boot
ekapkgs deploy .#nixosConfigurations.prod --target-host prod-server --build-host builder
ekapkgs deploy .#nixosConfigurations.prod --target-host prod-server --dry-run

If remote activation fails, deploy prints the SSH command to roll back the target host to its previous configuration.

System diagnostics

ekapkgs doctor                             # check nix, store, caches, disk space

Shell completions

ekapkgs completions bash > ~/.bash_completion.d/ekapkgs
ekapkgs completions zsh > ~/.zsh/completions/_ekapkgs
ekapkgs completions fish > ~/.config/fish/completions/ekapkgs.fish

Configuration

Config at ~/.config/ekapkgs/config.toml:

[[caches]]
name = "mycache"
url = "https://cache.ekapkgs.org"
token = "ekap_..."
priority = 10

Server

# Quick start — serve from local nix store
ekapkgs-serve --signing-key cache-key.sec --storage nix-store

# With config file
ekapkgs-serve --config /etc/ekapkgs-serve/config.toml

# Token management (offline — requires server restart)
ekapkgs-serve token create ci-main
ekapkgs-serve token create ci-pr --read-only
ekapkgs-serve token list
ekapkgs-serve token revoke ci-main

# Dynamic token minting (on a running server, no restart)
# Set EKAPKGS_ADMIN_TOKEN via env var or systemd EnvironmentFile
EKAPKGS_ADMIN_TOKEN=secret ekapkgs-serve --signing-key key --storage /tmp/cache
ekapkgs cache auth token mycache --admin-token secret

# Certificate signing (optional)
ekapkgs-serve generate-ca ekapkgs-root-ca-1
ekapkgs-serve issue-cert cache-2025 --ca-key ekapkgs-root-ca-1.sec --ca-name ekapkgs-root-ca-1

Server config:

[server]
bind = "0.0.0.0:8080"

[storage]
backend = "filesystem"  # or "nix-store" or "castore"
path = "/var/cache/ekapkgs"

[storage.gc]
max_size = "50GiB"
gc_interval_secs = 300

[signing]
secret_key_file = "/etc/ekapkgs-serve/cache-key.sec"

[signing.certificate]
cert_file = "/etc/ekapkgs-serve/cache-2025.cert.json"
private_key_file = "/etc/ekapkgs-serve/cache-2025.key"

[auth]
write_tokens = ["legacy-token-if-needed"]

Storage backends

  • filesystem — reads/writes a cache directory ({hash}.narinfo + nar/). Supports LRU garbage collection with configurable size limits.
  • nix-store — serves directly from /nix/store via the nix daemon, like nix-serve. No cache directory needed.
  • castore — content-addressed storage with chunk-level deduplication. Decomposes NARs into a blake3 Merkle tree of FastCDC chunks. Identical file content across store paths is stored once. Clients negotiate at the chunk level and download only data they don't already have locally. Supports LRU garbage collection. Typical storage savings: 30-50%.

Nix compatibility

The server speaks the standard nix binary cache protocol on the same port as gRPC. Plain nix build --substituters http://your-server works unchanged.

Building

Requires Rust 1.85+, protoc, and libfuse3-dev:

nix shell nixpkgs#gcc nixpkgs#protobuf nixpkgs#fuse3
cargo build --workspace
cargo test --workspace

Project structure

crates/
  ekapkgs/            # client binary
  ekapkgs-serve/      # server binary
  ekapkgs-protocol/   # protobuf types + cert verification (no IO)
  ekapkgs-nix/        # nix CLI wrapping
  ekapkgs-ui/         # logging, progress bars
proto/
  ekapkgs/v1/         # canonical .proto definitions

License

MPL-2.0

About

Official CLI for use with ekapkgs

Resources

Code of conduct

Stars

21 stars

Watchers

2 watching

Forks

Releases

Packages

Contributors

Languages