Why Nix? Eliminates "works on my machine" by pinning every dependency - from Go version to system libraries - in a single file. Build today, rebuild in 5 years, get identical binaries.
When to use:
When not to use:
# Enter development shell (installs all dependencies)
nix develop
# Pre-commit hooks are automatically installed in Nix shell
# They include: nixpkgs-fmt for Nix formatting
# Build QNTX binary
nix build
# Build CI container image (defaults to your architecture)
nix build .#ci-image
# Build specific architecture
nix build .#ci-image-amd64
nix build .#ci-image-arm64
# Run checks (flake validation, build verification)
nix flake check
For local development outside the Nix shell, install local Git hooks:
git config core.hooksPath .githooks
This enables:
gofmtSee .githooks/README.md for details.
Why reproducible builds? Proves the binary you download matches what CI built. Rebuild the same commit later → identical SHA256 hash. No hidden changes.
How it works:
.github/workflows/nix-image.ymlWhy Cachix? First build takes ~30 min (compiles everything). Cachix caches binaries. Next build: ~5 min (just downloads from cache).
Caching strategy:
flake.lock hash (nixpkgs version)flake.lock → rebuild everything (new cache key)flake.lock → instant downloads from CachixWhy vendorHash? Nix downloads your Go modules during build. Hash proves you got what you expected (security). Wrong hash = build fails.
How to update:
# After changing go.mod/go.sum, run this:
./.githooks/update-nix-hash.sh
# Or manually: let it fail, copy new hash from error
nix build .#qntx # Fails with "got: sha256-ABC..."
# Copy "got" hash to vendorHash in flake.nix
# Verify
nix build .#qntx
# Commit together
git add flake.nix go.mod go.sum
Why upgrade? Get newer Go/Rust versions, security patches, bug fixes in build tools.
When to upgrade? Monthly, or when you need a specific package version.
nix flake update nixpkgs # Updates flake.lock
nix build .#qntx # Test build still works
nix flake check # Verify all packages build
git add flake.lock
git commit -m "Update nixpkgs"
Warning: This invalidates Cachix cache (new packages = rebuild everything once).
When needed: CI tests require a new CLI tool (e.g., jq for JSON processing).
How to add:
flake.nix → find mkCiImage function → add to contents = [...]config.Env PATH listnix build .#ci-image && docker load < resultExample: Adding jq:
contents = [
qntx
pkgs.jq # Add this
# ...
];
config.Env = [
"PATH=${pkgs.lib.makeBinPath [ qntx pkgs.jq ... ]}" # Add to PATH
];
Pros:
Cons:
When to use:
Pros:
Cons:
When to use:
Setup:
git config core.hooksPath .githooks
Why: Nix hashes your Go modules to detect tampering. You changed go.mod but didn't update the hash → security check fails.
Fix: ./.githooks/update-nix-hash.sh or copy "got:" hash from error to flake.nix.
Why: Nix sandbox blocks network to force reproducibility. Can't download during build → must declare all deps upfront.
Fix: Update vendorHash (Go deps) or add to contents = [...] (system deps).
Why: You're on different nixpkgs version. CI uses flake.lock, you might have uncommitted flake.lock.
Fix: git add flake.lock and commit it. Or nix flake update to match CI.
Daily development:
make cli) for speedgit config core.hooksPath .githooks) for instant formattingBefore pushing:
nix flake check to catch Nix-specific breakagego.mod: run ./.githooks/update-nix-hash.shflake.lock when modified (ensures CI matches your build)Monthly maintenance:
nix flake update to get latest packages (security patches, Go version bumps)After tagging:
.githooks/README.md - Local Git hooks setupflake.nix - Nix configuration source of truth.github/workflows/nix-image.yml - CI pipeline definition