No description https://setup.cnr.ad
  • Shell 50.5%
  • Nix 28.7%
  • Python 18.3%
  • Vim Script 2.5%
Find a file
2026-04-15 09:05:50 -06:00
AppSettings refactor(home-manager): extract launchd config and normalize scripts 2026-04-15 09:05:50 -06:00
modules refactor(home-manager): extract launchd config and normalize scripts 2026-04-15 09:05:50 -06:00
.editorconfig feat: add devShell for direnv, add zeit completion, remove screen sharing mode 2026-02-23 14:40:49 -07:00
.envrc chore: clean up .envrc comments 2026-02-23 14:41:29 -07:00
.gitignore feat: add devShell for direnv, add zeit completion, remove screen sharing mode 2026-02-23 14:40:49 -07:00
.gitmessage feat: add devShell for direnv, add zeit completion, remove screen sharing mode 2026-02-23 14:40:49 -07:00
bootstrap.sh refactor(home-manager): extract launchd config and normalize scripts 2026-04-15 09:05:50 -06:00
CLAUDE.md chore: add project CLAUDE.md, update newsboat/opencode/tmux/zsh configs 2026-04-03 18:23:11 -06:00
flake.lock refactor(home-manager): manage aliasrc outside nix, add jj-starship, senpai, pomodoro status 2026-03-23 00:10:58 -06:00
flake.nix refactor(home-manager): extract launchd config and normalize scripts 2026-04-15 09:05:50 -06:00
install.sh refactor(home-manager): extract launchd config and normalize scripts 2026-04-15 09:05:50 -06:00
open-apps.sh refactor(home-manager): extract launchd config and normalize scripts 2026-04-15 09:05:50 -06:00
README.md refactor(home-manager): extract launchd config and normalize scripts 2026-04-15 09:05:50 -06:00
registry.json wq 2025-09-01 18:29:47 -06:00
setup-keychain.sh refactor(home-manager): extract launchd config and normalize scripts 2026-04-15 09:05:50 -06:00
sync-to-bitwarden.sh refactor(home-manager): extract launchd config and normalize scripts 2026-04-15 09:05:50 -06:00
test-bw-keychain.sh refactor(home-manager): extract launchd config and normalize scripts 2026-04-15 09:05:50 -06:00

Nix Configuration for macOS

Personal nix-darwin + home-manager flake for macOS (Apple Silicon). One script, ~15 minutes, fully configured machine.

New Machine Setup

Prerequisites

  • macOS on Apple Silicon
  • Internet connection

Quick Start

# On a fresh machine with nothing installed:
curl -sSL https://setup.cnr.ad | sh

# Or if you've already cloned this repo:
./bootstrap.sh

The setup.cnr.ad URL serves install.sh, a tiny POSIX sh wrapper that downloads the full bootstrap.sh to a temp file and runs it with bash. This avoids stdin/pipe conflicts with interactive installers.

The bootstrap script handles everything:

Step What it does Time
1 Set computer name ~10 sec
2 Install Rosetta 2 ~1 min
3 Install Xcode CLT ~2 min
4 Install Nix (Determinate Systems) ~2 min
5 Install Homebrew ~1 min
6 Clone config repos (nix + nvim) via HTTPS ~10 sec
7 nix-darwin switch (packages, configs, system prefs) ~5 min
8 Create data directories (Mail, calendars, contacts) instant
9 Print keychain setup instructions (secrets added separately) instant

Post-Bootstrap

After the bootstrap script finishes:

# 1. Add secrets to macOS Keychain (REQUIRED before email/calendar/IRC work)
./setup-keychain.sh --personal --bitwarden   # recommended: auto-pull from Bitwarden
./setup-keychain.sh --personal               # alternative: interactive prompts
./setup-keychain.sh --personal --pass        # alternative: read from pass

# 2. Initial data sync (requires keychain secrets from step 1)
mbsync -a && notmuch new                 # email
vdirsyncer discover && vdirsyncer sync   # calendars + contacts

# 3. Sign into GUI apps
#    - Bitwarden
#    - Docker Desktop
#    - Raycast (license key)
#    - Ollama

Note: neomutt, mbsync, vdirsyncer, and senpai will all fail if you skip step 1. Run security find-generic-password -s gmail-imap -w to check if secrets are present.

What's Managed

nix-darwin (system level)

Category Details
System preferences Dark mode, caps-lock-to-escape, dock (auto-hide, left), menu bar hidden, Touch ID sudo
Homebrew casks Ghostty, Bitwarden, Docker Desktop, Rectangle, Raycast, Ollama, etc.
Homebrew brews newsboat, minikube, temporal, trippy, java
Services Tailscale
Shell Zsh as default

home-manager (user level)

Category What's deployed
Packages 100+ nix packages (neovim, go, rust, k8s tools, etc.)
Shell Zsh + oh-my-zsh (11 plugins), aliases, shortcuts
Prompt Starship with Kanagawa theme + jj integration
Terminal Tmux (C-a prefix, 6 plugins, Kanagawa theme, quick-launch bindings)
Git programs.git with GPG signing, conventional commit template
VCS programs.jujutsu (jj)
Email neomutt + mbsync + notmuch (Kanagawa colorscheme)
Calendar khal (3 calendars) + vdirsyncer (Google + RustiCal CalDAV)
Contacts khard + vdirsyncer CardDAV
Todos todoman (CalDAV-backed)
RSS newsboat (630+ YouTube feeds with duration filtering, podcasts, blogs)
Music mpd + ncmpcpp (vim bindings, wave visualizer)
Files vifm (vim bindings, iceberg colorscheme)
Monitor btop (vim keys, braille graphs)
IRC senpai + glirc
Window mgmt Rectangle (keyboard shortcuts config)
Security GPG agent with SSH, YubiKey agent, pass, browserpass
AI/Coding opencode, gh-dash
Scripts Custom scripts (mail sync, calendar import, contact save, etc.)
Agents 5 launchd agents (mail-sync, khal-notify, vdirsyncer, podcast-cleanup, yt-duration-cache)

Not managed by Nix

Item Reason
Neovim config Separate git repo (github.com/conrad760/nvim) — cloned by bootstrap
Keychain secrets Added by setup-keychain.sh — 7 entries for email, calendar, IRC, GitHub
Mail data ~/Mail/ — synced by mbsync after bootstrap
Calendar data ~/khal/ — synced by vdirsyncer after bootstrap
Contact data ~/contacts/ — synced by vdirsyncer after bootstrap
Password store ~/.password-store/ — clone from your git remote
GPG keys On YubiKey — no file management needed

Project Structure

.
├── flake.nix                          # Main flake (darwinConfigurations."shadow")
├── flake.lock                         # Locked dependency versions
├── install.sh                         # curl setup.cnr.ad | sh  (POSIX sh wrapper)
├── bootstrap.sh                       # Fresh machine setup script (called by install.sh)
├── setup-keychain.sh                  # Populate macOS Keychain secrets
├── AppSettings/
│   └── rectangle/RectangleConfig.json # Rectangle window manager config
├── modules/
│   ├── darwin/
│   │   ├── default.nix                # Imports system, environment, homebrew
│   │   └── settings/
│   │       ├── system.nix             # macOS system preferences
│   │       ├── environment.nix        # Shell, env vars
│   │       └── homebrew.nix           # Declarative Homebrew casks + brews
│   └── home-manager/
│       ├── default.nix                # Packages and config deployments
│       ├── launchd.nix                # launchd agents + activation hooks
│       ├── settings/
│       │   ├── zsh.nix                # Zsh + oh-my-zsh + plugins
│       │   ├── tmux.nix               # Tmux + plugins
│       │   └── git.nix                # programs.git + programs.jujutsu
│       └── files/
│           ├── bin/                    # Scripts -> ~/.local/bin/
│           └── config/                # Config files deployed to ~/.config/
│               ├── neomutt/           #   neomuttrc, mailcap, colors/
│               ├── newsboat/          #   config, base-urls, youtube-urls, podcast-urls
│               ├── vifm/              #   vifmrc, shortcuts, colors/
│               ├── ncmpcpp/           #   config, bindings
│               ├── btop/              #   btop.conf
│               ├── senpai/            #   senpai.scfg
│               ├── glirc/             #   config
│               ├── opencode/          #   opencode.json
│               ├── git/               #   gitcommit_template.txt
│               ├── mbsyncrc           #   -> ~/.mbsyncrc
│               ├── vdirsyncer-config  #   -> ~/.config/vdirsyncer/config
│               ├── khard.conf         #   -> ~/.config/khard/khard.conf
│               ├── todoman-config.py  #   -> ~/.config/todoman/config.py
│               ├── gh-dash.yml        #   -> ~/.config/gh-dash/config.yml
│               └── tmux-status.py     #   Custom tmux status bar
├── .editorconfig
├── .envrc                             # direnv: `use flake`
└── .gitmessage                        # Conventional commit template for this repo

Day-to-Day Usage

Rebuild after config changes

nixswitch                   # alias for: darwin-rebuild switch --flake ~/.config/nix#shadow

Update all flake inputs + rebuild

nixup                       # alias for: nix flake update + nixswitch

Add a nix package

  1. Add to home.packages in modules/home-manager/default.nix
  2. Run nixswitch

Add a Homebrew cask/brew

  1. Add to modules/darwin/settings/homebrew.nix
  2. Run nixswitch

Edit a deployed config

  1. Edit the source file under modules/home-manager/files/config/
  2. Run nixswitch (deploys the updated file)

For configs with path templating (khal, mpd, notmuch), edit the text = ''...'' block in default.nix.

Roll back

darwin-rebuild --rollback

Search for packages

nix search nixpkgs <package-name>

Secrets

Configs use security find-generic-password to read secrets from macOS Keychain at runtime. No secrets are stored in this repository.

Keychain service Used by Purpose
gmail-imap mbsync Gmail IMAP app password
gmail-smtp neomutt Gmail SMTP app password
vdirsyncer-google-url vdirsyncer Google Calendar private .ics URL
rustical-username vdirsyncer RustiCal CalDAV username
rustical-token vdirsyncer RustiCal CalDAV token
senpai senpai IRC password
gh-token gh CLI GitHub personal access token

To add or update secrets:

# Add new secret
security add-generic-password -a <account> -s <service> -w <password>

# Update existing secret
security add-generic-password -a <account> -s <service> -w <password> -U

# Delete a secret (needed to re-run setup-keychain.sh for that entry, since it skips existing)
security delete-generic-password -a <account> -s <service>

# Verify a secret
security find-generic-password -s <service> -w

Or use ./setup-keychain.sh to add all secrets interactively.

setup-keychain.sh skips entries that already exist. To re-pull a secret from Bitwarden, delete it first with security delete-generic-password, then re-run the script.

Git Workflow

This repo uses conventional commits:

<type>(<scope>): <description>

Types: feat, fix, docs, style, refactor, test, chore, perf, ci, build, revert

Examples:

feat(home-manager): add ncmpcpp config deployment
fix(darwin): correct homebrew cask list
docs: update README with bootstrap instructions

Troubleshooting

Build fails

nix flake check                        # validate flake
nix-instantiate --parse <file.nix>     # check nix syntax
rm -rf result* && nixswitch            # clean rebuild

nix flake check runs Nix formatting checks, shell script lint/format checks, and a flake eval for darwinConfigurations.shadow.

home-manager file conflict

If darwin-rebuild fails with "file already exists":

# Back up the conflicting file and retry
mv ~/.config/<path> ~/.config/<path>.bak
nixswitch

This happens when a file exists at a path home-manager wants to manage. The first rebuild after adding new file deployments may hit this.

neomutt / mbsync / vdirsyncer / senpai not working

Most likely cause: missing keychain secrets. Check with:

security find-generic-password -s gmail-imap -w    # should print your app password
security find-generic-password -s gmail-smtp -w

If you get SecKeychainSearchCopyNext: The specified item could not be found, run:

./setup-keychain.sh --personal --bitwarden

Then sync mail with mbsync gmail before opening neomutt.

Homebrew failures

brew doctor                    # diagnose brew issues
brew update                    # update brew itself
nixswitch                      # retry

Rollback to previous generation

darwin-rebuild --rollback      # go back one generation
# or list and switch to a specific generation:
nix-env --list-generations -p /nix/var/nix/profiles/system
nix-env --switch-generation <N> -p /nix/var/nix/profiles/system

Notes

  • files/config/tmux-status.py contains a hardcoded email for filtering declined CalDAV events. Update the USER_EMAIL line if needed.
  • Newsboat youtube-urls and podcast-urls are deployed via home-manager. To add/remove feeds, edit the files in modules/home-manager/files/config/newsboat/ and run nixswitch.

Resources