Enforce Nix + Pixi + direnv + Home-manager configuration patterns
Hard Guardrails for configuration management in this repository. Reference:
docs/adr/adr-003-version-management.md
┌──────────────────────────────────────────────────────────────────┐
│ USER ENVIRONMENT │
├──────────────────────────────────────────────────────────────────┤
│ direnv (.envrc) │
│ └── `use flake` → Automatic environment activation │
├──────────────────────────────────────────────────────────────────┤
│ Nix Flake (flake.nix) Pixi (pixi.toml) │
│ ├── System packages (git, helix) ├── ROS2 Humble │
│ ├── Development tools (cargo, gcc) ├── Python packages │
│ ├── LSP servers (pyright, nil) ├── Node.js │
│ └── Home-manager modules └── Conda ecosystem │
├──────────────────────────────────────────────────────────────────┤
│ Home-manager Modules (modules/) │
│ ├── common/ Cross-platform (shells, editor, direnv) │
│ ├── linux/ Linux-specific (systemd, docker, udev) │
│ └── macos/ macOS-specific (homebrew, system.nix) │
├──────────────────────────────────────────────────────────────────┤
│ DevPod (.devcontainer/devcontainer.json) │
│ └── Remote development with Nix + volume caching │
└──────────────────────────────────────────────────────────────────┘
| Tool | File | Purpose |
|---|---|---|
| Nix flakes | flake.nix | System packages, dev shells |
| Pixi | pixi.toml | ROS2, Python, conda packages |
| direnv | .envrc | Auto-activate use flake |
| Home-manager | modules/ | User configuration |
| Lock files | *.lock | Reproducibility |
| Tool | File | Violation |
|---|---|---|
| mise/rtx | .mise.toml, .tool-versions | Redundant with Nix + Pixi |
| asdf | .asdfrc | Superseded by mise, which is superseded by Nix |
| 0install | *.0install.xml | Conflicts with Nix store |
| pyenv | .python-version | Use pixi.toml Python version |
| nvm | .nvmrc | Use pixi.toml or flake.nix Node.js |
| Symlinks | * (type=symlink) | Causes Git/Windows/tooling issues |
Nix and Home-manager use symlinks by design:
~/.config/* → /nix/store/...-home-manager-files/...~/.nix-profile → /nix/var/nix/profiles/...If symlinks are unacceptable:
| Category | Primary Tool | File Location |
|---|---|---|
| System CLI tools | Nix | flake.nix |
| Development shells | Nix devshell | flake.nix |
| ROS2 packages | Pixi + RoboStack | pixi.toml |
| Python packages | Pixi + conda-forge | pixi.toml |
| Node.js runtime | Pixi or Nix | pixi.toml or flake.nix |
| Rust toolchain | Nix | flake.nix |
| Shell configuration | Home-manager | modules/common/shell/ |
| Editor configuration | Home-manager | modules/common/editor/ |
| Docker images | DevContainer | .devcontainer/ |
# Validate configuration policies
conftest test . --policy .claude/policies/configuration.rego
# Check for forbidden files
ls .tool-versions .mise.toml .asdfrc 2>/dev/null && echo "VIOLATION!" || echo "OK"
# Verify direnv integration
grep -q "use flake" .envrc && echo "OK" || echo "VIOLATION: .envrc missing 'use flake'"
# Verify lock files exist
[[ -f flake.lock && -f pixi.lock ]] && echo "OK" || echo "VIOLATION: Missing lock files"
# Verify Home-manager module structure
find modules/ -name "*.nix" -exec grep -L "lib\|mkDefault\|mkOption" {} \;
Consider When:
Do NOT Use For:
flake.nix)Consider When:
Do NOT Use For:
Need to manage configuration?
├── Project-level packages → Nix (flake.nix) or Pixi (pixi.toml)
├── Project-level shells → Nix devshell
├── User-level dotfiles?
│ ├── Nix system available → Home-manager
│ └── Portable/non-Nix → chezmoi
├── Runtime version switching?
│ ├── Reproducibility critical → Nix + pixi environments
│ └── Quick switching needed → mise (fallback only)
└── Remote development → DevPod with .devcontainer/
# modules/common/example.nix
{ config, lib, pkgs, ... }:
let
inherit (lib) mkDefault mkOption types;
in
{
options.example = {
enable = mkOption {
type = types.bool;
default = true;
description = "Enable example configuration";
};
};
config = lib.mkIf config.example.enable {
# Configuration here
};
}
# In modules/common/direnv.nix → programs.direnv.stdlib
# Enhanced use_flake with pixi integration
use_flake_pixi() {
use flake
if [[ -f pixi.toml ]]; then
eval "$(pixi shell-hook)"
fi
}
# ROS2 workspace layout
layout_ros2() {
export ROS_DOMAIN_ID=${ROS_DOMAIN_ID:-0}
if [[ -f install/local_setup.bash ]]; then
source install/local_setup.bash
fi
}
{
"name": "project-name",
"features": {
"ghcr.io/devcontainers/features/nix:1": {
"extraNixConfig": "experimental-features = nix-command flakes"
}
},
"mounts": [
"source=nix-store,target=/nix,type=volume"
],
"postCreateCommand": "direnv allow && nix develop"
}
# .pre-commit-config.yaml