Personal Nix home-manager configuration for macOS (Apple Silicon) that manages user environment, packages, and dotfiles using Nix flakes.
- 🏠 Declarative Environment: Complete user environment managed through Nix
- 📦 Package Management: Reproducible package installation across machines
- 🔧 Development Tools: Comprehensive development environment setup
- 🚀 Flake-based: Modern Nix flakes for better reproducibility
- 🍎 macOS Optimized: Specifically configured for Apple Silicon Macs
- 💾 Binary Caches: Fast package installation with pre-built binaries
- ⚙️ Global Direnv: Universal direnv integration across all terminals and editors
-
Install Nix (if not already installed):
curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- install
-
Install Home Manager:
nix run home-manager/master -- init --switch
-
Clone this repository:
git clone https://github.com/sadjow/home-manager ~/.config/home-manager cd ~/.config/home-manager
-
Apply the configuration:
home-manager switch --flake .
├── flake.nix # Flake definition with inputs and outputs
├── flake.lock # Locked dependency versions
├── darwin-configuration.nix # nix-darwin system config (hostname, etc.)
├── home.nix # Main home-manager configuration
├── home/
│ ├── AGENTS.md # Shared global agent instructions
│ ├── claude/ # Authored Claude Code configuration
│ ├── nix/
│ │ ├── caches.nix # Binary cache URLs and public keys
│ │ └── default.nix # User Nix settings
│ ├── agent-skills.nix # Pinned public skills and local private links
│ ├── claude-code.nix # Claude Code integration
│ ├── codex/ # Codex profiles shared with T3 Code
│ ├── codex.nix # Codex CLI, defaults, and isolated profiles
│ ├── t3-code.nix # T3 Code provider instances
│ └── shell.nix # Shell configuration (zsh/bash with global direnv)
├── docs/
│ ├── AGENT_HARNESS_SKILL_LINEAGE.md # Personal and project skill feedback loop
│ └── CHANNEL_STRATEGY.md # Nixpkgs channel strategy and alternatives
├── overlays/
│ └── README.md # Information about custom overlays
├── AGENTS.md # Shared repository guidance for coding agents
└── README.md # This file
- Languages: Ruby, Dart, Node.js (via asdf)
- Editors: Neovim
- Version Control: Git, GitHub CLI
- Build Tools: CocoaPods, FFmpeg
- Shell: tmux, direnv, ripgrep, bat
- Security: AWS Vault, GPG
- Network: nmap
- AI Tools: Claude Code (via external flake)
- Package Management: Cachix, asdf-vm
- devenv: Fast, declarative development environments
- claude-code: AI coding assistant with dedicated Node.js runtime
home/codex.nix manages the personal Codex defaults in ~/.codex/config.toml.
Each activation sets Full access: sandbox_mode = "danger-full-access" and
approval_policy = "never". This removes command sandbox restrictions and disables
approval prompts governed by that policy. Computer Use app permissions and Locked
Use remain separate controls.
The activation preserves other settings and TOML comments, keeps the file writable
with mode 600, and saves the previous content to config.toml.home-manager-backup
when it changes. An incompatible default_permissions selector is removed.
Existing task overrides and the isolated codex-spireworks profile are unchanged.
See OpenAI's default permissions documentation.
home/t3-code.nix declares the provider instances T3 Code shows in its Settings
UI: the claudeAgent and codex instances, the isolated claude_spireworks,
claude_stackbuilders, and codex_spireworks instances built from
home/claude/profiles.nix and home/codex/profiles.nix, and one Cursor instance
per organization, cursor_spireworks and cursor_durst. Each activation merges them into ~/.t3/userdata/settings.json with the same
three-way reconciliation as the Claude settings: the file stays writable with
mode 600, provider fields chosen in the UI survive while the declared fields
are unchanged, and T3 Code reloads the file as soon as activation rewrites it.
The baseline lives in ~/.t3/userdata/.home-manager-settings/, and the previous
content is saved beside the runtime file with the suffix .home-manager-backup.
Provider authentication stays with each CLI's own home directory, so nothing in
the declared settings is secret. To add another isolated Claude Code or Codex
account, add its profile to the matching profiles file and describe its instance
in home/t3-code.nix; the instance runs the profile's wrapper command and points
homePath at the same config directory.
The Cursor agent keeps a single browser login per machine in the macOS keychain,
which cursor_spireworks uses. Every other organization authenticates with an
API key from its Cursor dashboard, supplied as a sensitive CURSOR_API_KEY
variable in the Environment section of its instance in T3 Code; the value goes to
T3 Code's secret store, never to this repository. cursor_durst is declared
disabled so it cannot fall back to the keychain login, and enabling it in the UI
after adding the key survives later activations because the declared field is
unchanged.
Home Manager installs codex-daemon-reconcile. Running it without arguments checks
saved daemon feature overrides against the installed CLI without changing state.
It exits with status 1 when overrides conflict or the check fails.
codex-daemon-reconcile
codex-daemon-reconcile --restartUse --restart when ready to interrupt active or queued work on the shared daemon.
The helper stops the daemon and starts it fresh through Codex's lifecycle commands,
which clear saved session feature overrides. Other daemon settings are left to
Codex to preserve. Desired persistent feature preferences belong in config.toml.
CODEX_HOME selects the target profile; the default is ~/.codex.
Home Manager activation does not restart the daemon.
Codex 0.161.0 can retain a stale api_key_model_discovery override after a CLI
upgrade. app-server daemon restart --enable api_key_model_discovery was observed
to report success while retaining the disabled override. The interactive
“Restart with these settings” action also repairs it. A fresh stop/start avoids
editing Codex's internal settings file directly.
Upstream report: openai/codex#51787.
After reconciliation, launch codex and confirm the compatibility prompt is gone.
Matching versions, features list, and a successful restart are insufficient:
CLI defaults, persisted overrides, and the live server handshake are separate
evidence. The helper checks saved overrides and daemon availability; it does not
claim to verify the live feature handshake. If a lifecycle command fails or is
interrupted, check codex app-server daemon version before retrying.
Public skills are maintained in sadjow/skills.
home/agent-skills.nix installs the revision pinned by the skills flake input
into these discovery directories:
| Consumer | Directory |
|---|---|
| Shared agent discovery | ~/.agents/skills |
| Codex | ~/.codex/skills |
| Claude Code | ~/.claude/skills and each isolated profile's skills |
| Cursor | ~/.cursor/skills |
Each public skill has a symbolic link in every directory, resolving to the same pinned source in the Nix store. The destinations do not contain independent copies. Edit the public skills repository; installed Nix store files are read-only. Local checkout edits become installed public skills only after publishing them, updating the pin, and activating the resulting Home Manager generation.
To adopt a newer published revision:
nix flake update skills --flake 'path:.'
nix flake check 'path:.'
home-manager build --flake 'path:.#sadjow'A path: build includes uncommitted changes. When the worktree has any, compare
the first two paths below before updating the pin: a match means that work is
already active, while a difference means the switch would also activate it. After
the build, the closure difference should list only the updated skills source.
nix eval --raw 'path:.#homeConfigurations.sadjow.activationPackage.outPath'
readlink -f ~/.local/state/nix/profiles/home-manager
nix store diff-closures "$(readlink -f ~/.local/state/nix/profiles/home-manager)" ./resultActivate separately with home-manager switch --flake 'path:.#sadjow' when ready.
Home Manager does not run npm or fetch a moving skills branch during activation.
Unmodified upstream skills keep their existing installer-managed source; their
installation commands are in the public repository's upstream catalog.
Optional private skills stay in a separate authenticated checkout at
~/.config/skills-private. Clone that repository using your configured Git
credentials and restrict its directory permissions to 700. During activation,
Home Manager discovers skills/*/SKILL.md in that checkout and creates local links
for the same discovery directories. Each link resolves to the same private
checkout, so edits there are available through every link without republishing.
New or renamed private skills need another activation to reconcile their links.
No private contents or skill-name inventory are read
at Nix evaluation time or copied into the Nix store. A missing checkout is allowed;
an unrelated existing destination is preserved and reported as a conflict.
A successful build does not activate its links. After switching, verify the installed skill files rather than relying only on the checkout or build result:
bash scripts/check-agent-skills
# Print dangling links in the two discovery directories used by this setup for Codex.
find "$HOME/.agents/skills" "$HOME/.codex/skills" -type l ! -exec test -e {} \; -print
# Replace browser-harness with the skill being investigated.
for directory in .agents/skills .codex/skills .claude/skills .claude-spireworks/skills .claude-stackbuilders/skills .cursor/skills; do
test -f "$HOME/$directory/browser-harness/SKILL.md" || printf 'Missing: %s\n' "$directory/browser-harness"
doneThe integration check exercises the configuration and private-link reconciler in a temporary home; it does not audit the active installation. The filesystem checks above verify installed paths. Confirm discovery in the client as well, starting a new task or restarting it if its catalog has not refreshed.
Moving or renaming a skill source can leave an older generation's links dangling. Build and activate the current configuration after migrating sources. If activation fails because a real directory occupies a managed skill path, inspect that exact path and move it to a backup outside the discovery directories before retrying. Keep the backup until the managed replacement has been verified.
For an obsolete dangling link, inspect its target with readlink and confirm that
it belongs to a retired or renamed skill before moving it to the backup. Do not
bulk-delete unrelated links or directories. A failed switch can already have
applied some changes; rerun the switch after resolving the conflict and verify
that it completes successfully.
Cache URLs and public signing keys are defined once in home/nix/caches.nix. Home Manager uses this catalog to generate the user Nix configuration. It takes effect after activation, including for subsequent builds of this configuration.
After home-manager switch, Nix automatically checks these caches for matching packages across projects. Public caches do not require a Cachix token or a separate cachix use command. New caches must be registered in the catalog; Nix does not discover a cache from a package name.
Flake-provided configuration is also accepted automatically through the existing nix.settings.accept-flake-config = true setting. A cached build must match the exact package derivation and platform; changing inputs can require a local build.
The former Garnix cache is excluded because its hosted service shut down on July 15, 2026.
Verification:
# Show only the effective cache URLs and public signing keys
nix config show --json | jq '{substituters: .substituters.value, "trusted-public-keys": ."trusted-public-keys".value}'This configuration provides comprehensive direnv integration that works seamlessly across all terminals and editors:
- Universal Support: Works in VSCode, Cursor, and any terminal application
- Multi-shell: Supports both zsh and bash shells
- Login Shell Compatibility: Properly configured for login shells spawned by editors
- Auto-loading: Direnv hooks are loaded automatically without manual setup
The global direnv integration is implemented through:
- Profile-level hooks: Added to
.zprofileand.bash_profilefor login shells - Interactive shell integration: Built-in home-manager direnv support
- Global PATH configuration: Ensures direnv is available system-wide
- Redundant loading protection: Prevents duplicate initialization
# Test direnv in different shell contexts
zsh -l -c 'command -v _direnv_hook && echo "✓ direnv loaded in zsh"'
bash -l -c 'command -v _direnv_hook && echo "✓ direnv loaded in bash"'
direnv --version # Should show installed versionThis means when you open a project with a .envrc file in any editor, the environment will be automatically activated in all terminal sessions without additional configuration.
# Apply home-manager configuration
home-manager switch --flake .
# Apply nix-darwin system configuration
sudo darwin-rebuild switch --flake .#codecraft
# Build configuration without switching
home-manager build --flake .
# Preview changes (dry run)
home-manager switch --flake . -n
# Update flake dependencies
nix flake update
# Check configuration validity
nix flake check# Show flake outputs
nix flake show
# Update specific input
nix flake lock --update-input <input-name>
# Run garbage collection
nix-collect-garbage -dEdit home.nix and add packages to the home.packages list:
home.packages = [
pkgs.your-package
# ... other packages
];The configuration is modular. Key files to modify:
home.nix: Main package list and basic settingshome/AGENTS.md: Shared global instructions for supported coding agents, held to a word budget (see Context Budget)home/claude/: Authored Claude Code settings, agents, commands, and hookshome/claude-code.nix: Claude Code file ownership and runtime-state boundaryhome/nix/default.nix: User Nix settingshome/nix/caches.nix: Binary cache URLs and public signing keyshome/shell.nix: Shell configuration and direnv integrationflake.nix: Add new input flakes or change system configurations
Add the cache URL and its complete public signing key to the caches attribute set in home/nix/caches.nix:
"https://your-cache.cachix.org" = "your-cache.cachix.org-1:your-public-key=";Apply with home-manager switch --flake .#sadjow. If the change includes a new untracked file, use home-manager switch --flake 'path:.#sadjow' so Nix includes it.
- Flake-based: Uses Nix flakes for improved reproducibility and dependency management
- Modular: Configuration split into logical modules for easier maintenance
- macOS Optimized: Specifically configured for Apple Silicon (aarch64-darwin)
- External Dependencies: Important tools like Claude Code managed as separate flakes
- Binary Caching: Multiple cache sources for fast package installation
- Agent Harness Skill Lineage: Keeps portable personal capabilities while deriving independent project-owned copies or adaptations (see Agent Harness Skill Lineage)
- Channel Strategy: Uses nixpkgs-unstable for latest packages (see Channel Strategy Documentation)
- Platform: macOS (Apple Silicon / aarch64-darwin)
- Nix Version: Compatible with Nix 2.19+
- Home Manager: Uses latest stable release
-
Flake evaluation errors:
nix flake check --show-trace
-
Binary cache issues:
nix store ping --store https://cache-url.org
-
Home Manager conflicts:
home-manager switch --flake . --show-trace
- Check the Home Manager manual
- Review the Nix flakes documentation
- See AGENTS.md for coding agent repository guidance
- claude-code-nix: Dedicated Nix package for Claude Code
- devenv: Fast, declarative development environments
This configuration is provided as-is for personal use. Feel free to adapt it for your own needs.
Note: This configuration is personalized for user "sadjow". You'll need to update usernames, paths, and personal preferences when adapting it for your own use.