This commit is contained in:
Harivansh Rathi 2026-03-12 13:51:18 -04:00
commit 2bf50c8969
12 changed files with 784 additions and 0 deletions

3
.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
result
.direnv

58
README.md Normal file
View file

@ -0,0 +1,58 @@
# Rathi's Nix Config
This repo is the start of a full-machine macOS setup built with:
- `nix-darwin` for system settings
- `home-manager` for home directory files
- `nix-homebrew` plus `homebrew.*` for the large set of macOS packages and casks that still make sense to manage through Homebrew
The friend config under `tmp/dots` is kept here as reference material only. The config in this repo is your own scaffold.
## Current approach
The migration is intentionally conservative:
- Homebrew inventory is captured declaratively in [`modules/homebrew.nix`](./modules/homebrew.nix).
- Your live dotfiles stay the source of truth for now via out-of-store symlinks from [`home/dotfiles.nix`](./home/dotfiles.nix).
- Cleanup is set to `"none"` so the first switch does not delete anything you forgot to inventory.
That gives you a reproducible baseline without forcing a risky rewrite of shell/editor configs on day one.
## Layout
- `flake.nix`: top-level flake and host wiring
- `hosts/hari-macbook-pro/default.nix`: this machine's host config
- `modules/base.nix`: Nix settings and core packages
- `modules/macos.nix`: macOS defaults and host-level settings
- `modules/homebrew.nix`: taps, brews, and casks from the current machine
- `home/dotfiles.nix`: Home Manager symlinks into `~/dots`
- `docs/machine-audit.md`: inventory and migration notes from the current box
## Commands
Bootstrap the host:
```bash
nix run github:LnL7/nix-darwin/master#darwin-rebuild -- switch --flake .#hari-macbook-pro
```
After the first successful switch:
```bash
just switch
just build
just check
```
## What Still Needs Manual Work
- Secrets and tokens under `~/.secrets`, `~/.npmrc`, `~/.config/gcloud`, `~/.config/gh`, and similar paths
- Launch agents that are currently outside Nix
- App state under `~/Library/Application Support`
- Apps installed outside Homebrew casks or the App Store
- Translating raw files from `~/dots` into pure Home Manager modules over time
## Important Note About Dotfiles
Your live machine currently points at `~/dots`, not `~/Documents/GitHub/dots`. This config follows the live machine and expects `~/dots` to exist.

255
docs/machine-audit.md Normal file
View file

@ -0,0 +1,255 @@
# Machine Audit
This is the baseline inventory used to seed the first pass of this Nix config.
## Host Summary
- Machine: `hari-macbook-pro`
- Hostname: `hari-macbook-pro.local`
- Platform: `arm64-darwin`
- OS: macOS `26.3` (`25D5112c`)
- Nix: `2.34.1`
- `darwin-rebuild`: not installed yet
## Filesystem Roots Checked
Top-level roots on `/`:
- `Applications`
- `Library`
- `System`
- `Users`
- `nix`
- `opt`
- `private`
- `usr`
Large user-owned roots spotted during audit:
- `/Users/rathi`
- `/Users/rathi/Documents`
- `/Users/rathi/Library`
- `/Users/rathi/.config`
- `/Users/rathi/.local`
- `/opt/homebrew`
## Live Dotfiles Source Of Truth
The live machine is wired to `~/dots`, not `~/Documents/GitHub/dots`.
Confirmed symlinks:
- `~/.gitconfig -> ~/dots/git/.gitconfig`
- `~/.zshrc -> ~/dots/zsh/.zshrc`
There is also a duplicate clone at `~/Documents/GitHub/dots`. Content matched during the audit, but the active machine points at `~/dots`.
## Homebrew Inventory
This repo currently mirrors the top-level Homebrew inventory rather than every transitive dependency.
### Taps
- `daytonaio/tap`
- `getcompanion-ai/tap`
- `hashicorp/tap`
- `homebrew/services`
- `humanlayer/humanlayer`
- `jnsahaj/lumen`
- `nicosuave/tap`
- `nikitabobko/tap`
- `opencode-ai/tap`
- `pantsbuild/tap`
- `pipedreamhq/pd-cli`
- `steipete/tap`
- `stripe/stripe-cli`
- `supabase/tap`
- `tallesborges/zdx`
- `withgraphite/tap`
### Brew Leaves
The current leaves were captured into [`modules/homebrew.nix`](../modules/homebrew.nix). A few noteworthy details:
- `python@3.13` was installed but `link: false` in the generated Brewfile
- `withgraphite/tap/graphite` was also `link: false`
- Go tools and one cargo tool were present in the Brewfile but are not yet expressed in the Nix module
- VS Code extension `anthropic.claude-code` was also present in the Brewfile and is not yet managed here
### Casks
Current casks were also captured into [`modules/homebrew.nix`](../modules/homebrew.nix), including:
- `aerospace`
- `codex`
- `companion`
- `gcloud-cli`
- `ghostty@tip`
- `warp`
- `virtualbox`
### Brew Services
Installed but not currently running:
- `cloudflared`
- `postgresql@14`
- `postgresql@16`
- `postgresql@17`
- `redis`
- `tailscale`
- `unbound`
## Apps Outside Current Brew Casks
The following apps were present in `/Applications` but did not match the current cask inventory during a rough audit, so they should be reviewed separately:
- `Amphetamine.app`
- `Cap.app`
- `ChatGPT.app`
- `Claude.app`
- `Cluely.app`
- `Conductor.app`
- `Dia.app`
- `Docker.app`
- `Granola.app`
- `Helium.app`
- `Karabiner-Elements.app`
- `Karabiner-EventViewer.app`
- `Klack.app`
- `Numbers.app`
- `PastePal.app`
- `Raycast.app`
- `Readout.app`
- `Rectangle.app`
- `Safari.app`
- `Screen Studio.app`
- `Signal.app`
- `Tailscale.app`
- `Telegram.app`
- `Typora.app`
- `Wispr Flow.app`
- `Zen.app`
- `kitty.app`
- `logioptionsplus.app`
Some of these may belong in:
- Mac App Store
- direct DMG installers
- manual vendor installers
- future Homebrew casks that were not part of the current audit
## Launch Agents Found
These are current launch agents worth deciding on explicitly:
- `com.nanoclaw.plist`
- `com.thread-view.collector.plist`
- `com.thread-view.ngrok.plist`
- `pi.plist`
- `homebrew.mxcl.postgresql@16.plist`
- `org.virtualbox.vboxwebsrv.plist`
- Google updater agents
- iMazing mini agent
These are not yet represented in Nix.
## Config Directories Found
Notable user config roots under `~/.config`:
- `agents`
- `amp`
- `gcloud`
- `gh`
- `gh-dash`
- `ghostty`
- `git`
- `graphite`
- `k9s`
- `karabiner`
- `kitty`
- `nanoclaw`
- `opencode`
- `raycast`
- `rpi`
- `stripe`
- `tmux`
- `worktrunk`
- `zed`
Notable app state under `~/Library/Application Support`:
- `Claude`
- `Codex`
- `Code`
- `Cursor`
- `Docker Desktop`
- `Ghostty`
- `Google`
- `LogiOptionsPlus`
- `OpenAI`
- `Raycast`
- `Screen Studio`
- `Signal`
- `Slack`
- `Telegram Desktop`
- `Warp`
- `Zed`
These paths are exactly why the first config keeps Homebrew and dotfile migration conservative.
## Codebase Summary
Code roots found:
- `~/Documents/GitHub` with `108` repos
- `~/code/symphony-workspaces`
- `~/dev/diffs.nvim`
Repo manifest counts under `~/Documents/GitHub`:
- `package.json`: `56`
- `pnpm-workspace.yaml`: `7`
- `turbo.json`: `5`
- `pyproject.toml`: `6`
- `requirements.txt`: `7`
- `go.mod`: `3`
- `Cargo.toml`: `4`
- `flake.nix`: `4`
- `Dockerfile`: `10`
- `docker-compose.yml`: `7`
Practical implication:
- JavaScript/TypeScript is the dominant toolchain
- Python is the second major toolchain
- Go and Rust are both active enough to be first-class system runtimes
- Docker and local infra tooling belong in the baseline machine config
## Migration Boundaries
Safe to move into Nix now:
- core CLI packages
- current Homebrew taps, brews, and casks
- dotfiles already living in `~/dots`
- basic macOS defaults
Should stay manual or secret-managed for now:
- `~/.secrets`
- `~/.npmrc`
- `~/.yarnrc`
- cloud credentials and tokens under `~/.config`
- app-internal state in `~/Library/Application Support`
- custom launch agents until they are rewritten declaratively
Recommended next steps:
1. Switch this host once with cleanup disabled.
2. Translate `git`, `zsh`, and `ghostty` from raw symlinks into pure Home Manager modules.
3. Decide whether `~/dots` should remain the source of truth or be folded into this repo.
4. Capture secrets explicitly instead of relying on ad hoc local files.
5. Review the unmanaged `/Applications` set and choose Homebrew cask, App Store, or manual buckets for each.

106
flake.lock generated Normal file
View file

@ -0,0 +1,106 @@
{
"nodes": {
"brew-src": {
"flake": false,
"locked": {
"lastModified": 1769363988,
"narHash": "sha256-BiGPeulrDVetXP+tjxhMcGLUROZAtZIhU5m4MqawCfM=",
"owner": "Homebrew",
"repo": "brew",
"rev": "d01011cac6d72032c75fd2cd9489909e95d9faf2",
"type": "github"
},
"original": {
"owner": "Homebrew",
"ref": "5.0.12",
"repo": "brew",
"type": "github"
}
},
"home-manager": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1773332277,
"narHash": "sha256-1V+wRrZD9Sw12AQBUWk9CR+XhDZQ8q6yBE0S3Wjbd1M=",
"owner": "nix-community",
"repo": "home-manager",
"rev": "4aeef1941f862fe3a70d1b8264b4e289358c2325",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "home-manager",
"type": "github"
}
},
"nix-darwin": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1773000227,
"narHash": "sha256-zm3ftUQw0MPumYi91HovoGhgyZBlM4o3Zy0LhPNwzXE=",
"owner": "LnL7",
"repo": "nix-darwin",
"rev": "da529ac9e46f25ed5616fd634079a5f3c579135f",
"type": "github"
},
"original": {
"owner": "LnL7",
"ref": "master",
"repo": "nix-darwin",
"type": "github"
}
},
"nix-homebrew": {
"inputs": {
"brew-src": "brew-src"
},
"locked": {
"lastModified": 1769437432,
"narHash": "sha256-8d7KnCpT2LweRvSzZYEGd9IM3eFX+A78opcnDM0+ndk=",
"owner": "zhaofengli-wip",
"repo": "nix-homebrew",
"rev": "a5409abd0d5013d79775d3419bcac10eacb9d8c5",
"type": "github"
},
"original": {
"owner": "zhaofengli-wip",
"repo": "nix-homebrew",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1773201692,
"narHash": "sha256-NXrKzNMniu4Oam2kAFvqJ3GB2kAvlAFIriTAheaY8hw=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b6067cc0127d4db9c26c79e4de0513e58d0c40c9",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"home-manager": "home-manager",
"nix-darwin": "nix-darwin",
"nix-homebrew": "nix-homebrew",
"nixpkgs": "nixpkgs"
}
}
},
"root": "root",
"version": 7
}

62
flake.nix Normal file
View file

@ -0,0 +1,62 @@
{
description = "Rathi's macOS nix-darwin + Home Manager config";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
nix-darwin = {
url = "github:LnL7/nix-darwin/master";
inputs.nixpkgs.follows = "nixpkgs";
};
home-manager = {
url = "github:nix-community/home-manager";
inputs.nixpkgs.follows = "nixpkgs";
};
nix-homebrew = {
url = "github:zhaofengli-wip/nix-homebrew";
};
};
outputs = inputs @ {
self,
nixpkgs,
nix-darwin,
home-manager,
nix-homebrew,
...
}: let
system = "aarch64-darwin";
username = "rathi";
hostname = "hari-macbook-pro";
pkgs = import nixpkgs {inherit system;};
in {
formatter.${system} = pkgs.alejandra;
darwinConfigurations.${hostname} = nix-darwin.lib.darwinSystem {
inherit system;
specialArgs = {inherit inputs self username hostname;};
modules = [
./hosts/${hostname}
home-manager.darwinModules.home-manager
nix-homebrew.darwinModules.nix-homebrew
{
users.users.${username}.home = "/Users/${username}";
home-manager.useGlobalPkgs = true;
home-manager.useUserPackages = true;
home-manager.backupFileExtension = "hm-bak";
home-manager.users.${username} = import ./home;
nix-homebrew = {
enable = true;
enableRosetta = true;
user = username;
autoMigrate = true;
};
}
];
};
};
}

8
home/default.nix Normal file
View file

@ -0,0 +1,8 @@
{...}: {
imports = [
./dotfiles.nix
];
home.stateVersion = "24.11";
programs.home-manager.enable = true;
}

44
home/dotfiles.nix Normal file
View file

@ -0,0 +1,44 @@
{
config,
lib,
...
}: let
dotfilesDir = "${config.home.homeDirectory}/dots";
link = path: config.lib.file.mkOutOfStoreSymlink "${dotfilesDir}/${path}";
in {
home.activation.ensureDotfilesRepo = lib.hm.dag.entryBefore ["checkLinkTargets"] ''
if [ ! -d "${dotfilesDir}" ]; then
echo "Expected dotfiles repo at ${dotfilesDir}"
exit 1
fi
'';
home.file.".aerospace.toml".source = link "aerospace/.aerospace.toml";
home.file.".gitconfig".source = link "git/.gitconfig";
home.file.".zshenv".source = link "zsh/.zshenv";
home.file.".zshrc".source = link "zsh/.zshrc";
home.file.".config/nvim".source = link "nvim/.config/nvim";
home.file.".config/tmux/tmux.conf".source = link "tmux/.config/tmux/tmux.conf";
home.file.".config/tmux/session-list.sh".source = link "tmux/.config/tmux/session-list.sh";
home.file.".config/karabiner/karabiner.json".source =
link "karabiner/.config/karabiner/karabiner.json";
home.file.".claude/CLAUDE.md".source = link "claude/.claude/CLAUDE.md";
home.file.".claude/commands".source = link "claude/.claude/commands";
home.file.".claude/settings.json".source = link "claude/.claude/settings.json";
home.file.".claude/statusline.sh".source = link "claude/.claude/statusline.sh";
home.file.".codex/AGENTS.md".source = link "codex/.codex/AGENTS.md";
home.file.".codex/config.toml".source = link "codex/.codex/config.toml";
home.file."Library/Application Support/com.mitchellh.ghostty/config".source =
link "ghostty/Library/Application Support/com.mitchellh.ghostty/config.ghostty";
home.file."Library/Application Support/lazygit/config.yml".source =
link "lazygit/Library/Application Support/lazygit/config.yml";
}

View file

@ -0,0 +1,25 @@
{
pkgs,
self,
username,
hostname,
...
}: {
imports = [
../../modules/base.nix
../../modules/macos.nix
../../modules/homebrew.nix
];
networking.hostName = hostname;
users.users.${username} = {
name = username;
home = "/Users/${username}";
shell = pkgs.zsh;
};
system.primaryUser = username;
system.configurationRevision = self.rev or self.dirtyRev or null;
system.stateVersion = 6;
}

15
justfile Normal file
View file

@ -0,0 +1,15 @@
default:
just --list
check:
nix flake check
build:
nix build .#darwinConfigurations.hari-macbook-pro.system
switch:
nix run github:LnL7/nix-darwin/master#darwin-rebuild -- switch --flake .#hari-macbook-pro
fmt:
nix fmt

59
modules/base.nix Normal file
View file

@ -0,0 +1,59 @@
{
pkgs,
username,
...
}: {
nix.enable = true;
nix.settings = {
experimental-features = [
"nix-command"
"flakes"
];
trusted-users = [
"@admin"
username
];
};
nix.gc = {
automatic = true;
interval = {
Weekday = 7;
Hour = 3;
Minute = 0;
};
options = "--delete-older-than 14d";
};
nixpkgs.config.allowUnfree = true;
programs.zsh.enable = true;
environment.shells = [pkgs.zsh];
environment.systemPackages = with pkgs; [
bat
curl
fd
fzf
git
gnupg
go
jq
just
neovim
nodejs_22
python3
ripgrep
rustup
tree
uv
wget
zoxide
];
environment.variables = {
EDITOR = "nvim";
VISUAL = "nvim";
};
}

135
modules/homebrew.nix Normal file
View file

@ -0,0 +1,135 @@
{...}: {
homebrew = {
enable = true;
onActivation = {
autoUpdate = false;
upgrade = false;
cleanup = "none";
};
taps = [
"daytonaio/tap"
"getcompanion-ai/tap"
"hashicorp/tap"
"homebrew/services"
"humanlayer/humanlayer"
"jnsahaj/lumen"
"nicosuave/tap"
"nikitabobko/tap"
"opencode-ai/tap"
"pantsbuild/tap"
"pipedreamhq/pd-cli"
"steipete/tap"
"stripe/stripe-cli"
"supabase/tap"
"tallesborges/zdx"
"withgraphite/tap"
];
brews = [
"apache-arrow"
"binwalk"
"cloc"
"cloudflared"
"cmake"
"coreutils"
"criterion"
"daytonaio/tap/daytona"
"diff-so-fancy"
"e2fsprogs"
"fd"
"ffmpeg"
"flyctl"
"fzf"
"gh"
"git-delta"
"git-filter-repo"
"git-lfs"
"gitleaks"
"gnu-time"
"go"
"hashicorp/tap/terraform"
"helm"
"imagemagick"
"jnsahaj/lumen/lumen"
"jq"
"k9s"
"kind"
"lazygit"
"libpq"
"librsvg"
"livekit"
"livekit-cli"
"llmfit"
"mactop"
"minikube"
"mint"
"mise"
"neovim"
"nicosuave/tap/memex"
"node"
"openjdk"
"pandoc"
"pipx"
"poppler"
"portaudio"
"postgresql@14"
"postgresql@16"
"postgresql@17"
"potrace"
"redis"
"resvg"
"ripgrep"
"semgrep"
"sevenzip"
"sox"
"steipete/tap/bird"
"steipete/tap/gogcli"
"steipete/tap/summarize"
"stow"
"stripe/stripe-cli/stripe"
"supabase/tap/supabase"
"swiftformat"
"swiftlint"
"tailscale"
"tmux"
"tree"
"trivy"
"universal-ctags"
"websocat"
"wget"
"withgraphite/tap/graphite"
"worktrunk"
"yazi"
"yq"
"yt-dlp"
"zoxide"
"zsh-autosuggestions"
"zsh-syntax-highlighting"
];
casks = [
"aerospace"
"anaconda"
"codelayer"
"codex"
"codexbar"
"companion"
"emacs-app"
"font-jetbrains-mono"
"font-symbols-only-nerd-font"
"gcloud-cli"
"ghostty@tip"
"ngrok"
"opencode-desktop"
"osaurus"
"pants"
"riptide-beta"
"riptide-dev"
"riptide-experimental"
"virtualbox"
"warp"
];
};
}

14
modules/macos.nix Normal file
View file

@ -0,0 +1,14 @@
{...}: {
security.pam.services.sudo_local.touchIdAuth = true;
system.defaults = {
dock.autohide = true;
dock.show-recents = false;
NSGlobalDomain = {
ApplePressAndHoldEnabled = false;
InitialKeyRepeat = 15;
KeyRepeat = 2;
};
};
}