commit 2bf50c8969ec588fa5cdf55f1c73f569f4f5d73a Author: Harivansh Rathi Date: Thu Mar 12 13:51:18 2026 -0400 init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3f9940a --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +result +.direnv + diff --git a/README.md b/README.md new file mode 100644 index 0000000..b674e74 --- /dev/null +++ b/README.md @@ -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. + diff --git a/docs/machine-audit.md b/docs/machine-audit.md new file mode 100644 index 0000000..6ea0c14 --- /dev/null +++ b/docs/machine-audit.md @@ -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. diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..dc87662 --- /dev/null +++ b/flake.lock @@ -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 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..0abdcad --- /dev/null +++ b/flake.nix @@ -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; + }; + } + ]; + }; + }; +} diff --git a/home/default.nix b/home/default.nix new file mode 100644 index 0000000..d2f9da1 --- /dev/null +++ b/home/default.nix @@ -0,0 +1,8 @@ +{...}: { + imports = [ + ./dotfiles.nix + ]; + + home.stateVersion = "24.11"; + programs.home-manager.enable = true; +} diff --git a/home/dotfiles.nix b/home/dotfiles.nix new file mode 100644 index 0000000..9f7a9c1 --- /dev/null +++ b/home/dotfiles.nix @@ -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"; +} diff --git a/hosts/hari-macbook-pro/default.nix b/hosts/hari-macbook-pro/default.nix new file mode 100644 index 0000000..8a6b306 --- /dev/null +++ b/hosts/hari-macbook-pro/default.nix @@ -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; +} diff --git a/justfile b/justfile new file mode 100644 index 0000000..9c64db7 --- /dev/null +++ b/justfile @@ -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 + diff --git a/modules/base.nix b/modules/base.nix new file mode 100644 index 0000000..2876271 --- /dev/null +++ b/modules/base.nix @@ -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"; + }; +} diff --git a/modules/homebrew.nix b/modules/homebrew.nix new file mode 100644 index 0000000..b1b37ad --- /dev/null +++ b/modules/homebrew.nix @@ -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" + ]; + }; +} diff --git a/modules/macos.nix b/modules/macos.nix new file mode 100644 index 0000000..a192f41 --- /dev/null +++ b/modules/macos.nix @@ -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; + }; + }; +}