- Root:
flake.nix,flake.lock,devshells/{default.nix,ci.nix},formatter.nix,README.md. - Packages live under
packages/<tool>/withpackage.nix,default.nix, optionalupdate.py, and lockfiles when needed. - Formatting config:
formatter.nix. - Utilities and docs:
scripts/,docs/,.github/.
- Enter dev shell:
nix develop. - Build a package:
nix build --accept-flake-config .#<package>(e.g.,nix build .#geth). - Run without installing:
nix run .#<package> -- --help. - Repo checks (builds + lints):
nix flake check. - Format everything:
nix fmt. - Regenerate README package section:
./scripts/generate-package-docs.py.
- Indentation: 2 spaces; avoid tabs.
- Nix: small, composable derivations; prefer
buildNpmPackage/rustPlatform.buildRustPackage/stdenv.mkDerivationas in existing packages. - File layout per package:
package.nix(definition),default.nix(wrapper),update.py(optional custom updater),nix-update-args(optional nix-update flags). - Tools via treefmt: nixfmt, deadnix, shfmt, shellcheck, mdformat, yamlfmt, taplo. Always run
nix fmtbefore committing.
Prefer nix-update over custom update scripts. Most packages can be updated with:
nix run nixpkgs#nix-update -- --flake <package>For this to work, package.nix must have version/hash attributes inline (not loaded from JSON):
buildGoModule rec {
pname = "example";
version = "1.0.0"; # nix-update finds and updates this
src = fetchFromGitHub {
owner = "owner";
repo = "repo";
rev = "v${version}";
hash = "sha256-..."; # nix-update updates this
};
subPackages = [ "." ]; # for go find the relevant packages containing the binary
vendorHash = "sha256-..."; # nix-update updates this too
}Testing updates: After writing or modifying a package, verify updates work by:
- Temporarily downgrading the version in
package.nix - Running
nix run nixpkgs#nix-update -- --flake <package> - Confirming version and hashes are updated correctly
Only use custom update.py scripts when nix-update cannot handle the package, such as:
- Packages with complex version schemes nix-update cannot parse
- Sources not supported by nix-update (non-GitHub, custom APIs)
- Packages requiring special hash calculation logic
Custom updaters should use the scripts/updater/ library. See existing update.py files for examples.
Every package MUST have proper metadata in package.nix:
meta = with lib; {
description = "Clear, concise description";
homepage = "https://project-homepage.com";
license = licenses.mit; # or licenses.unfree, etc.
sourceProvenance = with lib.sourceTypes; [ fromSource ];
maintainers = with maintainers; [ username ];
mainProgram = "binary-name";
platforms = platforms.all; # or specific platforms
};Every package should have a category in passthru for README organization:
passthru.category = "Execution Clients";
meta = { ... };Available categories (in display order):
- Execution Clients - Ethereum execution layer clients (geth, erigon, besu, reth, nethermind)
- Consensus Clients - Ethereum consensus layer clients (prysm, lighthouse, teku)
- Validators - Validator clients and key management (vouch, charon, web3signer, dirk)
- Staking Tools - Staking infrastructure and utilities (rocketpool, rocketpoold, eigenlayer, ethdo, ethstaker-deposit-cli)
- MEV - MEV infrastructure (mev-boost, mev-boost-relay, blutgang)
- SSV - Secret Shared Validators tooling (ssvnode, ssv-dkg)
- Account Abstraction - ERC-4337 bundlers and account abstraction tooling (alto)
- Polygon - Polygon blockchain clients and tools (bor, heimdall-v2)
- Development Tools - Smart contract development and testing (foundry, slither, heimdall, sedge, tx-fuzz, snarkjs)
- Libraries - Cryptographic and protocol libraries (blst, ckzg, mcl, evmc, bls)
- Utilities - Other Ethereum tools (eth-validator-watcher, kurtosis, rotki-bin, zcli, ethereal)
For maintainers not yet in nixpkgs, define them in lib/default.nix:
{ inputs, ... }:
inputs.nixpkgs.lib.extend (
_final: prev: {
maintainers = prev.maintainers // {
username = {
github = "github-username";
githubId = 123456; # Get from: curl -s https://api.github.com/users/username | jq -r '.id'
name = "Full Name";
};
};
}
)Then in packages/<package>/default.nix, pass flake to the package:
{ pkgs, flake }: pkgs.callPackage ./package.nix { inherit flake; }And in packages/<package>/package.nix, reference custom maintainers:
{
lib,
flake,
# ... other args
}:
stdenv.mkDerivation rec {
# ...
meta = with lib; {
maintainers = with flake.lib.maintainers; [ username ];
# ... other meta
};
}Use versionCheckHook to verify packages report correct versions during build:
doInstallCheck = true;
nativeInstallCheckInputs = [ versionCheckHook ];For tools that need a writable HOME directory (many CLI tools try to create config/cache directories), use versionCheckHomeHook:
-
In
packages/<package>/default.nix, pass the hook:{ pkgs, perSystem, ... }: pkgs.callPackage ./package.nix { inherit (perSystem.self) versionCheckHomeHook; }
-
In
packages/<package>/package.nix, add it to inputs and use it:{ versionCheckHook, versionCheckHomeHook, # ... }: stdenv.mkDerivation { # ... doInstallCheck = true; nativeInstallCheckInputs = [ versionCheckHook versionCheckHomeHook ]; }
- Build locally:
nix build .#<package>. - Run flake checks:
nix flake check. - Per-package checks (when defined):
nix build .#checks.$(nix eval --raw --impure --expr builtins.currentSystem).pkgs-<package>. - For scripts, ensure
shellcheckpasses; enabledoCheck = truein packages when feasible.
- Commit style mirrors history:
<package>: summary.- Version bumps:
<package>: X -> Y (#123); new packages:<package>: init at X.Y.Z.
- Version bumps:
- PRs: clear description, rationale, and testing notes; link issues; include sample run output for CLIs.
- Before pushing: run
nix fmtandnix flake check.
- Some tools are unfree; enable unfree if needed in your Nix config.
- Sandbox experiments: consider using confined execution wrappers for sensitive operations.
- Pin sources with hashes; avoid network access at build time.
When working on package requests or fixes, you MUST install Nix from the official installer to properly test changes, unless already present
# Install Nix with daemon mode
sh <(curl -L https://nixos.org/nix/install) --daemon
# Enable flakes and nix-command (required for this repository)
echo "experimental-features = nix-command flakes" | sudo tee -a /etc/nix/nix.conf
# Restart the Nix daemon to apply changes
if [[ "$OSTYPE" == "darwin"* ]]; then
sudo launchctl kickstart -k system/org.nixos.nix-daemon
else
sudo systemctl restart nix-daemon
fi-
Rust packages with git dependencies: May fail during cargo vendoring if dependencies have workspace inheritance issues. Consider using pre-built binaries as a workaround.
-
Binary packages: When packaging pre-built binaries:
- Use
dontUnpack = trueif the download is a single executable file - Use
autoPatchelfHookon Linux to handle dynamic library dependencies - Common missing libraries:
gcc-unwrapped.libfor libgcc_s.so.1
- Use
-
Update scripts: Follow shellcheck recommendations - declare and assign variables separately to avoid masking return values.
-
Custom nix-update arguments: For packages that need special nix-update flags (e.g., filtering out nightly releases), create a
nix-update-argsfile with one argument per line:# packages/qwen-code/nix-update-args --use-github-releases --version-regex ^v([0-9]+\.[0-9]+\.[0-9]+)$The CI workflow reads this file and passes the arguments to nix-update automatically.