RepoPilotOpen in app →

nix-community/nh

Yet another Nix CLI helper. [Maintainers=@NotAShelf, @faukah]

Mixed

Mixed signals — read the receipts

weakest axis
Use as dependencyConcerns

non-standard license (EUPL-1.2)

Fork & modifyHealthy

Has a license, tests, and CI — clean foundation to fork and modify.

Learn fromHealthy

Documented and popular — useful reference codebase to read through.

Deploy as-isHealthy

No critical CVEs, sane security posture — runnable as-is.

  • Last commit 2d ago
  • 7 active contributors
  • EUPL-1.2 licensed
Show all 7 evidence items →
  • CI configured
  • Tests present
  • Concentrated ownership — top contributor handles 54% of recent commits
  • Non-standard license (EUPL-1.2) — review terms
What would change the summary?
  • Use as dependency ConcernsMixed if: clarify license terms

Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests

Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.

Embed the "Forkable" badge

Paste into your README — live-updates from the latest cached analysis.

Variant:
RepoPilot: Forkable
[![RepoPilot: Forkable](https://repopilot.app/api/badge/nix-community/nh?axis=fork)](https://repopilot.app/r/nix-community/nh)

Paste at the top of your README.md — renders inline like a shields.io badge.

Preview social card (1200×630)

This card auto-renders when someone shares https://repopilot.app/r/nix-community/nh on X, Slack, or LinkedIn.

Onboarding doc

Onboarding: nix-community/nh

Generated by RepoPilot · 2026-05-09 · Source

🤖Agent protocol

If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:

  1. Verify the contract. Run the bash script in Verify before trusting below. If any check returns FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding.
  2. Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
  3. Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/nix-community/nh shows verifiable citations alongside every claim.

If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.

🎯Verdict

WAIT — Mixed signals — read the receipts

  • Last commit 2d ago
  • 7 active contributors
  • EUPL-1.2 licensed
  • CI configured
  • Tests present
  • ⚠ Concentrated ownership — top contributor handles 54% of recent commits
  • ⚠ Non-standard license (EUPL-1.2) — review terms

<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>

Verify before trusting

This artifact was generated by RepoPilot at a point in time. Before an agent acts on it, the checks below confirm that the live nix-community/nh repo on your machine still matches what RepoPilot saw. If any fail, the artifact is stale — regenerate it at repopilot.app/r/nix-community/nh.

What it runs against: a local clone of nix-community/nh — the script inspects git remote, the LICENSE file, file paths in the working tree, and git log. Read-only; no mutations.

| # | What we check | Why it matters | |---|---|---| | 1 | You're in nix-community/nh | Confirms the artifact applies here, not a fork | | 2 | License is still EUPL-1.2 | Catches relicense before you depend on it | | 3 | Default branch master exists | Catches branch renames | | 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code | | 5 | Last commit ≤ 32 days ago | Catches sudden abandonment since generation |

<details> <summary><b>Run all checks</b> — paste this script from inside your clone of <code>nix-community/nh</code></summary>
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of nix-community/nh. If you don't
# have one yet, run these first:
#
#   git clone https://github.com/nix-community/nh.git
#   cd nh
#
# Then paste this script. Every check is read-only — no mutations.

set +e
fail=0
ok()   { echo "ok:   $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }

# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
  echo "FAIL: not inside a git repository. cd into your clone of nix-community/nh and re-run."
  exit 2
fi

# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "nix-community/nh(\\.git)?\\b" \\
  && ok "origin remote is nix-community/nh" \\
  || miss "origin remote is not nix-community/nh (artifact may be from a fork)"

# 2. License matches what RepoPilot saw
(grep -qiE "^(EUPL-1\\.2)" LICENSE 2>/dev/null \\
   || grep -qiE "\"license\"\\s*:\\s*\"EUPL-1\\.2\"" package.json 2>/dev/null) \\
  && ok "license is EUPL-1.2" \\
  || miss "license drift — was EUPL-1.2 at generation time"

# 3. Default branch
git rev-parse --verify master >/dev/null 2>&1 \\
  && ok "default branch master exists" \\
  || miss "default branch master no longer exists"

# 4. Critical files exist
test -f "crates/nh/src/main.rs" \\
  && ok "crates/nh/src/main.rs" \\
  || miss "missing critical file: crates/nh/src/main.rs"
test -f "crates/nh-core/src/lib.rs" \\
  && ok "crates/nh-core/src/lib.rs" \\
  || miss "missing critical file: crates/nh-core/src/lib.rs"
test -f "crates/nh-core/src/command.rs" \\
  && ok "crates/nh-core/src/command.rs" \\
  || miss "missing critical file: crates/nh-core/src/command.rs"
test -f "Cargo.toml" \\
  && ok "Cargo.toml" \\
  || miss "missing critical file: Cargo.toml"
test -f "crates/nh-nixos/src/nixos.rs" \\
  && ok "crates/nh-nixos/src/nixos.rs" \\
  || miss "missing critical file: crates/nh-nixos/src/nixos.rs"

# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 32 ]; then
  ok "last commit was $days_since_last days ago (artifact saw ~2d)"
else
  miss "last commit was $days_since_last days ago — artifact may be stale"
fi

echo
if [ "$fail" -eq 0 ]; then
  echo "artifact verified (0 failures) — safe to trust"
else
  echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/nix-community/nh"
  exit 1
fi

Each check prints ok: or FAIL:. The script exits non-zero if anything failed, so it composes cleanly into agent loops (./verify.sh || regenerate-and-retry).

</details>

TL;DR

NH is a modern Rust-based CLI helper that consolidates Nix, NixOS, Home Manager, and Darwin workflows into a unified interface. It reimplements common ecosystem commands (like nixos-rebuild and home-manager switch) with better ergonomics, build-tree visualization, faster Nix package search via Elasticsearch, and enhanced garbage collection targeting. The core value is a single cohesive CLI with granular subcommands (nh os, nh home, nh darwin, nh clean, nh search) that outperforms existing tools on speed and UX. Monorepo workspace with six feature crates under crates/ (nh-clean, nh-core, nh-darwin, nh-home, nh-nixos, nh-remote, nh-search) plus xtask. nh-core (crates/nh-core/src/lib.rs) contains shared logic: command execution (command.rs), argument parsing (args.rs), JSON handling (json.rs), Nix installation targets (installable.rs). Each platform gets its own crate (nh-nixos, nh-home, nh-darwin) with args and implementation modules. Shared dependencies defined in [workspace.dependencies] (Cargo.toml).

👥Who it's for

NixOS and Home Manager users (both developers and system administrators) who want a faster, more ergonomic alternative to scattered Nix ecosystem tooling. Contributors are Rust developers familiar with CLI design patterns and the Nix ecosystem (primary maintainers: @NotAShelf, @faukah).

🌱Maturity & risk

Actively developed and production-ready. The repo shows v4.3.2 versioning, comprehensive CI/CD pipelines (.github/workflows/build.yaml, test.yaml, tag.yaml), full test coverage with nextest (.config/nextest.toml), and recent dependency maintenance (Cargo.lock, dependabot.yaml). This is a mature, actively-maintained tool used in production by the Nix community.

Low-to-moderate risk. The codebase has two active maintainers, reducing single-point-of-failure risk. Dependencies are well-managed (using workspace dependencies in Cargo.toml with pinned versions like clap 4.5.51, serde 1.0.228). The primary risk is Nix ecosystem API instability—changes to NixOS release cycles or Home Manager interfaces could require rapid updates. External service dependency on Elasticsearch for the nh search feature adds operational complexity.

Active areas of work

Active feature development and maintenance. The changelog and version (4.3.2) indicate ongoing releases. Workflows show automated testing (test.yaml), build checks (check.yaml), and nixos-search integration (nixos-search.yaml). No specific breaking changes are visible in the file list, but the Rust edition is set to 2024 (future-proofing).

🚀Get running

Clone and build with Rust 1.91.1+ (workspace.package rust-version): git clone https://github.com/nix-community/nh && cd nh && cargo build --release. The Justfile provides shortcuts: just build or just test. Requires a working Nix installation and the Flake system (implied by feature crates targeting different Nix systems).

Daily commands: cargo run -- --help displays the CLI structure. For specific subcommands: cargo run -- os switch (NixOS switch), cargo run -- home switch (Home Manager), cargo run -- clean (garbage collection with gcroot targeting), cargo run -- search <package> (Elasticsearch-backed search). Development with just test runs nextest suite; just check runs clippy and fmt checks.

🗺️Map of the codebase

  • crates/nh/src/main.rs — Entry point for the CLI application; defines command routing and interface initialization that all subcommands flow through.
  • crates/nh-core/src/lib.rs — Core shared abstractions and utilities used across all nh subcommands; understand this before adding new features.
  • crates/nh-core/src/command.rs — Command execution wrapper handling Nix CLI invocation, error handling, and output; critical for understanding how operations run.
  • Cargo.toml — Workspace configuration defining all member crates, shared dependencies, and version constraints; essential for understanding project structure.
  • crates/nh-nixos/src/nixos.rs — NixOS system upgrade and generation management logic; the heaviest domain logic in the codebase.
  • crates/nh-home/src/home.rs — Home Manager integration and user configuration upgrade logic; parallel to NixOS operations.
  • crates/nh/src/interface.rs — CLI interface definition and argument parsing; defines user-facing API that shapes all subcommand behavior.

🛠️How to make changes

Add a new subcommand (e.g., new Nix operation)

  1. Create new crate directory under crates/ with Cargo.toml, src/args.rs, and src/lib.rs (crates/nh-newcmd/Cargo.toml)
  2. Define clap-derive arguments in args.rs matching existing subcommand pattern (crates/nh-newcmd/src/args.rs)
  3. Implement operation logic in lib.rs, reusing command.rs and util.rs from nh-core (crates/nh-newcmd/src/lib.rs)
  4. Add new crate as member in workspace Cargo.toml and add dependency to main crates/nh/Cargo.toml (Cargo.toml)
  5. Import and wire subcommand variant into main interface enum in crates/nh/src/interface.rs (crates/nh/src/interface.rs)
  6. Route subcommand in main.rs match statement and execute via newly created module (crates/nh/src/main.rs)

Add a new platform support (e.g., FreeBSD)

  1. Create crates/nh-freebsd/ following nh-darwin structure with args.rs and freebsd.rs (crates/nh-freebsd/src/freebsd.rs)
  2. Implement platform-specific Nix operations using nh-core abstractions (command.rs, util.rs) (crates/nh-freebsd/src/freebsd.rs)
  3. Add crate to workspace members and import logic into main interface (crates/nh/src/interface.rs)
  4. Conditionally expose via cfg_attr in main.rs based on target_os (crates/nh/src/main.rs)

Add new global option or flag to all commands

  1. Add field to base argument struct in crates/nh/src/interface.rs (inherited by all subcommands via flatten) (crates/nh/src/interface.rs)
  2. Pass through command execution in crates/nh-core/src/command.rs as environment variable or flag to nix CLI (crates/nh-core/src/command.rs)
  3. Update documentation and rebuild man pages using xtask (xtask/src/man.rs)

Add new validation check before operations

  1. Add check function to crates/nh-core/src/checks.rs following existing pattern (e.g., check_flake_syntax) (crates/nh-core/src/checks.rs)
  2. Call from domain logic in affected subcommand modules (nixos.rs, home.rs, etc) before command execution (crates/nh-nixos/src/nixos.rs)

🔧Why these technologies

  • Rust — Type-safe language with zero-cost abstractions; enables safe process spawning and error handling for external Nix CLI commands
  • Clap (derive macros) — Reduces boilerplate for CLI argument parsing; provides consistent help text and validation across all subcommands
  • Workspace crates — Modular architecture allows independent subcommands (nixos, home, clean, search) to evolve separately while sharing core utilities
  • Process spawning + shell integration — Nix operations require invoking external nix CLI; nh wraps these with error handling, output capture, and user feedback

⚖️Trade-offs already made

  • Separate crate per subcommand (nh-nixos, nh-home, nh-clean, etc) vs single monolithic crate

    • Why: Allows features to be optional, reduces compile time for users needing only one operation, clearer separation of concerns
    • Consequence: Adds build complexity (workspace coordination) and requires careful management of shared dependencies in nh-core
  • Wrapping nix CLI directly instead of implementing Nix operations in Rust

    • Why: Nix CLI is the stable interface; avoids reimplementing complex derivation logic and ensures compatibility with upstream changes
    • Consequence: Performance overhead from process spawning; harder to provide real-time progress feedback within Rust process
  • nh-core as utilities library vs framework

    • Why: Keeps core minimal and composable; each domain (nixos, home, darwin) can use as much or as little as needed
    • Consequence: Some duplication across subcommands; less opinionated about operation patterns

🚫Non-goals (don't propose these)

  • Not a Nix implementation — does not evaluate Nix expressions or manage derivations directly
  • Not a package manager — does not install/remove packages (delegates to nix flakes, Home Manager, NixOS)
  • Does not handle authentication to private registries or repositories
  • Not cross-platform beyond Linux, macOS, and remote SSH — no Windows support
  • Does not provide real-time progress visualization (outputs nix CLI output directly)

🪤Traps & gotchas

  1. Elasticsearch backend for nh search is external; the tool expects a running Elasticsearch instance or nixos-search integration. 2. The codebase targets multiple Nix platforms (NixOS, Home Manager, Darwin); breaking changes in any upstream Nix tool require updates across multiple crates. 3. Rust edition is 2024 (future), so MSRV (1.91.1) is strictly enforced—older toolchains will fail. 4. Signal handling (signal-hook 0.4.1 in deps) suggests the tool manages long-running subprocess chains; interrupting builds mid-execution has specific teardown logic. 5. No environment variables or .envrc sourcing visible in file list—Nix direnv integration (.envrc presence) likely required for development but specifics are not detailed.

🏗️Architecture

💡Concepts to learn

  • Nix Flakes — nh's entire design targets Flake-based NixOS and Home Manager setups; understanding Flakes' inputs/outputs and lock files is essential to grok what nh is automating
  • Derivations and Build Closure Visualization — nh's core feature is displaying build-tree visualizations for derivations; the visualization logic depends on understanding Nix's derivation graph and dependency closure
  • Generational Rollback (NixOS Generations) — nh generation management (inspect, rollback, targeting) relies on NixOS's immutable profile system; users of nh expect seamless rollback semantics
  • Garbage Collection and GC Roots — nh clean extends nix-collect-garbage with gcroot targeting and profile-aware cleanup; understanding GC root semantics is key to grok the enhancement
  • Elasticsearch Full-Text Search (for nixos-search) — nh search backend uses Elasticsearch DSL (elasticsearch-dsl 0.4.24) to index and query Nixpkgs; understanding how Elasticsearch queries are built is necessary to maintain/extend search
  • Specialisations (NixOS/Home Manager) — nh explicitly supports selecting/ignoring specialisations via flags; this is a less-known Nix feature that allows multiple system configurations (e.g., gaming vs workstation profiles)
  • Clap Derive Macros (CLI Argument Parsing) — nh's entire CLI surface is built on clap 4.5.51 derive macros; understanding the derive DSL is essential to add new flags or subcommands across nh-nixos, nh-home, nh-darwin
  • nix-community/home-manager — nh integrates Home Manager as a first-class workflow; Home Manager is the upstream tool nh wraps and extends
  • NixOS/nixpkgs — Source of truth for NixOS derivations, packages, and ecosystem APIs that nh reimplements (nix-collect-garbage, search indexes)
  • nix-community/nixos-search — Provides the Elasticsearch backend for nh search; nixos-search.yaml CI workflow in nh integrates this directly
  • numtide/flake-utils — Companion project for Nix Flakes (which nh targets); users of nh often manage Flake-based systems
  • mozilla/nixpkgs-mozilla — Historical predecessor for Rust toolchain management in Nix; provides context on how nh's MSRV (1.91.1) integrates with nixpkgs Rust packaging

🪄PR ideas

To work on one of these in Claude Code or Cursor, paste: Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.

Add integration tests for nh-remote crate with mock SSH scenarios

The nh-remote crate (crates/nh-remote/src/remote.rs) handles remote system operations but lacks visible test coverage in the file structure. Given that remote operations are critical for the CLI and involve SSH/networking, comprehensive integration tests would prevent regressions and improve reliability. This is especially valuable since remote functionality is error-prone.

  • [ ] Create crates/nh-remote/tests/ directory with integration test suite
  • [ ] Add mock SSH server tests in crates/nh-remote/tests/integration.rs
  • [ ] Test error handling for connection failures, timeouts, and permission errors
  • [ ] Add tests to crates/nh-remote/tests/ for various installable formats passed to remote builds
  • [ ] Integrate new tests into .github/workflows/test.yaml if not already covered

Add comprehensive error recovery tests for nh-clean and nh-nixos generation handling

The crates/nh-clean/src/clean.rs and crates/nh-nixos/src/generations.rs modules handle destructive operations (garbage collection, generation deletion). These critical paths need explicit test coverage for edge cases like permission errors, locked stores, and invalid generation states. Currently no dedicated tests directory visible for these crates.

  • [ ] Create crates/nh-clean/tests/clean_scenarios.rs with tests for safe/unsafe modes
  • [ ] Add crates/nh-nixos/tests/generations.rs with tests for invalid/missing generation IDs
  • [ ] Test error handling when nix store is locked or inaccessible
  • [ ] Add property-based tests using proptest (already a dependency) for generation number edge cases
  • [ ] Document expected behavior in CONTRIBUTING.md for destructive operations testing

Implement CLI documentation generation and add missing command examples to docs/

The repo has clap_mangen dependency configured for generating man pages, and various subcommands across nh-clean, nh-darwin, nh-home, nh-nixos, nh-search crates, but the docs/ directory appears minimal. Generated man pages should be committed and a documentation generation step should be added to the build workflow.

  • [ ] Create Rust code in xtask/ (or Justfile target) to generate man pages using clap_mangen for all crate binaries
  • [ ] Generate man pages to docs/man/ directory for nh, nh-clean, nh-darwin, nh-home, nh-nixos, nh-search
  • [ ] Add generated man pages to .gitignore or commit them based on project preference
  • [ ] Add man page generation step to .github/workflows/build.yaml as post-build artifact
  • [ ] Create docs/COMMANDS.md with examples for each subcommand (clean, search, darwin, home, nixos)

🌿Good first issues

  • Add integration tests for nh clean with time-based retention flags (crates/nh-clean/src/clean.rs) to verify gcroot cleanup behavior on multiple NixOS versions. Tests can use tempfile crate (already a dependency) to mock profile structures.
  • Implement missing completion generators for Nushell (clap_complete_nushell 4.5.10 is already a dependency but not used). Add shell completion generation in xtask and wire it into the build process—similar to existing clap_complete usage for bash/zsh.
  • Document the Elasticsearch integration for nh search in the codebase (likely in crates/nh-search/ which is listed in Cargo.toml but has no files shown). Add inline comments and a brief architecture doc explaining how elasticsearch-dsl-0.4.24 queries are constructed and how nixos-search.yaml CI flow fits in.

Top contributors

Click to expand

📝Recent commits

Click to expand
  • 32b9694 — build: bump dependencies (NotAShelf)
  • 3dffcc6 — flake: bump nixpkgs (NotAShelf)
  • 868814f — flake: filter x86_64-darwin in the checks output (NotAShelf)
  • 0ffe7af — nh-search: update backend version 46 -> 48 (NotAShelf)
  • 9709609 — docs: add changelog entry for the subprocess regression fix (NotAShelf)
  • 22f5a78 — nh-remote: fix indicatif spinner for closure copying (#635) (faukah)
  • bcc09e7 — Fix remaining check failures and improve CI to prevent regressions (#633) (Stebalien)
  • 022883e — Merge pull request #626 from nix-community/notashelf/push-zlyknnslyqoz (NotAShelf)
  • b2043a7 — Merge branch 'master' into notashelf/push-zlyknnslyqoz (NotAShelf)
  • 676e70e — Merge pull request #632 from LarsZauberer/docs-gcroots (faukah)

🔒Security observations

The codebase shows generally good security practices with use of modern security libraries (secrecy, rustls, color-eyre). However, there are concerns about the invalid Rust edition specification, truncated dependency information preventing full audit, and potential command injection risks in subprocess execution. The project should validate the edition setting, complete the dependency audit, and ensure proper input sanitization for all external command execution. The use of internal path dependencies and workspace structure is appropriate for modularity and maintenance.

  • Medium · Outdated Rust Edition Declaration — Cargo.toml (workspace.package section). The Cargo.toml workspace specifies edition = "2024", which is not a valid Rust edition. Valid editions are 2015, 2018, and 2021. This may cause build failures or unexpected behavior. Fix: Update to a valid Rust edition. Use edition = "2021" for modern Rust projects.
  • Medium · Incomplete Dependency Documentation — Cargo.toml (workspace.dependencies section). The Cargo.toml dependencies list appears truncated (tracing-subscriber features are cut off). This makes it impossible to fully audit dependency configurations for security issues. Fix: Provide complete dependency specifications and run cargo audit to check for known vulnerabilities in all dependencies.
  • Low · HTTP Used in Reqwest Configuration — Cargo.toml (reqwest dependency). The reqwest dependency uses 'rustls' for TLS, which is good, but the default features are disabled. Ensure that only HTTPS connections are enforced in the codebase when making external requests. Fix: Review all HTTP client usage in the codebase to ensure HTTPS is enforced. Consider adding feature flags or runtime checks to prevent insecure connections.
  • Low · secrecy Crate Dependency — Cargo.toml (secrecy dependency). The project uses the 'secrecy' crate with serde features enabled. Ensure that sensitive data (passwords, tokens, keys) are properly handled and not logged or serialized unintentionally. Fix: Review all code using secrecy to ensure sensitive values are not exposed through logging, debugging output, or serialization. Use SecretString/SecretVec appropriately.
  • Low · Subprocess Invocation Without Input Validation Visible — Cargo.toml (subprocess dependency) and crates/nh-core/src/command.rs, crates/nh-remote/src/remote.rs. The 'subprocess' crate is used, which suggests the project executes external commands. Without seeing the implementation, there's a risk of command injection if user input is not properly sanitized. Fix: Ensure all subprocess calls properly escape and validate inputs. Use parameterized command execution rather than shell string concatenation. Review crates/nh-core/src/command.rs for proper argument handling.
  • Low · External API Calls Without Apparent Rate Limiting — Cargo.toml (elasticsearch-dsl, reqwest) and crates/nh-search/src/search.rs. Dependencies like 'elasticsearch-dsl' and 'reqwest' suggest external API calls. The codebase structure doesn't show obvious rate limiting or timeout mechanisms. Fix: Implement rate limiting, timeouts, and retry logic for external API calls. Set reasonable connection and request timeouts on reqwest clients.

LLM-derived; treat as a starting point, not a security audit.


Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.

Mixed signals · nix-community/nh — RepoPilot