- Archive 30 completed session documents to docs/archive/ - Extract learnings to docs/learnings/ (nix-flakes, nostr-sdk, grasp-audit) - Create CURRENT_STATUS.md as single source of truth - Create AGENTS.md with documentation guidelines - Create docs/archive/README.md for archive organization - Clean root directory: 32 files → 4 files Root directory now contains only: - README.md (project overview) - AGENTS.md (documentation guidelines) - CURRENT_STATUS.md (current state) - CLEANUP_SUMMARY.md (cleanup report) All historical documents preserved in docs/archive/ with proper dating. All reusable knowledge extracted to docs/learnings/. Benefits: - Easy to find current information - Clear document lifecycle - No more documentation sprawl - Learnings are accessible and reusable - Better onboarding for new developers/agents File counts: - Root: 4 (was 32) - Permanent docs: 7 - Learnings: 3 (new) - Archive: 32 (new) - Total: 49 well-organized docs
8.2 KiB
Nix Flakes - Learnings and Gotchas
Purpose: Document Nix flake patterns, gotchas, and best practices learned during ngit-grasp development
Last Updated: November 4, 2025
Critical Gotchas
Always Use nix develop, Not nix-shell
Problem: We use flake.nix, not shell.nix. Using nix-shell will fail or use the wrong environment.
# ✅ Correct - for flake.nix
cd grasp-audit
nix develop
nix develop -c cargo build
# ❌ Wrong - for shell.nix (we don't use this)
nix-shell
nix-shell --run "cargo build"
Why:
nix-shelllooks forshell.nixordefault.nixnix developlooks forflake.nix- We migrated from
shell.nixtoflake.nixon November 4, 2025
Related: See docs/archive/2025-11-04-flake-migration.md
Flake Structure
Our Standard Flake Pattern
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
rust-overlay.url = "github:oxalica/rust-overlay";
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { nixpkgs, rust-overlay, flake-utils, ... }:
flake-utils.lib.eachDefaultSystem (system:
let
overlays = [ (import rust-overlay) ];
pkgs = import nixpkgs { inherit system overlays; };
manifest = pkgs.lib.importTOML ./Cargo.toml;
in with pkgs; {
# Development shell
devShells.default = mkShell {
nativeBuildInputs = [
rust-bin.stable.latest.default
pkg-config
gitlint
];
buildInputs = [
openssl
];
shellHook = ''
echo "🦀 Development environment loaded"
export RUST_SRC_PATH=${pkgs.rustPlatform.rustLibSrc}
'';
};
# Package output
packages.default = pkgs.rustPlatform.buildRustPackage {
pname = manifest.package.name;
version = manifest.package.version;
src = ./.;
cargoLock = { lockFile = ./Cargo.lock; };
buildInputs = [ openssl ];
nativeBuildInputs = [ pkg-config ];
doCheck = false; # Run tests separately
};
});
}
Key Components
- rust-overlay: Provides latest stable Rust toolchain
- flake-utils: Cross-platform support helper
- manifest: Auto-read version from Cargo.toml
- devShells.default: Development environment
- packages.default: Buildable package
Common Flake Commands
Essential Commands
# Enter development shell
nix develop
# Run command in dev shell (one-off)
nix develop -c cargo build
# Show flake outputs
nix flake show
# Check flake validity
nix flake check
# Update flake inputs (like updating dependencies)
nix flake update
# Build the package directly
nix build
# Run without installing
nix run
# Show flake metadata
nix flake metadata
Debugging Commands
# Show detailed evaluation trace
nix develop --show-trace
# Print flake evaluation
nix eval .#devShells.x86_64-linux.default
# Check what's in the store
nix path-info .#packages.x86_64-linux.default
Subproject Flakes
grasp-audit Has Its Own Flake
Important: grasp-audit/ is a subproject with its own flake.nix and Cargo.toml.
# ✅ Correct - enter grasp-audit environment
cd grasp-audit
nix develop
cargo build
# ❌ Wrong - can't build from root
cd ngit-grasp
cargo build # This won't find grasp-audit dependencies
Why:
- Each Rust workspace needs its own Nix environment
- Dependencies are project-specific
- Flake inputs are locked per-project
Migration from shell.nix to flake.nix
What Changed
Before (shell.nix):
{ pkgs ? import <nixpkgs> {} }:
pkgs.mkShell {
buildInputs = with pkgs; [
rustc
cargo
openssl
pkg-config
];
}
After (flake.nix):
- Locked inputs (reproducible)
- Multi-output (dev shell + package)
- Cross-platform by default
- Better tooling integration
Migration Steps
- Create
flake.nixwith standard structure - Run
nix flake checkto validate - Update all documentation:
nix-shell→nix develop - Test that build works:
nix develop -c cargo build - Remove
shell.nix - Commit changes
Reference: See docs/archive/2025-11-04-flake-migration.md
Benefits of Flakes
Reproducibility
Locked inputs ensure everyone gets the same environment:
# flake.lock contains exact commits
$ cat flake.lock
{
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1698611440,
"narHash": "sha256-jPjHjrerhYDy3q9+s5EAsuhyhuknNfowY6yt6pjn9pc=",
"rev": "23e89e0c8c5e2d9cf5b5e7c3e8e8e8e8e8e8e8e8"
}
}
}
}
Everyone running nix develop gets exactly this version of nixpkgs.
Multi-Output
Single flake provides:
- devShells.default: Development environment
- packages.default: Buildable package
- apps.default: Runnable application (optional)
Composability
Flakes can use other flakes as inputs:
{
inputs = {
grasp-audit.url = "path:./grasp-audit";
};
}
Common Issues
Issue: "error: getting status of '/nix/store/...': No such file or directory"
Cause: Flake inputs need to be updated or fetched
Solution:
nix flake update
nix develop
Issue: "error: experimental feature 'nix-command' is not enabled"
Cause: Nix flakes are experimental and need to be enabled
Solution:
Add to ~/.config/nix/nix.conf:
experimental-features = nix-command flakes
Issue: Changes to flake.nix not taking effect
Cause: Flake evaluation is cached
Solution:
# Clear evaluation cache
nix flake update
# Or force re-evaluation
nix develop --refresh
Issue: "error: cannot find flake 'flake:self' in the flake registries"
Cause: Not in a git repository or flake.nix not committed
Solution:
git add flake.nix flake.lock
git commit -m "Add flake"
Note: Flakes require git. Uncommitted files are ignored by default.
Best Practices
1. Always Commit flake.lock
git add flake.lock
git commit -m "Update flake inputs"
Why: Ensures reproducibility across machines and CI/CD
2. Use Specific Rust Versions When Needed
# Latest stable (default)
rust-bin.stable.latest.default
# Specific version
rust-bin.stable."1.75.0".default
# Nightly
rust-bin.nightly."2024-01-01".default
3. Include Helpful Shell Hooks
shellHook = ''
echo "🦀 GRASP Audit development environment"
echo ""
echo "Common commands:"
echo " cargo build - Build project"
echo " cargo test - Run tests"
echo " cargo run - Run binary"
echo ""
export RUST_SRC_PATH=${pkgs.rustPlatform.rustLibSrc}
'';
4. Separate Build and Runtime Dependencies
# Build-time only
nativeBuildInputs = [
pkg-config
rustc
cargo
];
# Runtime needed
buildInputs = [
openssl
];
5. Disable Tests in Package Build
packages.default = pkgs.rustPlatform.buildRustPackage {
# ...
doCheck = false; # Run tests separately with cargo test
};
Why: Faster builds, tests run via cargo test in dev shell
Workflow Examples
Daily Development
# Start work
cd grasp-audit
nix develop
# Inside nix shell
cargo build
cargo test
cargo run -- --help
# Exit shell
exit
CI/CD
# One-off commands (no interactive shell)
nix develop -c cargo build
nix develop -c cargo test --lib
nix develop -c cargo test -- --ignored
Building Release
# Build package directly
nix build
# Result is in ./result/bin/
./result/bin/grasp-audit --version
References
- Nix Flakes Manual: https://nixos.org/manual/nix/stable/command-ref/new-cli/nix3-flake.html
- rust-overlay: https://github.com/oxalica/rust-overlay
- flake-utils: https://github.com/numtide/flake-utils
- Our Migration:
docs/archive/2025-11-04-flake-migration.md
Quick Reference
| Task | Command |
|---|---|
| Enter dev shell | nix develop |
| Run one command | nix develop -c <command> |
| Show outputs | nix flake show |
| Validate flake | nix flake check |
| Update inputs | nix flake update |
| Build package | nix build |
| Run package | nix run |
Last updated: November 4, 2025
Status: Living document - update as we learn more