Personal macOS configuration for an Apple Silicon Mac. Nix flakes compose nix-darwin, Home Manager, Homebrew, editable dotfile symlinks, and the local AI agent environment.
| Layer | Authority | Responsibility |
|---|---|---|
| Flake composition | flake.nix |
Inputs, supported outputs, username, platform, and shared packages |
| macOS system | nix/darwin/default.nix |
System defaults, Homebrew, Nix settings, and privileged activation |
| User environment | nix/home/default.nix |
CLI packages, Home Manager programs, symlinks, and user activation |
| Local Nix packages | nix/packages/ |
Local derivations and narrowly scoped package overrides |
| Editable configuration | Top-level application directories | Files linked from this checkout into $HOME |
The primary outputs are:
darwinConfigurations.mbp: the complete MacBook Pro configurationdarwinConfigurations.mba: the complete MacBook Air configurationhomeConfigurations.kaito: the user environment for faster iterationpackages.<system>.codex: the pinned Codex CLI exposed by this flakedevShells.<system>.default: commitlint and lefthook for repository work
The repository currently assumes:
aarch64-darwin- macOS user
kaito - checkout path
/Users/kaito/ghq/github.com/furedea/dotfiles
The username and checkout path are defined together in flake.nix.
Change them there before applying the configuration for another user or path.
-
Install upstream Nix with flakes enabled:
curl -sSfL https://artifacts.nixos.org/nix-installer | sh -s -- install --enable-flakes -
Open a new shell and clone this repository at the configured path:
git clone https://github.com/furedea/dotfiles \ "$HOME/ghq/github.com/furedea/dotfiles" cd "$HOME/ghq/github.com/furedea/dotfiles"
-
Bootstrap nix-darwin with the output for the current Mac:
# Use .#mba on the MacBook Air. sudo nix run \ github:nix-darwin/nix-darwin/nix-darwin-25.11#darwin-rebuild \ -- switch --flake .#mbp
The first switch installs the darwin-rebuild and home-manager commands used
for later updates.
Use the full switch to converge the actual Mac, including both nix-darwin and its integrated Home Manager profile:
# Use #mba on the MacBook Air.
sudo darwin-rebuild switch --flake \
"$HOME/ghq/github.com/furedea/dotfiles/#mbp"For changes limited to nix/home/default.nix, the
standalone Home Manager output provides a faster, unprivileged feedback loop:
home-manager switch --flake \
"$HOME/ghq/github.com/furedea/dotfiles/#kaito"Run :Lazy update in Neovim to update editor plugins and commit the resulting
nvim/lazy-lock.json change.
The standalone and nix-darwin-integrated Home Manager outputs use different
profiles. A package removed with only home-manager switch may remain visible
from /etc/profiles/per-user/kaito until the next full darwin-rebuild switch.
Files managed with mkOutOfStoreSymlink update immediately when their source in
this checkout changes. Nix-generated files and Home Manager program settings
still require a switch.
nix/darwin/default.nix manages:
- Nix flakes, weekly garbage collection, and Touch ID authentication for
sudo - keyboard, text input, Finder, Dock, trackpad, screenshots, lock screen, menu clock, Spotlight, and other macOS defaults
- Dock applications: Raycast, Arc, Obsidian, OrbStack, Slack, System Settings, and Nani
- display sleep disabled on both battery and charger
- Homebrew through nix-homebrew, with unlisted packages removed on activation
The current cask set is:
arc bitwarden chatgpt
deepl discord firefox
font-jetbrains-mono ghostty google-chrome
homerow karabiner-elements mactex
microsoft-excel microsoft-powerpoint microsoft-word
nani obsidian orbstack
raycast skim slack
steam tailscale-app vimr
The cask list in nix/darwin/default.nix is authoritative.
nix/home/default.nix installs and configures:
- shell and navigation tools including Zsh plugins, Starship, Atuin, direnv, zoxide, ghq, roots, git-wt, fzf, eza, bat, fd, ripgrep, and Yazi
- Git, Delta, GitHub CLI, Lazygit, Neovim, Tree-sitter, and Vim configuration
- Nix, Bash, Python, Rust, TypeScript, Lua, LaTeX, formatting, and linting tools
- Claude Code, Codex, OpenCode, and Herdr
programs.git,programs.delta,programs.gh,programs.lazygit,programs.direnv,programs.atuin,programs.agent-harness, andprograms.yazi
Activation also installs the stable Rust toolchain, installs the default Python requested by uv, creates the SSH signing key when absent, and reconciles the commit-pinned Herdr plugin set.
agent-harness owns the shared Claude
Code and Codex instructions, policies, hooks, skills, and generated files. This
repository builds release-matched Herdr and Moshi hook bundles, then passes them
through the module's generic hooks.extra boundary. Herdr comes from the pinned
flake input. Moshi Hook 0.2.87 is pinned in Nix for deterministic hook generation,
while the Homebrew installation remains the runtime daemon used by generated hooks.
Pairing tokens and mutable Moshi state are never added to the Nix store.
Herdr is pinned as a flake input and replaces tmux for local and managed remote
terminal sessions. The local configuration uses ctrl+a as its prefix and adds
popup commands for Yazi, Lazygit, a scratch shell, and the reviewr plugin.
Attach directly to a managed remote host with:
herdr --remote <ssh-target>
herdr --remote <ssh-target> --session <name>The persiyanov.reviewr plugin revision is declared in
nix/home/default.nix, synchronized during Home Manager
activation by herdr/sync_plugins.sh, and justified by
ADR-0002.
The following repository sources are linked directly into the home directory:
| Source | Target |
|---|---|
zsh/.zshrc, zsh/.zshenv, zsh/.zprofile |
~/.zshrc, ~/.zshenv, ~/.zprofile |
bash/.bashrc |
~/.bashrc |
git/ignore |
~/.config/git/ignore |
nvim/ |
~/.config/nvim |
vim/.vimrc |
~/.vimrc |
starship/starship.toml |
~/.config/starship.toml |
ghostty/config |
~/.config/ghostty/config |
karabiner/karabiner.json |
~/.config/karabiner/karabiner.json |
herdr/config.toml, herdr/reviewr.toml |
Herdr config and reviewr plugin config |
dprint/dprint.json |
~/dprint.json |
prettier/.prettierrc |
~/.prettierrc |
.editorconfig |
~/.editorconfig |
Git, GitHub CLI, Lazygit, Atuin, Yazi, Zsh plugin paths, and agent-harness files are generated from Home Manager modules rather than linked from similarly named reference files.
These tracked files are not applied by Home Manager or nix-darwin:
atuin/config.toml,gh/config.yml,git/.gitconfig, and the standaloneyazi/*.tomlfiles are reference copies; their active configuration comes fromnix/home/default.nixraycast/*.rayconfigis a manual Raycast exporttemplates/contains small files copied into other repositories as needed
.
├── flake.nix # Flake inputs and outputs
├── nix/ # nix-darwin, Home Manager, and local packages
├── docs/adr/ # Durable architecture decisions
├── zsh/ and bash/ # Interactive shell configuration
├── nvim/ and vim/ # Editor configuration
├── herdr/ # Herdr UI and plugin synchronization
├── ghostty/ # Terminal configuration
├── karabiner/ # Keyboard remapping
├── starship/ # Shell prompt
├── github/ # Repository creation and policy scripts
├── tests/ # Bats specifications by domain
├── dprint/ and prettier/ # Global formatter configuration
├── raycast/ # Manual settings export
└── templates/ # Manually copied reference templates
The Zsh configuration:
- replaces common commands with modern equivalents such as
eza,bat,rg,fd, anddust - initializes zoxide, Starship, direnv, Atuin, and Nix-provided Zsh plugins
- provides
yfor Yazi directory changes andgrfor the Git root - binds
ctrl+gto fuzzy repository and monorepo navigation through ghq, roots, and fzf - provides editor-based esa helpers backed by the official esa CLI, with incremental WIP saves from Neovim
The Bash configuration provides the same core aliases and initializes zoxide, Starship, and Atuin.
Create and clone a repository into the ghq root, then apply the standard repository policy:
repo create <name-or-owner/name> (--public|--private|--internal) [options]Common creation options include --template, --description, --homepage,
--add-readme, --gitignore, --license, --disable-issues,
--disable-wiki, and --team. Options that conflict with the managed ghq
clone destination (--clone, --source, --push, and --remote) are
rejected. Run repo create --help for details.
Apply the standard repository settings, vulnerability alerts, and ruleset to an existing repository:
repo configure <name-or-owner/name>A name without an owner defaults to the authenticated GitHub user. Run
repo --help or repo -h for the command overview. The commands are covered
by Bats tests under tests/github/.
Lefthook runs the pre-commit format and lint checks for changed files:
lefthook run pre-commitRun the executable specifications directly with:
bats tests/github
bats tests/herdr
bats tests/esa
bats tests/nixCI checks GitHub scripts with Bats and ShellCheck, checks Lua with Selene and StyLua, checks Nix with Statix, deadnix, and nixfmt, checks JSON/TOML with dprint, and lints prose with AutoCorrect. GitHub Actions are also checked with actionlint, zizmor, and CodeQL.
dprint intentionally owns JSON and TOML only. Markdown is formatted with prettierd because its four-space nested-list indentation matches the preferred Obsidian style.
The repository cannot automate credentials or settings protected by macOS TCC. After the first switch, configure as needed:
- sign in to GUI applications and iCloud
- run
gh auth login - run
esa auth login --scopes "read:post write:post" - configure Atuin synchronization credentials if history sync is wanted
- pair Moshi Hook with the iPhone app when restoring a host
- configure Night Shift, True Tone, display resolution, and Accessibility display options in System Settings
- import
raycast/*.rayconfigwhen restoring Raycast manually
Update every flake input, then apply the complete configuration:
nix flake update
# Use #mba on the MacBook Air.
sudo darwin-rebuild switch --flake \
"$HOME/ghq/github.com/furedea/dotfiles/#mbp"Update a single input when only one tool needs to move:
nix flake update llm-agents
home-manager switch --flake \
"$HOME/ghq/github.com/furedea/dotfiles/#kaito"