From f42a092b60dcd95ea646b21074f5f681a854b43b Mon Sep 17 00:00:00 2001 From: Morten Olsen Date: Mon, 15 Dec 2025 11:42:42 +0100 Subject: [PATCH] init --- .gitignore | 33 + Makefile | 128 ++++ README.md | 265 +++++++ docs/migration-analysis.md | 486 +++++++++++++ docs/nix-architecture.md | 1288 +++++++++++++++++++++++++++++++++++ docs/usage.md | 626 +++++++++++++++++ flake.lock | 69 ++ flake.nix | 105 +++ home/default.nix | 83 +++ home/personal.nix | 77 +++ home/work.nix | 82 +++ hosts/personal/default.nix | 56 ++ hosts/work/default.nix | 68 ++ modules/darwin/homebrew.nix | 177 +++++ modules/home/apps.nix | 172 +++++ modules/home/git-files.nix | 140 ++++ modules/home/git.nix | 172 +++++ modules/home/packages.nix | 168 +++++ modules/home/shell.nix | 227 ++++++ modules/home/ssh.nix | 152 +++++ modules/home/tmux.nix | 121 ++++ 21 files changed, 4695 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 README.md create mode 100644 docs/migration-analysis.md create mode 100644 docs/nix-architecture.md create mode 100644 docs/usage.md create mode 100644 flake.lock create mode 100644 flake.nix create mode 100644 home/default.nix create mode 100644 home/personal.nix create mode 100644 home/work.nix create mode 100644 hosts/personal/default.nix create mode 100644 hosts/work/default.nix create mode 100644 modules/darwin/homebrew.nix create mode 100644 modules/home/apps.nix create mode 100644 modules/home/git-files.nix create mode 100644 modules/home/git.nix create mode 100644 modules/home/packages.nix create mode 100644 modules/home/shell.nix create mode 100644 modules/home/ssh.nix create mode 100644 modules/home/tmux.nix diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b6681fd --- /dev/null +++ b/.gitignore @@ -0,0 +1,33 @@ +# Nix build outputs +result +result-* + +# Nix flake local files +.direnv/ + +# macOS +.DS_Store +.AppleDouble +.LSOverride +._* + +# Editor files +*.swp +*.swo +*~ +.vscode/ +.idea/ + +# Local overrides (for testing without committing) +local.nix +*.local.nix + +# Secrets (should never be committed) +secrets.yaml +*.age +*.enc + +# Temporary files +*.tmp +*.temp +*.log \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..0d7c261 --- /dev/null +++ b/Makefile @@ -0,0 +1,128 @@ +# Makefile for Nix Home Manager configuration +# Provides convenient targets for testing, building, and applying configurations + +# Default target shows help +.DEFAULT_GOAL := help + +# Flake reference for configurations +FLAKE_PERSONAL := .#personal +FLAKE_WORK := .#work + +# Nix experimental features flag (enables flakes without system config changes) +NIX_FLAGS := --extra-experimental-features 'nix-command flakes' + +# ============================================================================ +# PHONY TARGETS +# ============================================================================ +.PHONY: check show dry-run-personal dry-run-work \ + switch-personal switch-work build-personal build-work \ + update update-nixpkgs gc gc-old \ + fmt lint help + +# ============================================================================ +# TESTING/VALIDATION +# ============================================================================ + +## check: Run nix flake check to validate the flake +check: + nix $(NIX_FLAGS) flake check + +## show: Display the flake outputs +show: + nix $(NIX_FLAGS) flake show + +## dry-run-personal: Dry-run build for personal configuration (shows what would be built) +dry-run-personal: + nix $(NIX_FLAGS) build '.#darwinConfigurations.personal.system' --dry-run + +## dry-run-work: Dry-run build for work configuration (shows what would be built) +dry-run-work: + nix $(NIX_FLAGS) build '.#darwinConfigurations.work.system' --dry-run + +# ============================================================================ +# BUILDING/APPLYING +# ============================================================================ + +## switch-personal: Apply personal configuration with darwin-rebuild (requires sudo) +switch-personal: + nix $(NIX_FLAGS) build '.#darwinConfigurations.personal.system' + sudo ./result/sw/bin/darwin-rebuild activate + +## switch-work: Apply work configuration with darwin-rebuild (requires sudo) +switch-work: + nix $(NIX_FLAGS) build '.#darwinConfigurations.work.system' + sudo ./result/sw/bin/darwin-rebuild activate + +## build-personal: Build personal configuration without switching +build-personal: + nix $(NIX_FLAGS) build '.#darwinConfigurations.personal.system' + +## build-work: Build work configuration without switching +build-work: + nix $(NIX_FLAGS) build '.#darwinConfigurations.work.system' + +# ============================================================================ +# MAINTENANCE +# ============================================================================ + +## update: Update all flake inputs +update: + nix $(NIX_FLAGS) flake update + +## update-nixpkgs: Update only nixpkgs input +update-nixpkgs: + nix $(NIX_FLAGS) flake lock --update-input nixpkgs + +## gc: Run garbage collection to free disk space +gc: + nix-collect-garbage + +## gc-old: Delete old generations and run garbage collection +gc-old: + nix-collect-garbage -d + +# ============================================================================ +# DEVELOPMENT +# ============================================================================ + +## fmt: Format all Nix files using nixfmt +fmt: + find . -name '*.nix' -exec nixfmt {} \; + +## lint: Run statix linter on Nix files (if available) +lint: + @command -v statix >/dev/null 2>&1 && statix check . || echo "statix not installed, skipping lint" + +# ============================================================================ +# HELP +# ============================================================================ + +## help: Show this help message +help: + @echo "Nix Home Manager Configuration - Available Targets" + @echo "==================================================" + @echo "" + @echo "Testing/Validation:" + @echo " check - Run nix flake check to validate the flake" + @echo " show - Display the flake outputs" + @echo " dry-run-personal - Dry-run build for personal configuration" + @echo " dry-run-work - Dry-run build for work configuration" + @echo "" + @echo "Building/Applying:" + @echo " switch-personal - Apply personal configuration with darwin-rebuild (requires sudo)" + @echo " switch-work - Apply work configuration with darwin-rebuild (requires sudo)" + @echo " build-personal - Build personal configuration without switching" + @echo " build-work - Build work configuration without switching" + @echo "" + @echo "Maintenance:" + @echo " update - Update all flake inputs" + @echo " update-nixpkgs - Update only nixpkgs input" + @echo " gc - Run garbage collection to free disk space" + @echo " gc-old - Delete old generations and run garbage collection" + @echo "" + @echo "Development:" + @echo " fmt - Format all Nix files using nixfmt" + @echo " lint - Run statix linter on Nix files" + @echo "" + @echo "Help:" + @echo " help - Show this help message" \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..8e0edab --- /dev/null +++ b/README.md @@ -0,0 +1,265 @@ +# Nix Home Manager Dotfiles + +A declarative, reproducible dotfiles configuration using [Nix](https://nixos.org/), [nix-darwin](https://github.com/LnL7/nix-darwin), and [Home Manager](https://github.com/nix-community/home-manager). This setup supports two profiles: **personal** and **work**, allowing different configurations for different machines while sharing common settings. + +## Overview + +This repository manages: + +- **Shell environment**: Zsh with Starship prompt, Atuin history, Zoxide navigation, FZF fuzzy finder +- **Development tools**: Neovim, Tmux, Git with delta, lazygit +- **CLI utilities**: Modern replacements (eza, bat, ripgrep, fd, delta) +- **Container tools**: Docker, Colima, Kubernetes (kubectl, helm, k9s) +- **macOS applications**: Managed via Homebrew casks through nix-darwin +- **SSH configuration**: 1Password SSH agent integration with profile-specific hosts +- **Git configuration**: Profile-specific signing keys and project-based configs + +## Prerequisites + +### 1. Install Nix + +Install Nix with flakes support: + +```bash +# Install Nix (multi-user installation recommended for macOS) +curl -L https://nixos.org/nix/install | sh + +# Restart your shell or source the nix profile +. /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh +``` + +### 2. Enable Flakes + +Create or edit `~/.config/nix/nix.conf`: + +```bash +mkdir -p ~/.config/nix +echo "experimental-features = nix-command flakes" >> ~/.config/nix/nix.conf +``` + +### 3. Install Homebrew (macOS) + +Homebrew is required for GUI applications (casks): + +```bash +/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" +``` + +### 4. Install 1Password + +This configuration uses 1Password for SSH key management. Install 1Password and enable: +- **SSH Agent**: Settings → Developer → SSH Agent +- **CLI Integration**: Settings → Developer → Command-Line Interface (CLI) + +## Quick Start + +### Personal Machine Setup + +```bash +# Clone the repository +git clone ~/.dotfiles +cd ~/.dotfiles + +# Build and activate the personal configuration +make switch-personal +``` + +### Work Machine Setup + +```bash +# Clone the repository +git clone ~/.dotfiles +cd ~/.dotfiles + +# Build and activate the work configuration +make switch-work +``` + +## Makefile Commands + +The project includes a Makefile for convenient operations. Run `make help` to see all available commands. + +### Testing/Validation + +| Command | Description | +|---------|-------------| +| `make check` | Run nix flake check to validate the flake | +| `make show` | Display the flake outputs | +| `make dry-run-personal` | Dry-run build for personal configuration | +| `make dry-run-work` | Dry-run build for work configuration | + +### Building/Applying + +| Command | Description | +|---------|-------------| +| `make switch-personal` | Apply personal configuration with darwin-rebuild | +| `make switch-work` | Apply work configuration with darwin-rebuild | +| `make build-personal` | Build personal configuration without switching | +| `make build-work` | Build work configuration without switching | + +### Maintenance + +| Command | Description | +|---------|-------------| +| `make update` | Update all flake inputs | +| `make update-nixpkgs` | Update only nixpkgs input | +| `make gc` | Run garbage collection to free disk space | +| `make gc-old` | Delete old generations and run garbage collection | + +### Development + +| Command | Description | +|---------|-------------| +| `make fmt` | Format all Nix files using nixfmt | +| `make lint` | Run statix linter on Nix files | + +## Project Structure + +``` +. +├── flake.nix # Main flake entry point +├── flake.lock # Locked dependency versions +├── Makefile # Convenient make targets +├── README.md # This file +│ +├── home/ # Home Manager configurations +│ ├── default.nix # Shared home configuration +│ ├── personal.nix # Personal profile overrides +│ └── work.nix # Work profile overrides +│ +├── hosts/ # Machine-specific nix-darwin configs +│ ├── personal/ +│ │ └── default.nix # Personal machine darwin settings +│ └── work/ +│ └── default.nix # Work machine darwin settings +│ +├── modules/ # Reusable Nix modules +│ ├── darwin/ +│ │ └── homebrew.nix # Homebrew cask management +│ │ +│ └── home/ # Home Manager modules +│ ├── apps.nix # Application configs (aerospace, jellyfin-tui) +│ ├── git.nix # Git configuration +│ ├── git-files.nix # Project-specific git configs +│ ├── packages.nix # CLI packages via Nix +│ ├── shell.nix # Shell environment (zsh, starship, etc.) +│ ├── ssh.nix # SSH configuration +│ └── tmux.nix # Tmux configuration +│ +└── docs/ # Documentation + ├── migration-analysis.md # Original migration analysis + ├── nix-architecture.md # Architecture design + └── usage.md # Detailed usage guide +``` + +## Customization + +### Adding New Packages + +#### Via Nix (Preferred for CLI tools) + +Edit [`modules/home/packages.nix`](modules/home/packages.nix:13): + +```nix +home.packages = with pkgs; [ + # Add your package here + your-package +]; +``` + +#### Via Homebrew (For GUI apps/casks) + +Edit [`modules/darwin/homebrew.nix`](modules/darwin/homebrew.nix:21): + +```nix +casks = { + shared = [ + # Add casks for all machines + "your-app" + ]; + personal = [ + # Add casks for personal machine only + "personal-only-app" + ]; +}; +``` + +### Modifying Shell Configuration + +Edit [`modules/home/shell.nix`](modules/home/shell.nix:1) to customize: +- Shell aliases +- Environment variables +- Zsh settings +- Starship prompt +- Atuin history settings + +### Modifying Git Configuration + +Edit [`modules/home/git.nix`](modules/home/git.nix:1) for global settings, or the profile files: +- [`home/personal.nix`](home/personal.nix:20) - Personal git settings +- [`home/work.nix`](home/work.nix:23) - Work git settings + +### Adding SSH Hosts + +Edit [`modules/home/ssh.nix`](modules/home/ssh.nix:83) to add new SSH host configurations. + +## Profile Differences + +| Feature | Personal | Work | +|---------|----------|------| +| **Git Email** | Personal email | Work email | +| **Git Signing Key** | Personal SSH key | Work SSH key | +| **SSH Hosts** | github-private, gitea | github-private, github-zeronorth, coder | +| **Homebrew Casks** | All + personal apps | Shared apps only | +| **Personal Apps** | darktable, signal, proton-*, steam | - | +| **Jellyfin TUI** | Enabled | Disabled | + +## Troubleshooting + +### Build Errors + +```bash +# Check flake validity +make check + +# Build with verbose output +darwin-rebuild switch --flake .#personal --show-trace +``` + +### Rollback + +```bash +# List generations +darwin-rebuild --list-generations + +# Rollback to previous +darwin-rebuild --rollback +``` + +### Clean Rebuild + +```bash +# Remove result symlink and rebuild +rm -f result +make gc +make switch-personal +``` + +### Homebrew Issues + +```bash +# If Homebrew packages fail to install +brew update +brew doctor +make switch-personal +``` + +## Documentation + +- [`docs/usage.md`](docs/usage.md) - Detailed usage guide +- [`docs/nix-architecture.md`](docs/nix-architecture.md) - Architecture design and module documentation +- [`docs/migration-analysis.md`](docs/migration-analysis.md) - Original migration analysis + +## License + +This is a personal dotfiles repository. Feel free to use it as inspiration for your own configuration. \ No newline at end of file diff --git a/docs/migration-analysis.md b/docs/migration-analysis.md new file mode 100644 index 0000000..cab4695 --- /dev/null +++ b/docs/migration-analysis.md @@ -0,0 +1,486 @@ +# Chezmoi to Nix Home Manager Migration Analysis + +## Executive Summary + +This document analyzes the current chezmoi-based dotfiles configuration to prepare for migration to Nix Home Manager with flakes. The configuration manages a macOS development environment with two distinct profiles: **personal** and **work** (ZeroNorth). + +--- + +## 1. Managed Dotfiles Overview + +### 1.1 File Structure + +| Chezmoi Path | Target Path | Type | Templated | +|--------------|-------------|------|-----------| +| `dot_zshrc` | `~/.zshrc` | Shell config | No | +| `dot_tmux.conf` | `~/.tmux.conf` | Tmux config | No | +| `dot_gitconfig.tmpl` | `~/.gitconfig` | Git config | **Yes** | +| `dot_gitignore_global` | `~/.gitignore_global` | Git ignore | No | +| `dot_Brewfile.tmpl` | `~/.Brewfile` | Homebrew packages | **Yes** | +| `dot_aerospace.toml` | `~/.aerospace.toml` | Window manager | No | +| `dot_config/atuin/config.toml` | `~/.config/atuin/config.toml` | Shell history | No | +| `dot_shellrc/rc.d/*` | `~/.shellrc/rc.d/*` | Shell scripts | No | +| `dot_ssh/config` | `~/.ssh/config` | SSH config | No | +| `dot_ssh/config.d/*` | `~/.ssh/config.d/*` | SSH host configs | No | +| `dot_ssh/private_keys/*` | `~/.ssh/keys/*` | SSH public keys | **Partial** | +| `private_Library/...` | `~/Library/...` | App configs | No | +| `private_Projects/*` | `~/Projects/*` | Project configs | No | + +### 1.2 External Dependencies + +From [`.chezmoiexternal.toml`](.chezmoiexternal.toml:1): + +```toml +[".ssh/authorized_keys"] + type = "file" + url = "https://github.com/morten-olsen.keys" + refreshPeriod = "168h" +``` + +**Migration consideration:** This fetches SSH authorized keys from GitHub. In Nix, this could be handled by: +- A custom derivation that fetches the keys at build time +- A systemd service/launchd agent that periodically updates the file +- Manual management with `home.file` and periodic updates + +--- + +## 2. Personal vs Work Differentiation + +### 2.1 Template Variable: `.work` + +The configuration uses a boolean `.work` template variable to differentiate between personal and work environments. + +### 2.2 Conditional Logic Summary + +#### `.chezmoiignore` - File Exclusions + +From [`.chezmoiignore`](.chezmoiignore:2): + +| Condition | Ignored Files | +|-----------|---------------| +| `work = false` (Personal) | `Projects/private`, `Projects/zeronorth` | +| `work = true` (Work) | `Projects/.gitconfig` | + +**Interpretation:** +- **Personal machines:** Don't deploy work-related project configs +- **Work machines:** Don't deploy the personal Projects/.gitconfig, use project-specific ones instead + +#### `dot_Brewfile.tmpl` - Package Differences + +From [`dot_Brewfile.tmpl`](dot_Brewfile.tmpl:66): + +| Environment | Additional Packages | +|-------------|---------------------| +| Personal only (`work != true`) | `darktable`, `signal`, `proton-mail-bridge`, `proton-pass`, `protonvpn`, `steam` | +| Work | (none additional) | + +#### `dot_gitconfig.tmpl` - Git Configuration + +From [`dot_gitconfig.tmpl`](dot_gitconfig.tmpl:45): + +| Environment | Git Include Configuration | +|-------------|---------------------------| +| Personal (`work = false`) | Single includeIf for `~/Projects/` → `~/Projects/.gitconfig` | +| Work (`work = true`) | Two includeIfs: `~/Projects/private/` and `~/Projects/zeronorth/` with separate configs | + +#### `dot_ssh/private_keys/private_github-private.pub.tmpl` - SSH Keys + +From [`dot_ssh/private_keys/private_github-private.pub.tmpl`](dot_ssh/private_keys/private_github-private.pub.tmpl:1): + +| Environment | SSH Key | +|-------------|---------| +| Personal | `ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFaIAP/ZJ7+7jeR44e1yIJjfQAB6MN351LDKJAXVF62P` | +| Work | `ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILAzuPy7D/54GxMq9Zhz0CUjaDnEQ6RkQ/yqVYl7U55k` | + +--- + +## 3. Detailed Configuration Analysis + +### 3.1 Shell Configuration + +#### Zsh ([`dot_zshrc`](dot_zshrc:1)) + +- Sets `XDG_RUNTIME_DIR` and `LANG` +- Sources scripts from `~/.shellrc/rc.d/*.sh` and `~/.shellrc/zshrc.d/*.zsh` +- Configures GPG TTY for SSH sessions +- Displays ASCII art welcome banner +- Configures FZF with Catppuccin color scheme +- Initializes: `atuin`, `direnv`, `starship`, `zoxide`, `pyenv` +- Adds Rust/Cargo to PATH + +#### Shell RC Scripts + +| File | Purpose | +|------|---------| +| [`01-env.sh`](dot_shellrc/rc.d/01-env.sh:1) | PATH setup, aliases (ls→eza, cat→bat, grep→rg, diff→delta, less→bat) | +| [`01-nvim.sh`](dot_shellrc/rc.d/01-nvim.sh:1) | Sets EDITOR=nvim, alias vim=nvim | +| [`05-nvm.sh`](dot_shellrc/rc.d/05-nvm.sh:1) | NVM initialization | + +### 3.2 Git Configuration + +#### Global Config ([`dot_gitconfig.tmpl`](dot_gitconfig.tmpl:1)) + +- **Aliases:** graph, ll, st, cm, append, submodules, df, last, br, brr, undo, unstage +- **Core:** Uses delta as pager, disables hooks, references global gitignore +- **Pull:** Fast-forward only +- **Push:** Auto setup remote +- **LFS:** Configured +- **Difftool:** nvim diff + +#### Project-Specific Configs + +| Config | Email | Signing Key | URL Rewrites | +|--------|-------|-------------|--------------| +| [`Projects/.gitconfig`](private_Projects/dot_gitconfig:1) (Personal) | `fbtijfdq@void.black` | Personal key | github-private, gitea.olsen.cloud | +| [`Projects/private/.gitconfig`](private_Projects/private_private/dot_gitconfig:1) (Work) | `fbtijfdq@void.black` | Work key | github-private, gitea.olsen.cloud | +| [`Projects/zeronorth/.gitconfig`](private_Projects/private_zeronorth/dot_gitconfig:1) (Work) | `morten.olsen@zeronorth.com` | ZeroNorth key | github-zeronorth | + +### 3.3 SSH Configuration + +#### Main Config ([`dot_ssh/config`](dot_ssh/config:1)) + +- Includes colima SSH config +- Includes all files from `~/.ssh/config.d/` +- Global settings: ControlMaster, 1Password SSH agent, ForwardAgent + +#### Host Configurations + +| Host | Hostname | Port | Identity | +|------|----------|------|----------| +| [`github.com`](dot_ssh/config.d/github:1) | ssh.github.com | 443 | Default | +| [`github-private`](dot_ssh/config.d/git-private:1) | ssh.github.com | 443 | github-private.pub | +| [`github-zeronorth`](dot_ssh/config.d/git-zeronorth:1) | ssh.github.com | 443 | github-zeronorth.pub | +| [`gitea-ssh.olsen.cloud`](dot_ssh/config.d/git-private:8) | gitea-ssh.olsen.cloud | 2202 | github-private.pub | +| [`coder.*`](dot_ssh/config.d/git-coder:7) | (proxy) | - | Coder CLI | + +### 3.4 Tmux Configuration ([`dot_tmux.conf`](dot_tmux.conf:1)) + +- Mouse enabled +- 256-color terminal support +- Vim-style keybindings (hjkl navigation) +- TPM (Tmux Plugin Manager) with auto-install +- Plugins: tmux-power, tmux-yank, tmux-sensible +- Vim-tmux navigator integration +- Custom bindings for splits, copy mode, lazygit popup + +### 3.5 Aerospace Window Manager ([`dot_aerospace.toml`](dot_aerospace.toml:1)) + +- Starts at login +- Tiling layout with gaps +- QWERTY key mapping +- Workspace navigation (alt+1-6) +- Window movement (cmd+shift+hjkl) +- Service mode for layout management +- Floating layout for Elgato apps + +### 3.6 Application Configs + +#### Atuin ([`dot_config/atuin/config.toml`](dot_config/atuin/config.toml:1)) + +```toml +style = "compact" +keymap_mode = "vim-normal" +``` + +#### Jellyfin TUI ([`private_Library/private_Application Support/jellyfin-tui/private_config.yaml`](private_Library/private_Application Support/jellyfin-tui/private_config.yaml:1)) + +- Server: jellyfin.olsen.cloud +- Username: morten +- Password stored in separate file + +### 3.7 Environment Variables (Work) + +From [`Projects/zeronorth/.envrc`](private_Projects/private_zeronorth/dot_envrc:1): + +```bash +export NODE_AUTH_TOKEN="op://Employee/Github NPM Token/password" +export NPM_TOKEN="op://Employee/Github NPM Token/password" +export NPM_GITHUB_TOKEN="op://Employee/Github NPM Token/password" +export AWS_PROFILE=zntest +``` + +Uses 1Password CLI references for secrets. + +--- + +## 4. Homebrew Packages + +### 4.1 Taps + +- `coder/coder` +- `felixkratz/formulae` +- `fluxcd/tap` +- `nikitabobko/tap` +- `sst/tap` + +### 4.2 Formulae (All Environments) + +| Category | Packages | +|----------|----------| +| **Languages/Runtimes** | python@3.13, deno, rustup, pyenv, uv | +| **Shell Tools** | zsh, atuin, starship, fzf, zoxide, direnv | +| **File Utils** | bat, eza, fd, ripgrep, rsync, unzip | +| **Git** | git, gh, git-delta, jj | +| **Containers/K8s** | docker, docker-buildx, docker-compose, colima, kubernetes-cli, helm, helmfile, k9s, istioctl, flux | +| **Dev Tools** | neovim, tmux, jq, graphviz, terraform, ansible, sshpass | +| **Media** | ffmpeg | +| **Security** | gnupg | +| **Other** | curl, watch, coder, opencode, tree-sitter-cli | + +### 4.3 Casks (All Environments) + +- **Terminal:** ghostty +- **Productivity:** 1password, 1password-cli, raycast, obsidian +- **Development:** dbeaver-community, lens +- **Media:** jellyfin-media-player, ollama-app +- **Window Management:** aerospace +- **Networking:** localsend, mqtt-explorer +- **Home Automation:** home-assistant + +### 4.4 Casks (Personal Only) + +- darktable (photo editing) +- signal (messaging) +- proton-mail-bridge, proton-pass, protonvpn (Proton suite) +- steam (gaming) + +--- + +## 5. Recommended Nix Home Manager Structure + +### 5.1 Flake Structure + +``` +flake.nix +├── flake.lock +├── home/ +│ ├── default.nix # Common configuration +│ ├── personal.nix # Personal-specific config +│ ├── work.nix # Work-specific config +│ └── modules/ +│ ├── shell/ +│ │ ├── zsh.nix +│ │ ├── starship.nix +│ │ ├── atuin.nix +│ │ └── aliases.nix +│ ├── git/ +│ │ ├── default.nix +│ │ ├── personal.nix +│ │ └── work.nix +│ ├── ssh/ +│ │ ├── default.nix +│ │ └── hosts.nix +│ ├── tmux.nix +│ ├── aerospace.nix +│ ├── neovim.nix +│ └── packages.nix +└── docs/ + └── migration-analysis.md +``` + +### 5.2 Configuration Approach + +```nix +# flake.nix (simplified) +{ + inputs = { + nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; + home-manager = { + url = "github:nix-community/home-manager"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + # Darwin support for macOS + nix-darwin = { + url = "github:LnL7/nix-darwin"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; + + outputs = { self, nixpkgs, home-manager, nix-darwin, ... }: { + homeConfigurations = { + "personal" = home-manager.lib.homeManagerConfiguration { + # Personal machine config + modules = [ ./home/default.nix ./home/personal.nix ]; + }; + "work" = home-manager.lib.homeManagerConfiguration { + # Work machine config + modules = [ ./home/default.nix ./home/work.nix ]; + }; + }; + }; +} +``` + +### 5.3 Module Mapping + +| Chezmoi File | Nix Module | Home Manager Option | +|--------------|------------|---------------------| +| `dot_zshrc` | `shell/zsh.nix` | `programs.zsh` | +| `dot_tmux.conf` | `tmux.nix` | `programs.tmux` | +| `dot_gitconfig.tmpl` | `git/default.nix` | `programs.git` | +| `dot_gitignore_global` | `git/default.nix` | `programs.git.ignores` | +| `dot_Brewfile.tmpl` | `packages.nix` | `home.packages` + homebrew module | +| `dot_aerospace.toml` | `aerospace.nix` | `home.file` | +| `dot_config/atuin/*` | `shell/atuin.nix` | `programs.atuin` | +| `dot_ssh/*` | `ssh/default.nix` | `programs.ssh` | + +--- + +## 6. Migration Challenges and Considerations + +### 6.1 Homebrew Integration + +**Challenge:** Many casks are macOS-specific and not available in nixpkgs. + +**Solutions:** +1. Use `nix-darwin` with homebrew module for casks +2. Keep a minimal Brewfile for casks only +3. Use `home.packages` for CLI tools available in nixpkgs + +**Packages requiring Homebrew:** +- All casks (1password, ghostty, aerospace, etc.) +- Some taps (coder, flux, opencode) + +### 6.2 1Password SSH Agent Integration + +**Challenge:** SSH config references 1Password agent socket. + +**Solution:** Use conditional paths or environment-specific SSH config: +```nix +programs.ssh = { + extraConfig = '' + IdentityAgent "~/Library/Group Containers/2BUA8C4S2C.com.1password/t/agent.sock" + ''; +}; +``` + +### 6.3 Tmux Plugin Manager (TPM) + +**Challenge:** TPM auto-installs from git, which doesn't fit Nix's declarative model. + +**Solutions:** +1. Use `programs.tmux.plugins` with nixpkgs tmux plugins +2. Package custom plugins as Nix derivations +3. Keep TPM but manage it outside Nix (hybrid approach) + +### 6.4 NVM (Node Version Manager) + +**Challenge:** NVM is imperative and conflicts with Nix's approach. + +**Solutions:** +1. Use `programs.nodejs` with specific version +2. Use `nix-shell` or `direnv` with `use nix` for project-specific Node versions +3. Use `devenv` or `flake.nix` per-project + +### 6.5 External Key Fetching + +**Challenge:** `.chezmoiexternal.toml` fetches SSH keys from GitHub. + +**Solutions:** +1. Create a derivation that fetches at build time (keys become stale) +2. Use a launchd agent to periodically update +3. Manual management with documentation + +### 6.6 Private/Sensitive Files + +**Challenge:** Files prefixed with `private_` contain sensitive data. + +**Solutions:** +1. Use `sops-nix` for encrypted secrets +2. Use `agenix` for age-encrypted secrets +3. Keep sensitive files outside Nix, managed separately +4. Use 1Password CLI references (already used in `.envrc`) + +### 6.7 Project-Specific Git Configs + +**Challenge:** Multiple git configs for different project directories. + +**Solution:** Use `programs.git.includes`: +```nix +programs.git = { + includes = [ + { + condition = "gitdir:~/Projects/zeronorth/"; + path = "~/Projects/zeronorth/.gitconfig"; + } + ]; +}; +``` + +### 6.8 macOS-Specific Paths + +**Challenge:** Hardcoded paths like `/opt/homebrew/bin`, `/Applications/`. + +**Solution:** Use Nix variables and conditionals: +```nix +home.sessionPath = [ "/opt/homebrew/bin" "$HOME/.local/bin" ]; +``` + +--- + +## 7. Migration Phases + +### Phase 1: Foundation +1. Set up flake structure with nix-darwin and home-manager +2. Create base configuration with common packages +3. Implement personal/work profile switching + +### Phase 2: Shell Environment +1. Migrate zsh configuration +2. Set up starship, atuin, zoxide, direnv +3. Configure shell aliases and environment variables + +### Phase 3: Development Tools +1. Migrate git configuration with conditional includes +2. Set up SSH configuration +3. Configure tmux (decide on TPM approach) +4. Set up neovim (if managed by chezmoi elsewhere) + +### Phase 4: Applications +1. Configure aerospace (via home.file) +2. Set up application configs (atuin, jellyfin-tui) +3. Handle Homebrew casks via nix-darwin + +### Phase 5: Secrets and Sensitive Data +1. Implement sops-nix or agenix for secrets +2. Migrate SSH keys +3. Handle 1Password integrations + +### Phase 6: Testing and Validation +1. Test on personal machine +2. Test on work machine +3. Document any manual steps required + +--- + +## 8. Summary + +### Key Findings + +1. **Two distinct profiles:** Personal and Work, differentiated by `.work` boolean +2. **Template usage:** Limited to Brewfile, gitconfig, SSH keys, and ignore rules +3. **External dependencies:** SSH authorized keys fetched from GitHub +4. **macOS-centric:** Heavy use of Homebrew casks, 1Password, aerospace +5. **Secrets:** 1Password CLI references used for work environment + +### Recommended Approach + +1. **Use nix-darwin** for macOS system configuration and Homebrew cask management +2. **Use home-manager** for user-level dotfiles and packages +3. **Implement profiles** using Nix modules with `mkIf` conditionals +4. **Keep Homebrew** for casks that aren't available in nixpkgs +5. **Use sops-nix** for managing sensitive configuration +6. **Gradual migration** - start with shell and git, then expand + +### Files to Create + +``` +flake.nix # Main flake with darwin and home-manager +home/default.nix # Common home-manager config +home/personal.nix # Personal-specific settings +home/work.nix # Work-specific settings +home/modules/shell/zsh.nix # Zsh configuration +home/modules/git/default.nix # Git configuration +home/modules/ssh/default.nix # SSH configuration +home/modules/tmux.nix # Tmux configuration +home/modules/packages.nix # Package declarations \ No newline at end of file diff --git a/docs/nix-architecture.md b/docs/nix-architecture.md new file mode 100644 index 0000000..b22e21e --- /dev/null +++ b/docs/nix-architecture.md @@ -0,0 +1,1288 @@ + +# Nix Home Manager Flake Architecture + +## Overview + +This document defines the architecture for migrating from chezmoi to Nix Home Manager with flakes. The design supports two target environments (personal and work) while maintaining a clean, modular, and extensible structure. + +--- + +## 1. Directory Structure + +``` +. +├── flake.nix # Main flake entry point +├── flake.lock # Locked dependencies +├── README.md # Setup and usage documentation +│ +├── hosts/ # Machine-specific configurations +│ ├── personal/ +│ │ └── default.nix # Personal machine darwin config +│ └── work/ +│ └── default.nix # Work machine darwin config +│ +├── home/ # Home Manager configurations +│ ├── default.nix # Shared home configuration +│ ├── personal.nix # Personal profile overrides +│ └── work.nix # Work profile overrides +│ +├── modules/ # Reusable Nix modules +│ ├── darwin/ # macOS-specific modules +│ │ ├── default.nix # Common darwin settings +│ │ ├── homebrew.nix # Homebrew cask management +│ │ └── system.nix # System preferences +│ │ +│ └── home/ # Home Manager modules +│ ├── shell/ +│ │ ├── default.nix # Shell module entry point +│ │ ├── zsh.nix # Zsh configuration +│ │ ├── starship.nix # Starship prompt +│ │ ├── atuin.nix # Shell history +│ │ ├── direnv.nix # Directory environments +│ │ └── aliases.nix # Shell aliases +│ │ +│ ├── git/ +│ │ ├── default.nix # Git module entry point +│ │ ├── config.nix # Core git configuration +│ │ └── identities.nix # Git identity management +│ │ +│ ├── ssh/ +│ │ ├── default.nix # SSH module entry point +│ │ └── hosts.nix # SSH host configurations +│ │ +│ ├── terminal/ +│ │ ├── default.nix # Terminal module entry point +│ │ ├── tmux.nix # Tmux configuration +│ │ └── neovim.nix # Neovim configuration +│ │ +│ ├── packages/ +│ │ ├── default.nix # Package module entry point +│ │ ├── cli.nix # CLI tools +│ │ ├── development.nix # Development tools +│ │ └── kubernetes.nix # K8s tools +│ │ +│ └── apps/ +│ ├── default.nix # Apps module entry point +│ └── aerospace.nix # Window manager config +│ +├── lib/ # Helper functions +│ └── default.nix # Utility functions +│ +├── overlays/ # Nixpkgs overlays +│ └── default.nix # Custom package overlays +│ +├── secrets/ # Encrypted secrets (sops-nix) +│ ├── secrets.yaml # Encrypted secrets file +│ └── .sops.yaml # SOPS configuration +│ +└── docs/ # Documentation + ├── migration-analysis.md # Migration analysis + └── nix-architecture.md # This document +``` + +### Design Rationale + +1. **`hosts/`** - Machine-level configurations using nix-darwin. Each host can have unique system settings while sharing common darwin modules. + +2. **`home/`** - Profile-specific home-manager configurations. The [`default.nix`](home/default.nix) contains shared settings, while [`personal.nix`](home/personal.nix) and [`work.nix`](home/work.nix) contain profile-specific overrides. + +3. **`modules/`** - Reusable, composable modules organized by functionality. Split into `darwin/` (system-level) and `home/` (user-level) to maintain clear separation. + +4. **`lib/`** - Helper functions for reducing boilerplate and improving maintainability. + +5. **`overlays/`** - Custom package modifications or additions to nixpkgs. + +6. **`secrets/`** - Encrypted secrets managed by sops-nix for sensitive data like API tokens. + +--- + +## 2. Flake Structure + +### 2.1 Inputs + +```nix +{ + inputs = { + # Core dependencies + nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; + + # Home Manager for user environment + home-manager = { + url = "github:nix-community/home-manager"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + + # nix-darwin for macOS system configuration + nix-darwin = { + url = "github:LnL7/nix-darwin"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + + # Secrets management + sops-nix = { + url = "github:Mic92/sops-nix"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + + # Optional: Homebrew management through nix-darwin + nix-homebrew = { + url = "github:zhaofengli-wip/nix-homebrew"; + }; + }; +} +``` + +### 2.2 Outputs Structure + +```nix +{ + outputs = { self, nixpkgs, home-manager, nix-darwin, sops-nix, nix-homebrew, ... }@inputs: + let + # Supported systems (macOS for now, Linux can be added later) + systems = [ "aarch64-darwin" "x86_64-darwin" ]; + + # Helper to generate per-system attributes + forAllSystems = nixpkgs.lib.genAttrs systems; + + # Common special args passed to all modules + specialArgs = { inherit inputs; }; + in + { + # Darwin (macOS) system configurations + darwinConfigurations = { + # Personal machine configuration + "personal" = nix-darwin.lib.darwinSystem { + system = "aarch64-darwin"; + inherit specialArgs; + modules = [ + ./hosts/personal + ./modules/darwin + home-manager.darwinModules.home-manager + { + home-manager = { + useGlobalPkgs = true; + useUserPackages = true; + extraSpecialArgs = specialArgs; + users.alice = { ... }: { + imports = [ + ./home + ./home/personal.nix + ]; + }; + }; + } + ]; + }; + + # Work machine configuration + "work" = nix-darwin.lib.darwinSystem { + system = "aarch64-darwin"; + inherit specialArgs; + modules = [ + ./hosts/work + ./modules/darwin + home-manager.darwinModules.home-manager + { + home-manager = { + useGlobalPkgs = true; + useUserPackages = true; + extraSpecialArgs = specialArgs; + users.alice = { ... }: { + imports = [ + ./home + ./home/work.nix + ]; + }; + }; + } + ]; + }; + }; + + # Standalone Home Manager configurations (for non-darwin systems) + homeConfigurations = { + "personal@linux" = home-manager.lib.homeManagerConfiguration { + pkgs = nixpkgs.legacyPackages.x86_64-linux; + extraSpecialArgs = specialArgs; + modules = [ + ./home + ./home/personal.nix + ]; + }; + + "work@linux" = home-manager.lib.homeManagerConfiguration { + pkgs = nixpkgs.legacyPackages.x86_64-linux; + extraSpecialArgs = specialArgs; + modules = [ + ./home + ./home/work.nix + ]; + }; + }; + + # Development shells for this repository + devShells = forAllSystems (system: + let pkgs = nixpkgs.legacyPackages.${system}; + in { + default = pkgs.mkShell { + packages = with pkgs; [ nixfmt sops age ]; + }; + } + ); + }; +} +``` + +### 2.3 Key Design Decisions + +1. **nix-darwin as primary entry point** - On macOS, use `darwinConfigurations` which integrates home-manager as a module. This allows managing both system-level settings (Homebrew, system preferences) and user-level dotfiles in one command. + +2. **Standalone homeConfigurations** - Provided for future Linux support or cases where nix-darwin isn't desired. + +3. **`useGlobalPkgs` and `useUserPackages`** - Ensures home-manager uses the same nixpkgs instance as darwin, avoiding duplicate package downloads. + +4. **`specialArgs`** - Passes inputs to all modules, enabling access to flake inputs anywhere in the configuration. + +--- + +## 3. Module Design + +### 3.1 Module Architecture Diagram + +```mermaid +graph TB + subgraph Flake + F[flake.nix] + end + + subgraph Darwin Configs + DP[hosts/personal] + DW[hosts/work] + end + + subgraph Darwin Modules + DM[modules/darwin/default.nix] + HB[modules/darwin/homebrew.nix] + SYS[modules/darwin/system.nix] + end + + subgraph Home Configs + HC[home/default.nix] + HP[home/personal.nix] + HW[home/work.nix] + end + + subgraph Home Modules + SH[modules/home/shell] + GIT[modules/home/git] + SSH[modules/home/ssh] + TM[modules/home/terminal] + PKG[modules/home/packages] + APP[modules/home/apps] + end + + F --> DP + F --> DW + DP --> DM + DW --> DM + DM --> HB + DM --> SYS + + DP --> HC + DW --> HC + DP --> HP + DW --> HW + + HC --> SH + HC --> GIT + HC --> SSH + HC --> TM + HC --> PKG + HC --> APP +``` + +### 3.2 Shared vs Profile-Specific Content + +| Component | Shared | Personal-Specific | Work-Specific | +|-----------|--------|-------------------|---------------| +| **Shell** | zsh, starship, atuin, aliases | - | - | +| **Git** | Core config, aliases, delta | Personal signing key, personal email | Work signing key, work email, zeronorth config | +| **SSH** | 1Password agent, control master | github-private host | github-zeronorth host | +| **Packages** | CLI tools, dev tools, k8s | Personal apps (darktable, proton, steam) | - | +| **Homebrew Casks** | 1password, ghostty, raycast | signal, proton-*, steam | - | +| **Project Configs** | - | ~/Projects/.gitconfig | ~/Projects/private/.gitconfig, ~/Projects/zeronorth/.gitconfig | + +### 3.3 Module Option Pattern + +Each module should follow this pattern for configurability: + +```nix +# modules/home/git/default.nix +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.modules.git; +in +{ + options.modules.git = { + enable = mkEnableOption "Git configuration"; + + userName = mkOption { + type = types.str; + default = "Morten Olsen"; + description = "Git user name"; + }; + + userEmail = mkOption { + type = types.str; + description = "Git user email"; + }; + + signing = { + enable = mkEnableOption "Git commit signing"; + + key = mkOption { + type = types.str; + description = "SSH signing key"; + }; + }; + + includes = mkOption { + type = types.listOf (types.submodule { + options = { + condition = mkOption { type = types.str; }; + path = mkOption { type = types.str; }; + }; + }); + default = []; + description = "Conditional git config includes"; + }; + }; + + config = mkIf cfg.enable { + programs.git = { + enable = true; + userName = cfg.userName; + userEmail = cfg.userEmail; + + signing = mkIf cfg.signing.enable { + key = cfg.signing.key; + signByDefault = true; + }; + + extraConfig = { + gpg.format = "ssh"; + gpg.ssh.program = "/Applications/1Password.app/Contents/MacOS/op-ssh-sign"; + # ... other shared config + }; + + includes = cfg.includes; + }; + }; +} +``` + +### 3.4 Module Implementations + +#### Shell Module ([`modules/home/shell/default.nix`](modules/home/shell/default.nix)) + +```nix +{ config, lib, pkgs, ... }: + +{ + imports = [ + ./zsh.nix + ./starship.nix + ./atuin.nix + ./direnv.nix + ./aliases.nix + ]; + + options.modules.shell = { + enable = lib.mkEnableOption "Shell configuration"; + }; + + config = lib.mkIf config.modules.shell.enable { + # Enable all shell sub-modules + modules.shell.zsh.enable = true; + modules.shell.starship.enable = true; + modules.shell.atuin.enable = true; + modules.shell.direnv.enable = true; + }; +} +``` + +#### Git Module ([`modules/home/git/default.nix`](modules/home/git/default.nix)) + +```nix +{ config, lib, pkgs, ... }: + +{ + imports = [ + ./config.nix + ./identities.nix + ]; + + options.modules.git = { + enable = lib.mkEnableOption "Git configuration"; + + profile = lib.mkOption { + type = lib.types.enum [ "personal" "work" ]; + description = "Git profile to use"; + }; + }; +} +``` + +#### SSH Module ([`modules/home/ssh/default.nix`](modules/home/ssh/default.nix)) + +```nix +{ config, lib, pkgs, ... }: + +{ + imports = [ ./hosts.nix ]; + + options.modules.ssh = { + enable = lib.mkEnableOption "SSH configuration"; + + onePasswordAgent = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Use 1Password SSH agent"; + }; + }; + + config = lib.mkIf config.modules.ssh.enable { + programs.ssh = { + enable = true; + controlMaster = "auto"; + controlPath = "/tmp/ssh-%r@%h:%p"; + forwardAgent = true; + + extraConfig = lib.mkIf config.modules.ssh.onePasswordAgent '' + IdentityAgent "~/Library/Group Containers/2BUA8C4S2C.com.1password/t/agent.sock" + ''; + + includes = [ "~/.colima/ssh_config" ]; + }; + }; +} +``` + +#### Homebrew Module ([`modules/darwin/homebrew.nix`](modules/darwin/homebrew.nix)) + +```nix +{ config, lib, pkgs, ... }: + +let + cfg = config.modules.homebrew; +in +{ + options.modules.homebrew = { + enable = lib.mkEnableOption "Homebrew management"; + + personalCasks = lib.mkOption { + type = lib.types.bool; + default = false; + description = "Include personal-only casks"; + }; + }; + + config = lib.mkIf cfg.enable { + homebrew = { + enable = true; + onActivation = { + autoUpdate = true; + cleanup = "zap"; + upgrade = true; + }; + + taps = [ + "nikitabobko/tap" + "coder/coder" + "fluxcd/tap" + "sst/tap" + ]; + + casks = [ + "1password" + "1password-cli" + "aerospace" + "dbeaver-community" + "ghostty" + "home-assistant" + "jellyfin-media-player" + "lens" + "localsend" + "mqtt-explorer" + "obsidian" + "ollama-app" + "raycast" + ] ++ lib.optionals cfg.personalCasks [ + "darktable" + "signal" + "proton-mail-bridge" + "proton-pass" + "protonvpn" + "steam" + ]; + }; + }; +} +``` + +### 3.5 Secrets Management + +For sensitive data, use sops-nix with age encryption: + +```nix +# secrets/.sops.yaml +keys: + - &personal age1... # Personal machine key + - &work age1... # Work machine key + +creation_rules: + - path_regex: secrets/personal\.yaml$ + key_groups: + - age: + - *personal + - path_regex: secrets/work\.yaml$ + key_groups: + - age: + - *work + - path_regex: secrets/secrets\.yaml$ + key_groups: + - age: + - *personal + - *work +``` + +For 1Password integration (already used in current setup), reference secrets directly: + +```nix +# In modules that need secrets +home.sessionVariables = { + NODE_AUTH_TOKEN = "op://Employee/Github NPM Token/password"; +}; +``` + +--- + +## 4. Configuration Profiles + +### 4.1 Shared Configuration ([`home/default.nix`](home/default.nix)) + +```nix +{ config, lib, pkgs, ... }: + +{ + imports = [ + ../modules/home/shell + ../modules/home/git + ../modules/home/ssh + ../modules/home/terminal + ../modules/home/packages + ../modules/home/apps + ]; + + home = { + stateVersion = "24.05"; + + sessionVariables = { + EDITOR = "nvim"; + LANG = "en_US.UTF-8"; + }; + + sessionPath = [ + "$HOME/.local/bin" + "$HOME/.cargo/bin" + "/opt/homebrew/bin" + ]; + }; + + # Enable shared modules + modules = { + shell.enable = true; + git.enable = true; + ssh.enable = true; + terminal.enable = true; + packages.enable = true; + apps.enable = true; + }; +} +``` + +### 4.2 Personal Profile ([`home/personal.nix`](home/personal.nix)) + +```nix +{ config, lib, pkgs, ... }: + +{ + # Git configuration for personal + modules.git = { + profile = "personal"; + userEmail = "fbtijfdq@void.black"; + signing.key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFaIAP/ZJ7+7jeR44e1yIJjfQAB6MN351LDKJAXVF62P"; + + includes = [ + { + condition = "gitdir:~/Projects/"; + path = "~/Projects/.gitconfig"; + } + ]; + }; + + # SSH hosts for personal + modules.ssh.hosts = { + github-private = { + hostname = "ssh.github.com"; + user = "git"; + port = 443; + identityFile = "~/.ssh/keys/github-private.pub"; + }; + gitea-ssh-olsen-cloud = { + hostname = "gitea-ssh.olsen.cloud"; + user = "git"; + port = 2202; + identityFile = "~/.ssh/keys/github-private.pub"; + }; + }; + + # Personal-only packages + modules.packages.personal = true; + + # Project-specific git config + home.file.".Projects/.gitconfig".text = '' + [user] + email = fbtijfdq@void.black + name = Morten Olsen + signingkey = ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFaIAP/ZJ7+7jeR44e1yIJjfQAB6MN351LDKJAXVF62P + + [commit] + gpgsign = true + + [gpg] + format = ssh + + [gpg "ssh"] + program = "/Applications/1Password.app/Contents/MacOS/op-ssh-sign" + + [url "ssh://git@ssh-gitea.olsen.cloud:2205/"] + insteadOf = "https://gitea.olsen.cloud/" + + [url "git@github-private:"] + insteadOf = https://github.com/ + ''; +} +``` + +### 4.3 Work Profile ([`home/work.nix`](home/work.nix)) + +```nix +{ config, lib, pkgs, ... }: + +{ + # Git configuration for work + modules.git = { + profile = "work"; + userEmail = "fbtijfdq@void.black"; # Default email + signing.key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILAzuPy7D/54GxMq9Zhz0CUjaDnEQ6RkQ/yqVYl7U55k"; + + includes = [ + { + condition = "gitdir:~/Projects/private/"; + path = "~/Projects/private/.gitconfig"; + } + { + condition = "gitdir:~/Projects/zeronorth/"; + path = "~/Projects/zeronorth/.gitconfig"; + } + ]; + }; + + # SSH hosts for work + modules.ssh.hosts = { + github-private = { + hostname = "ssh.github.com"; + user = "git"; + port = 443; + identityFile = "~/.ssh/keys/github-private.pub"; + }; + github-zeronorth = { + hostname = "ssh.github.com"; + user = "git"; + port = 443; + identityFile = "~/.ssh/keys/github-zeronorth.pub"; + }; + gitea-ssh-olsen-cloud = { + hostname = "gitea-ssh.olsen.cloud"; + user = "git"; + port = 2202; + identityFile = "~/.ssh/keys/github-private.pub"; + }; + }; + + # Work-only packages (none currently) + modules.packages.work = true; + + # Project-specific git configs + home.file = { + "Projects/private/.gitconfig".text = '' + [user] + email = fbtijfdq@void.black + name = Morten Olsen + signingkey = ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILAzuPy7D/54GxMq9Zhz0CUjaDnEQ6RkQ/yqVYl7U55k + + [commit] + gpgsign = true + + [gpg] + format = ssh + + [gpg "ssh"] + program = "/Applications/1Password.app/Contents/MacOS/op-ssh-sign" + + [url "ssh://git@ssh-gitea.olsen.cloud:2205/"] + insteadOf = "https://gitea.olsen.cloud/" + + [url "git@github-private:"] + insteadOf = https://github.com/ + ''; + + "Projects/zeronorth/.gitconfig".text = '' + [user] + email = morten.olsen@zeronorth.com + name = Morten Olsen + signingkey = ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKDbZITpz5QrVIxPn9gKVWMPK+3W3YZZGszFOQvO/h7M + + [commit] + gpgsign = true + + [gpg] + format = ssh + + [gpg "ssh"] + program = "/Applications/1Password.app/Contents/MacOS/op-ssh-sign" + + [url "git@github-zeronorth:"] + insteadOf = https://github.com/ + ''; + + "Projects/zeronorth/.envrc".text = '' + export NODE_AUTH_TOKEN="op://Employee/Github NPM Token/password" + export NPM_TOKEN="op://Employee/Github NPM Token/password" + export NPM_GITHUB_TOKEN="op://Employee/Github NPM Token/password" + export AWS_PROFILE=zntest + ''; + }; +} +``` + +### 4.4 Profile Selection Flow + +```mermaid +flowchart TD + A[New Machine Setup] --> B{Machine Type?} + B -->|Personal| C[darwin-rebuild switch --flake .#personal] + B -->|Work| D[darwin-rebuild switch --flake .#work] + + C --> E[Loads hosts/personal + home/personal.nix] + D --> F[Loads hosts/work + home/work.nix] + + E --> G[Personal SSH keys, git config, packages] + F --> H[Work SSH keys, git config, zeronorth setup] + + G --> I[System Ready] + H --> I +``` + +--- + +## 5. Usage Patterns + +### 5.1 Initial Setup on New Machine + +#### Prerequisites + +1. Install Nix with flakes enabled: +```bash +curl -L https://nixos.org/nix/install | sh +``` + +2. Enable flakes in Nix configuration: +```bash +mkdir -p ~/.config/nix +echo "experimental-features = nix-command flakes" >> ~/.config/nix/nix.conf +``` + +#### Personal Machine Setup + +```bash +# Clone the dotfiles repository +git clone https://github.com/username/dotfiles.git ~/.dotfiles +cd ~/.dotfiles + +# Build and activate the personal configuration +nix build .#darwinConfigurations.personal.system +./result/sw/bin/darwin-rebuild switch --flake .#personal + +# Subsequent rebuilds +darwin-rebuild switch --flake .#personal +``` + +#### Work Machine Setup + +```bash +# Clone the dotfiles repository +git clone https://github.com/username/dotfiles.git ~/.dotfiles +cd ~/.dotfiles + +# Build and activate the work configuration +nix build .#darwinConfigurations.work.system +./result/sw/bin/darwin-rebuild switch --flake .#work + +# Subsequent rebuilds +darwin-rebuild switch --flake .#work +``` + +### 5.2 Switching Configurations + +If you need to switch a machine from personal to work (or vice versa): + +```bash +# Switch to work configuration +darwin-rebuild switch --flake .#work + +# Switch back to personal configuration +darwin-rebuild switch --flake .#personal +``` + +**Note:** Switching profiles will change git configs, SSH hosts, and installed packages. Some manual cleanup may be needed for files that were created by the previous profile. + +### 5.3 Updating Configuration + +#### After Editing Nix Files + +```bash +# Rebuild and switch to new configuration +darwin-rebuild switch --flake .#personal # or .#work + +# Build without switching (for testing) +darwin-rebuild build --flake .#personal +``` + +#### Updating Flake Inputs + +```bash +# Update all inputs +nix flake update + +# Update specific input +nix flake lock --update-input nixpkgs +nix flake lock --update-input home-manager + +# Rebuild after update +darwin-rebuild switch --flake .#personal +``` + +### 5.4 Common Commands Reference + +| Command | Description | +|---------|-------------| +| `darwin-rebuild switch --flake .#personal` | Apply personal configuration | +| `darwin-rebuild switch --flake .#work` | Apply work configuration | +| `darwin-rebuild build --flake .#personal` | Build without applying | +| `nix flake update` | Update all flake inputs | +| `nix flake check` | Validate flake configuration | +| `nix repl --expr 'builtins.getFlake "."'` | Interactive debugging | +| `darwin-rebuild --rollback` | Rollback to previous generation | +| `nix-collect-garbage -d` | Clean up old generations | + +### 5.5 Development Workflow + +```bash +# Enter development shell with tools for editing this repo +nix develop + +# Format Nix files +nixfmt **/*.nix + +# Check configuration validity +nix flake check +``` + +--- + +## 6. Future Extensibility + +### 6.1 Adding Linux Support + +The architecture already includes placeholder `homeConfigurations` for Linux. To fully support Linux: + +1. Add Linux-specific modules in `modules/home/` with platform conditionals: + +```nix +{ config, lib, pkgs, ... }: + +{ + config = lib.mkMerge [ + # Common configuration + { ... } + + # macOS-specific + (lib.mkIf pkgs.stdenv.isDarwin { ... }) + + # Linux-specific + (lib.mkIf pkgs.stdenv.isLinux { ... }) + ]; +} +``` + +2. Create Linux host configurations in `hosts/linux/`. + +3. Use `homeConfigurations` for standalone home-manager on Linux. + +### 6.2 Adding New Machines + +To add a new machine configuration: + +1. Create a new directory under `hosts/`: +``` +hosts/ +├── personal/ +├── work/ +└── new-machine/ + └── default.nix +``` + +2. Add the configuration to `flake.nix`: +```nix +darwinConfigurations."new-machine" = nix-darwin.lib.darwinSystem { + # ... +}; +``` + +### 6.3 Adding New Modules + +To add a new module (e.g., for a new application): + +1. Create the module file: +``` +modules/home/apps/new-app.nix +``` + +2. Import it in the parent module: +```nix +# modules/home/apps/default.nix +imports = [ + ./aerospace.nix + ./new-app.nix +]; +``` + +3. Add options and configuration following the established pattern. + +--- + +## 7. Migration Checklist + +When implementing this architecture, follow this order: + +- [ ] Create basic flake.nix with inputs +- [ ] Set up directory structure +- [ ] Implement darwin modules (homebrew, system) +- [ ] Implement shell modules (zsh, starship, atuin) +- [ ] Implement git modules with profile support +- [ ] Implement SSH modules +- [ ] Implement terminal modules (tmux) +- [ ] Implement package modules +- [ ] Implement apps modules (aerospace) +- [ ] Create personal profile configuration +- [ ] Create work profile configuration +- [ ] Set up secrets management (sops-nix) +- [ ] Test on personal machine +- [ ] Test on work machine +- [ ] Document any manual steps +- [ ] Remove chezmoi after successful migration + +--- + +## 8. Summary + +This architecture provides: + +1. **Clean separation of concerns** - Darwin system config, home-manager user config, and reusable modules are clearly separated. + +2. **Profile-based configuration** - Personal and work environments are distinct profiles that share common modules but override specific settings. + +3. **Modular design** - Each functional area (shell, git, ssh, packages) is a self-contained module with configurable options. + +4. **macOS-first with extensibility** - Designed for macOS with nix-darwin, but structured to easily add Linux support later. + +5. **Declarative package management** - CLI tools via nixpkgs, GUI apps via Homebrew casks managed through nix-darwin. + +6. **Secrets handling** - Support for both sops-nix encrypted secrets and 1Password CLI references. + +7. **Simple usage patterns** - Single command to apply configuration, easy switching between profiles. + +The migration from chezmoi to this Nix-based setup will provide reproducible, declarative configuration management with the full power of the Nix ecosystem. + +--- + +## 9. Final Implementation + +This section documents the actual implemented structure, which may differ slightly from the original design above. + +### 9.1 Implemented Directory Structure + +``` +. +├── flake.nix # Main flake entry point +├── flake.lock # Locked dependencies +├── README.md # Setup and usage documentation +│ +├── home/ # Home Manager configurations +│ ├── default.nix # Shared home configuration +│ ├── personal.nix # Personal profile overrides +│ └── work.nix # Work profile overrides +│ +├── hosts/ # Machine-specific nix-darwin configs +│ ├── personal/ +│ │ └── default.nix # Personal machine darwin settings +│ └── work/ +│ └── default.nix # Work machine darwin settings +│ +├── modules/ # Reusable Nix modules +│ ├── darwin/ +│ │ └── homebrew.nix # Homebrew cask management +│ │ +│ └── home/ # Home Manager modules +│ ├── apps.nix # Application configs (aerospace, jellyfin-tui) +│ ├── git.nix # Git configuration with signing +│ ├── git-files.nix # Project-specific git configs +│ ├── packages.nix # CLI packages via Nix +│ ├── shell.nix # Shell environment (zsh, starship, etc.) +│ ├── ssh.nix # SSH configuration with 1Password +│ └── tmux.nix # Tmux configuration +│ +└── docs/ # Documentation + ├── migration-analysis.md # Original chezmoi analysis + ├── nix-architecture.md # This document + └── usage.md # Detailed usage guide +``` + +### 9.2 Module Reference + +#### Darwin Modules + +| Module | File | Purpose | +|--------|------|---------| +| **Homebrew** | [`modules/darwin/homebrew.nix`](../modules/darwin/homebrew.nix) | Manages Homebrew taps, formulae, and casks declaratively | + +#### Home Manager Modules + +| Module | File | Purpose | +|--------|------|---------| +| **Shell** | [`modules/home/shell.nix`](../modules/home/shell.nix) | Zsh, Starship, Atuin, Direnv, Zoxide, FZF, Pyenv | +| **Packages** | [`modules/home/packages.nix`](../modules/home/packages.nix) | CLI tools and development packages | +| **Git** | [`modules/home/git.nix`](../modules/home/git.nix) | Git configuration with delta, signing, aliases | +| **Git Files** | [`modules/home/git-files.nix`](../modules/home/git-files.nix) | Project-specific gitconfig files | +| **SSH** | [`modules/home/ssh.nix`](../modules/home/ssh.nix) | SSH with 1Password agent, host configurations | +| **Tmux** | [`modules/home/tmux.nix`](../modules/home/tmux.nix) | Tmux with vim navigation, plugins | +| **Apps** | [`modules/home/apps.nix`](../modules/home/apps.nix) | Aerospace window manager, Jellyfin TUI | + +### 9.3 Module Configuration Options + +#### `modules.homebrew` (Darwin) + +| Option | Type | Default | Description | +|--------|------|---------|-------------| +| `enable` | bool | `false` | Enable Homebrew management | +| `casks.shared` | list of str | (see module) | Casks installed on all machines | +| `casks.personal` | list of str | (see module) | Casks installed only on personal machines | +| `casks.enablePersonal` | bool | `false` | Whether to install personal casks | +| `brews` | list of str | (see module) | Homebrew formulae to install | +| `taps` | list of str | (see module) | Homebrew taps to add | +| `cleanup` | enum | `"zap"` | Cleanup behavior: `"none"`, `"uninstall"`, `"zap"` | + +#### `modules.ssh` (Home) + +| Option | Type | Default | Description | +|--------|------|---------|-------------| +| `enable` | bool | `false` | Enable SSH configuration | +| `enableGitHubPrivate` | bool | `false` | Enable github-private host | +| `enableGitHubZeronorth` | bool | `false` | Enable github-zeronorth host | +| `enableCoder` | bool | `false` | Enable Coder SSH hosts | +| `enableGiteaPrivate` | bool | `false` | Enable gitea-ssh.olsen.cloud host | +| `githubPrivateKeyPath` | str | `"~/.ssh/keys/github-private.pub"` | Path to GitHub private key | +| `githubZeronorthKeyPath` | str | `"~/.ssh/keys/github-zeronorth.pub"` | Path to GitHub Zeronorth key | + +#### `modules.git` (Home) + +| Option | Type | Default | Description | +|--------|------|---------|-------------| +| `enable` | bool | `false` | Enable Git configuration | +| `userName` | str | `"Morten Olsen"` | Git user name | +| `userEmail` | str | (required) | Git user email | +| `signingKey` | str | (required) | SSH signing key (public key) | +| `includes` | list of {condition, path} | `[]` | Conditional includes for project configs | + +#### `modules.gitFiles` (Home) + +| Option | Type | Default | Description | +|--------|------|---------|-------------| +| `enable` | bool | `false` | Enable project-specific git files | +| `personal.enable` | bool | `false` | Create ~/Projects/.gitconfig | +| `personal.email` | str | (example) | Email for personal projects | +| `personal.signingKey` | str | (required) | Signing key for personal projects | +| `private.enable` | bool | `false` | Create ~/Projects/private/.gitconfig | +| `private.email` | str | (example) | Email for private projects | +| `private.signingKey` | str | (required) | Signing key for private projects | +| `zeronorth.enable` | bool | `false` | Create ~/Projects/zeronorth/.gitconfig | +| `zeronorth.email` | str | (example) | Email for zeronorth projects | +| `zeronorth.signingKey` | str | (required) | Signing key for zeronorth projects | + +#### `modules.apps` (Home) + +| Option | Type | Default | Description | +|--------|------|---------|-------------| +| `enable` | bool | `false` | Enable application configurations | +| `aerospace.enable` | bool | `true` | Enable Aerospace window manager config | +| `jellyfin-tui.enable` | bool | `false` | Enable Jellyfin TUI config | +| `jellyfin-tui.serverUrl` | str | `""` | Jellyfin server URL | +| `jellyfin-tui.serverName` | str | `"Home Server"` | Display name for server | +| `jellyfin-tui.username` | str | `""` | Jellyfin username | +| `jellyfin-tui.passwordFile` | str | `""` | Path to password file | + +### 9.4 Profile Configuration Summary + +#### Personal Profile ([`home/personal.nix`](../home/personal.nix)) + +```nix +modules.git = { + enable = true; + userEmail = "alice@personal.example.com"; + signingKey = "ssh-ed25519 AAAAC3..."; + includes = [{ condition = "gitdir:~/Projects/"; path = "~/Projects/.gitconfig"; }]; +}; + +modules.gitFiles = { + enable = true; + personal = { enable = true; email = "..."; signingKey = "..."; }; +}; + +modules.ssh = { + enableGitHubPrivate = true; + enableGiteaPrivate = true; + enableGitHubZeronorth = false; + enableCoder = false; +}; + +modules.apps = { + jellyfin-tui = { enable = true; serverUrl = "..."; username = "..."; }; +}; +``` + +#### Work Profile ([`home/work.nix`](../home/work.nix)) + +```nix +modules.git = { + enable = true; + userEmail = "alice@work.example.com"; + signingKey = "ssh-ed25519 AAAAC3..."; + includes = [ + { condition = "gitdir:~/Projects/private/"; path = "~/Projects/private/.gitconfig"; } + { condition = "gitdir:~/Projects/zeronorth/"; path = "~/Projects/zeronorth/.gitconfig"; } + ]; +}; + +modules.gitFiles = { + enable = true; + private = { enable = true; email = "..."; signingKey = "..."; }; + zeronorth = { enable = true; email = "..."; signingKey = "..."; }; +}; + +modules.ssh = { + enableGitHubPrivate = true; + enableGitHubZeronorth = true; + enableCoder = true; + enableGiteaPrivate = false; +}; +``` + +### 9.5 Packages Installed + +#### Via Nix (from [`modules/home/packages.nix`](../modules/home/packages.nix)) + +| Category | Packages | +|----------|----------| +| **Modern CLI** | bat, eza, fd, ripgrep, delta | +| **File Utils** | jq, yq, tree, rsync, unzip, curl, wget, watch | +| **Git** | git, gh, jujutsu, lazygit | +| **Development** | neovim, tmux, nodejs_22, deno, rustup, python313, uv, gnumake, cmake | +| **Containers** | docker, docker-buildx, docker-compose, colima | +| **Kubernetes** | kubectl, kubernetes-helm, helmfile, k9s, istioctl, fluxcd | +| **Infrastructure** | terraform, ansible, sshpass, awscli2 | +| **Media** | ffmpeg | +| **Security** | gnupg, age, sops | +| **Misc** | graphviz, tree-sitter, htop, ncdu, tldr, nixfmt-rfc-style, nil | + +#### Via Homebrew Casks (from [`modules/darwin/homebrew.nix`](../modules/darwin/homebrew.nix)) + +**Shared (all machines):** +- 1password, 1password-cli +- ghostty, dbeaver-community, lens +- aerospace +- raycast, obsidian +- jellyfin-media-player, ollama-app +- localsend, mqtt-explorer, home-assistant + +**Personal only:** +- darktable +- proton-mail-bridge, proton-pass, protonvpn +- signal +- steam + +#### Via Homebrew Formulae + +- coder/coder/coder +- fluxcd/tap/flux +- sst/tap/opencode +- tree-sitter-cli + +### 9.6 Key Differences from Original Design + +1. **Simplified module structure** - Instead of deeply nested modules (e.g., `modules/home/shell/zsh.nix`), the implementation uses flatter files (e.g., `modules/home/shell.nix`) for simplicity. + +2. **No sops-nix** - Secrets are managed via 1Password CLI references rather than sops-nix encrypted files. + +3. **No lib/ directory** - Helper functions are defined inline in modules rather than in a separate lib directory. + +4. **No overlays/** - No custom package overlays are currently needed. + +5. **Combined shell module** - All shell-related configuration (zsh, starship, atuin, direnv, zoxide, fzf, pyenv) is in a single `shell.nix` file. + +6. **Separate git-files module** - Project-specific gitconfig files are managed in a dedicated `git-files.nix` module rather than inline in profile files. + +### 9.7 Migration Status + +The following chezmoi files have been migrated: + +| Chezmoi File | Nix Module | Status | +|--------------|------------|--------| +| `dot_zshrc` | `modules/home/shell.nix` | ✅ Migrated | +| `dot_tmux.conf` | `modules/home/tmux.nix` | ✅ Migrated | +| `dot_gitconfig.tmpl` | `modules/home/git.nix` | ✅ Migrated | +| `dot_gitignore_global` | `modules/home/git.nix` | ✅ Migrated | +| `dot_Brewfile.tmpl` | `modules/darwin/homebrew.nix` + `modules/home/packages.nix` | ✅ Migrated | +| `dot_aerospace.toml` | `modules/home/apps.nix` | ✅ Migrated | +| `dot_config/atuin/config.toml` | `modules/home/shell.nix` | ✅ Migrated | +| `dot_shellrc/rc.d/*` | `modules/home/shell.nix` | ✅ Migrated | +| `dot_ssh/config` | `modules/home/ssh.nix` | ✅ Migrated | +| `dot_ssh/config.d/*` | `modules/home/ssh.nix` | ✅ Migrated | +| `private_Projects/*` | `modules/home/git-files.nix` | ✅ Migrated | +| `private_Library/.../jellyfin-tui/*` | `modules/home/apps.nix` | ✅ Migrated | + +The original chezmoi files are kept in the repository for reference during the transition period. \ No newline at end of file diff --git a/docs/usage.md b/docs/usage.md new file mode 100644 index 0000000..37d7517 --- /dev/null +++ b/docs/usage.md @@ -0,0 +1,626 @@ +# Usage Guide + +This guide provides detailed instructions for using and maintaining your Nix Home Manager configuration. + +## Table of Contents + +1. [Initial Setup on a New Machine](#initial-setup-on-a-new-machine) +2. [Switching Between Profiles](#switching-between-profiles) +3. [Adding New Packages](#adding-new-packages) +4. [Adding New Configuration Modules](#adding-new-configuration-modules) +5. [Updating Flake Inputs](#updating-flake-inputs) +6. [Day-to-Day Operations](#day-to-day-operations) +7. [Troubleshooting](#troubleshooting) + +--- + +## Initial Setup on a New Machine + +### Step 1: Install Nix + +```bash +# Install Nix with the official installer +curl -L https://nixos.org/nix/install | sh + +# Follow the prompts and restart your terminal +# Or source the nix profile manually: +. /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh +``` + +### Step 2: Enable Flakes + +Flakes are an experimental feature that needs to be enabled: + +```bash +mkdir -p ~/.config/nix +cat >> ~/.config/nix/nix.conf << EOF +experimental-features = nix-command flakes +EOF +``` + +### Step 3: Install Homebrew (macOS only) + +Homebrew is required for GUI applications: + +```bash +/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" + +# Add Homebrew to PATH (for Apple Silicon Macs) +echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> ~/.zprofile +eval "$(/opt/homebrew/bin/brew shellenv)" +``` + +### Step 4: Install 1Password and Configure SSH Agent + +1. Download and install [1Password](https://1password.com/downloads/mac/) +2. Sign in to your 1Password account +3. Enable SSH Agent: + - Open 1Password → Settings → Developer + - Enable "SSH Agent" + - Enable "Use the SSH agent" +4. Enable CLI integration: + - In the same Developer settings + - Enable "Integrate with 1Password CLI" + +### Step 5: Clone the Repository + +```bash +# Clone to ~/.dotfiles (recommended location) +git clone ~/.dotfiles +cd ~/.dotfiles +``` + +### Step 6: Build and Apply Configuration + +For a **personal machine**: + +```bash +# First-time build (creates darwin-rebuild command) +nix build .#darwinConfigurations.personal.system + +# Apply the configuration +./result/sw/bin/darwin-rebuild switch --flake .#personal +``` + +For a **work machine**: + +```bash +# First-time build +nix build .#darwinConfigurations.work.system + +# Apply the configuration +./result/sw/bin/darwin-rebuild switch --flake .#work +``` + +### Step 7: Restart Your Shell + +```bash +# Start a new shell to pick up all changes +exec zsh +``` + +### Step 8: Verify Installation + +```bash +# Check that key tools are available +which starship # Should show nix store path +which atuin # Should show nix store path +which nvim # Should show nix store path + +# Verify git configuration +git config --get user.email +git config --get user.signingkey +``` + +--- + +## Switching Between Profiles + +### Switching from Personal to Work + +If you need to reconfigure a machine from personal to work: + +```bash +cd ~/.dotfiles +darwin-rebuild switch --flake .#work +``` + +**Note:** This will: +- Change git configuration (email, signing key) +- Update SSH hosts +- Remove personal-only Homebrew casks +- Disable personal-only features (like jellyfin-tui) + +### Switching from Work to Personal + +```bash +cd ~/.dotfiles +darwin-rebuild switch --flake .#personal +``` + +**Note:** This will: +- Change git configuration to personal settings +- Update SSH hosts +- Install personal Homebrew casks (darktable, signal, proton-*, steam) +- Enable personal features + +### Temporary Profile Testing + +To test a profile without fully switching: + +```bash +# Build without applying +darwin-rebuild build --flake .#work + +# Inspect what would change +ls -la result/ +``` + +--- + +## Adding New Packages + +### Adding CLI Tools via Nix (Recommended) + +Edit [`modules/home/packages.nix`](../modules/home/packages.nix): + +```nix +home.packages = with pkgs; [ + # ... existing packages ... + + # Add your new package + your-new-package +]; +``` + +Then rebuild: + +```bash +darwin-rebuild switch --flake .#personal # or .#work +``` + +#### Finding Package Names + +```bash +# Search for packages +nix search nixpkgs your-package-name + +# Or use the online search +# https://search.nixos.org/packages +``` + +### Adding GUI Applications via Homebrew + +Edit [`modules/darwin/homebrew.nix`](../modules/darwin/homebrew.nix): + +#### For All Machines (Shared) + +```nix +casks = { + shared = [ + # ... existing casks ... + "your-new-app" + ]; +}; +``` + +#### For Personal Machine Only + +```nix +casks = { + personal = [ + # ... existing personal casks ... + "personal-only-app" + ]; +}; +``` + +Then rebuild: + +```bash +darwin-rebuild switch --flake .#personal +``` + +### Adding Homebrew Formulae (Non-Nix CLI tools) + +For CLI tools that must come from Homebrew (e.g., from custom taps): + +```nix +# In modules/darwin/homebrew.nix +brews = [ + # ... existing brews ... + "tap-name/formula-name" +]; +``` + +### Adding Homebrew Taps + +```nix +# In modules/darwin/homebrew.nix +taps = [ + # ... existing taps ... + "owner/repo" +]; +``` + +--- + +## Adding New Configuration Modules + +### Creating a New Home Manager Module + +1. Create a new file in `modules/home/`: + +```bash +touch modules/home/your-module.nix +``` + +2. Add the module structure: + +```nix +# modules/home/your-module.nix +{ config, pkgs, lib, ... }: + +with lib; + +let + cfg = config.modules.yourModule; +in { + options.modules.yourModule = { + enable = mkEnableOption "Your module description"; + + # Add your options here + someSetting = mkOption { + type = types.str; + default = "default-value"; + description = "Description of this setting"; + }; + }; + + config = mkIf cfg.enable { + # Your configuration here + programs.someProgram = { + enable = true; + # ... + }; + }; +} +``` + +3. Import the module in [`home/default.nix`](../home/default.nix): + +```nix +imports = [ + # ... existing imports ... + ../modules/home/your-module.nix +]; +``` + +4. Enable the module: + +```nix +# In home/default.nix or profile-specific file +modules.yourModule = { + enable = true; + someSetting = "custom-value"; +}; +``` + +### Creating Profile-Specific Configuration + +For settings that differ between personal and work: + +1. Add options to your module with sensible defaults +2. Override in profile files: + +```nix +# home/personal.nix +modules.yourModule = { + enable = true; + someSetting = "personal-value"; +}; + +# home/work.nix +modules.yourModule = { + enable = true; + someSetting = "work-value"; +}; +``` + +### Adding Application Configuration Files + +For apps without Home Manager modules, use `home.file`: + +```nix +# In modules/home/apps.nix or a new module +home.file = { + ".config/your-app/config.toml".text = '' + # Your config content + setting = "value" + ''; + + # Or copy from a file + ".config/your-app/config.toml".source = ./files/your-app-config.toml; +}; +``` + +--- + +## Updating Flake Inputs + +### Update All Inputs + +```bash +cd ~/.dotfiles + +# Update all inputs to their latest versions +nix flake update + +# Review changes +git diff flake.lock + +# Apply the updates +darwin-rebuild switch --flake .#personal +``` + +### Update Specific Input + +```bash +# Update only nixpkgs +nix flake lock --update-input nixpkgs + +# Update only home-manager +nix flake lock --update-input home-manager + +# Update only nix-darwin +nix flake lock --update-input nix-darwin +``` + +### Pin to Specific Version + +Edit `flake.nix` to pin an input: + +```nix +inputs = { + # Pin to specific commit + nixpkgs.url = "github:nixos/nixpkgs/abc123..."; + + # Pin to specific branch + nixpkgs.url = "github:nixos/nixpkgs/nixos-24.05"; +}; +``` + +### Check for Updates + +```bash +# Show current input versions +nix flake metadata + +# Show what would be updated +nix flake update --dry-run +``` + +--- + +## Day-to-Day Operations + +### Rebuilding After Changes + +```bash +cd ~/.dotfiles + +# Make your changes to .nix files +vim modules/home/packages.nix + +# Rebuild and switch +darwin-rebuild switch --flake .#personal +``` + +### Quick Rebuild Alias + +Add to your shell configuration or use directly: + +```bash +# Alias for quick rebuilds +alias rebuild="darwin-rebuild switch --flake ~/.dotfiles#personal" +alias rebuild-work="darwin-rebuild switch --flake ~/.dotfiles#work" +``` + +### Checking Configuration Validity + +```bash +# Validate the flake +nix flake check + +# Build without switching (dry run) +darwin-rebuild build --flake .#personal +``` + +### Viewing Generation History + +```bash +# List all generations +darwin-rebuild --list-generations + +# See what's in the current generation +ls -la /run/current-system +``` + +### Rolling Back + +```bash +# Rollback to previous generation +darwin-rebuild --rollback + +# Switch to specific generation +darwin-rebuild switch --generation 42 +``` + +### Garbage Collection + +```bash +# Remove old generations (keeps current) +nix-collect-garbage + +# Remove all old generations +nix-collect-garbage -d + +# Remove generations older than 30 days +nix-collect-garbage --delete-older-than 30d +``` + +### Development Shell + +For working on this repository: + +```bash +cd ~/.dotfiles + +# Enter development shell with formatting tools +nix develop + +# Format all Nix files +nixfmt **/*.nix +``` + +--- + +## Troubleshooting + +### Common Issues + +#### "experimental-features" Error + +**Problem:** `error: experimental Nix feature 'flakes' is disabled` + +**Solution:** +```bash +mkdir -p ~/.config/nix +echo "experimental-features = nix-command flakes" >> ~/.config/nix/nix.conf +``` + +#### Homebrew Cask Installation Fails + +**Problem:** A cask fails to install during rebuild + +**Solution:** +```bash +# Update Homebrew +brew update + +# Check for issues +brew doctor + +# Try installing manually first +brew install --cask problematic-cask + +# Then rebuild +darwin-rebuild switch --flake .#personal +``` + +#### SSH Key Not Found + +**Problem:** Git operations fail with SSH key errors + +**Solution:** +1. Ensure 1Password SSH agent is enabled +2. Check the key is in 1Password +3. Verify the public key file exists: + ```bash + ls -la ~/.ssh/keys/ + ``` +4. Test SSH connection: + ```bash + ssh -T git@github.com + ``` + +#### Build Fails with "attribute not found" + +**Problem:** `error: attribute 'somePackage' missing` + +**Solution:** +```bash +# Update flake inputs +nix flake update + +# Search for correct package name +nix search nixpkgs package-name +``` + +#### Conflicting Files + +**Problem:** `error: collision between ... and ...` + +**Solution:** +1. Check if the file is managed by multiple sources +2. Remove manual file if it exists: + ```bash + rm ~/.config/conflicting-file + ``` +3. Rebuild + +### Debug Mode + +For detailed error information: + +```bash +# Build with trace +darwin-rebuild switch --flake .#personal --show-trace + +# Even more verbose +darwin-rebuild switch --flake .#personal --show-trace --verbose +``` + +### Interactive Debugging + +```bash +# Open Nix REPL with flake loaded +nix repl +:lf . +# Now you can inspect the configuration +darwinConfigurations.personal.config.home-manager.users.alice.home.packages +``` + +### Reset to Clean State + +If things are badly broken: + +```bash +# Remove result symlink +rm -f result + +# Clean Nix store +nix-collect-garbage -d + +# Remove home-manager generations +rm -rf ~/.local/state/home-manager + +# Rebuild from scratch +nix build .#darwinConfigurations.personal.system +./result/sw/bin/darwin-rebuild switch --flake .#personal +``` + +### Getting Help + +```bash +# Home Manager options +man home-configuration.nix + +# nix-darwin options +man darwin-configuration.nix + +# Search for options online +# https://home-manager-options.extranix.com/ +# https://daiderd.com/nix-darwin/manual/ +``` + +--- + +## Best Practices + +1. **Commit changes before rebuilding** - Makes it easy to rollback +2. **Test builds before switching** - Use `darwin-rebuild build` first +3. **Update inputs regularly** - Keep packages up to date +4. **Use garbage collection** - Prevent disk space issues +5. **Document custom modules** - Add comments explaining purpose +6. **Keep secrets out of Nix** - Use 1Password or sops-nix for secrets \ No newline at end of file diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..43dad4a --- /dev/null +++ b/flake.lock @@ -0,0 +1,69 @@ +{ + "nodes": { + "home-manager": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1765682243, + "narHash": "sha256-yeCxFV/905Wr91yKt5zrVvK6O2CVXWRMSrxqlAZnLp0=", + "owner": "nix-community", + "repo": "home-manager", + "rev": "58bf3ecb2d0bba7bdf363fc8a6c4d49b4d509d03", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "home-manager", + "type": "github" + } + }, + "nix-darwin": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1765684049, + "narHash": "sha256-svCS2r984qEowMT0y3kCrsD/m0J6zaF5I/UusS7QaH0=", + "owner": "LnL7", + "repo": "nix-darwin", + "rev": "9b628e171bfaea1a3d1edf31eee46251e0fe4a33", + "type": "github" + }, + "original": { + "owner": "LnL7", + "repo": "nix-darwin", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1765644376, + "narHash": "sha256-yqHBL2wYGwjGL2GUF2w3tofWl8qO9tZEuI4wSqbCrtE=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "23735a82a828372c4ef92c660864e82fbe2f5fbe", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "home-manager": "home-manager", + "nix-darwin": "nix-darwin", + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..3e3a05d --- /dev/null +++ b/flake.nix @@ -0,0 +1,105 @@ +{ + description = "Home Manager configuration with nix-darwin support"; + + inputs = { + # Core dependencies - using unstable for latest packages + nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; + + # Home Manager for user environment management + home-manager = { + url = "github:nix-community/home-manager"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + + # nix-darwin for macOS system configuration + nix-darwin = { + url = "github:LnL7/nix-darwin"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; + + outputs = { + self, + nixpkgs, + home-manager, + nix-darwin, + ... + } @ inputs: let + # Default username - can be overridden per-host if needed + username = "alice"; + + # Common special args passed to all modules + specialArgs = {inherit inputs username;}; + in { + # Darwin (macOS) system configurations + darwinConfigurations = { + # Personal machine configuration + "personal" = nix-darwin.lib.darwinSystem { + system = "aarch64-darwin"; + inherit specialArgs; + modules = [ + # Host-specific darwin configuration + ./hosts/personal + + # Home Manager as a darwin module + home-manager.darwinModules.home-manager + { + home-manager = { + useGlobalPkgs = true; + useUserPackages = true; + extraSpecialArgs = specialArgs; + users.${username} = {...}: { + imports = [ + ./home + ./home/personal.nix + ]; + }; + }; + } + ]; + }; + + # Work machine configuration + "work" = nix-darwin.lib.darwinSystem { + system = "aarch64-darwin"; + inherit specialArgs; + modules = [ + # Host-specific darwin configuration + ./hosts/work + + # Home Manager as a darwin module + home-manager.darwinModules.home-manager + { + home-manager = { + useGlobalPkgs = true; + useUserPackages = true; + extraSpecialArgs = specialArgs; + users.${username} = {...}: { + imports = [ + ./home + ./home/work.nix + ]; + }; + }; + } + ]; + }; + }; + + # Development shell for working on this repository + devShells = let + systems = ["aarch64-darwin" "x86_64-darwin"]; + forAllSystems = nixpkgs.lib.genAttrs systems; + in + forAllSystems (system: let + pkgs = nixpkgs.legacyPackages.${system}; + in { + default = pkgs.mkShell { + packages = with pkgs; [ + nixfmt-rfc-style + nil # Nix LSP + ]; + }; + }); + }; +} \ No newline at end of file diff --git a/home/default.nix b/home/default.nix new file mode 100644 index 0000000..2cb6a32 --- /dev/null +++ b/home/default.nix @@ -0,0 +1,83 @@ +# Shared Home Manager configuration +# +# This file contains settings common to all profiles (personal and work). +# Profile-specific settings are in personal.nix and work.nix. +{ + config, + pkgs, + lib, + username, + ... +}: { + # Import shared modules + imports = [ + # Shell configuration (zsh, starship, atuin, direnv, zoxide, fzf, pyenv) + ../modules/home/shell.nix + + # Packages (CLI tools, development tools, etc.) + ../modules/home/packages.nix + + # SSH configuration (1Password agent, host configs) + ../modules/home/ssh.nix + + # Tmux terminal multiplexer + ../modules/home/tmux.nix + + # Application configurations (aerospace, jellyfin-tui, etc.) + ../modules/home/apps.nix + ]; + + home = { + # Home Manager needs a bit of information about you and the paths it should manage + username = username; + homeDirectory = "/Users/${username}"; + + # This value determines the Home Manager release that your configuration is + # compatible with. This helps avoid breakage when a new Home Manager release + # introduces backwards incompatible changes. + # + # You should not change this value, even if you update Home Manager. If you do + # want to update the value, then make sure to first check the Home Manager + # release notes. + stateVersion = "24.05"; + + # Environment variables are now primarily managed in shell.nix + # These are kept here for any that need to be available outside of zsh + sessionVariables = { + EDITOR = "nvim"; + LANG = "en_US.UTF-8"; + }; + + # Add directories to PATH + # Note: Additional PATH entries are in shell.nix profileExtra + sessionPath = [ + "$HOME/.local/bin" + "$HOME/.cargo/bin" + "/opt/homebrew/bin" + ]; + }; + + # Let Home Manager install and manage itself + programs.home-manager.enable = true; + + # ========================================================================== + # SSH Configuration (shared settings) + # ========================================================================== + # Enable the SSH module - profile-specific hosts are configured in + # personal.nix and work.nix + modules.ssh = { + enable = true; + # Host-specific settings are configured per-profile + }; + + # ========================================================================== + # Apps Configuration (shared settings) + # ========================================================================== + # Enable the apps module - aerospace is enabled by default for macOS + # Profile-specific apps (like jellyfin-tui) are configured per-profile + modules.apps = { + enable = true; + aerospace.enable = true; + # jellyfin-tui is configured per-profile in personal.nix + }; +} \ No newline at end of file diff --git a/home/personal.nix b/home/personal.nix new file mode 100644 index 0000000..f6c0a34 --- /dev/null +++ b/home/personal.nix @@ -0,0 +1,77 @@ +# Personal profile Home Manager configuration +# +# This file contains settings specific to the personal machine. +# It imports the shared default.nix and adds personal-specific overrides. +{ + config, + pkgs, + lib, + ... +}: { + # Import personal-specific modules + imports = [ + ../modules/home/git.nix + ../modules/home/git-files.nix + ]; + + # ========================================================================== + # Git Configuration for Personal Profile + # ========================================================================== + modules.git = { + enable = true; + userEmail = "alice@personal.example.com"; + signingKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFaIAP/ZJ7+7jeR44e1yIJjfQAB6MN351LDKJAXVF62P"; + + # On personal machine, all projects under ~/Projects/ use the same config + includes = [ + { + condition = "gitdir:~/Projects/"; + path = "~/Projects/.gitconfig"; + } + ]; + }; + + # ========================================================================== + # Git Files Configuration for Personal Profile + # ========================================================================== + modules.gitFiles = { + enable = true; + + # Personal projects config (~/Projects/.gitconfig) + personal = { + enable = true; + email = "alice@personal.example.com"; + signingKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFaIAP/ZJ7+7jeR44e1yIJjfQAB6MN351LDKJAXVF62P"; + }; + }; + + # ========================================================================== + # SSH Hosts for Personal Profile + # ========================================================================== + modules.ssh = { + # Enable personal GitHub host + enableGitHubPrivate = true; + # Enable personal Gitea host + enableGiteaPrivate = true; + # Work-specific hosts disabled on personal machine + enableGitHubZeronorth = false; + enableCoder = false; + }; + + # ========================================================================== + # Apps Configuration for Personal Profile + # ========================================================================== + modules.apps = { + # Jellyfin TUI for media streaming + jellyfin-tui = { + enable = true; + serverName = "Home Server"; + serverUrl = "https://jellyfin.olsen.cloud"; + username = "morten"; + passwordFile = "/Users/alice/Library/Application Support/jellyfin-tui/pass"; + }; + }; + + # Personal-only packages + # modules.packages.personal = true; +} \ No newline at end of file diff --git a/home/work.nix b/home/work.nix new file mode 100644 index 0000000..e9d0edc --- /dev/null +++ b/home/work.nix @@ -0,0 +1,82 @@ +# Work profile Home Manager configuration +# +# This file contains settings specific to the work machine. +# It imports the shared default.nix and adds work-specific overrides. +{ + config, + pkgs, + lib, + ... +}: { + # Import work-specific modules + imports = [ + ../modules/home/git.nix + ../modules/home/git-files.nix + ]; + + # ========================================================================== + # Git Configuration for Work Profile + # ========================================================================== + # On work machine, we use a default email but override per-project + # The main git config doesn't have a default includeIf for ~/Projects/ + # Instead, we have separate includes for private and zeronorth directories + modules.git = { + enable = true; + # Default email for work (used when not in a specific project directory) + userEmail = "morten.olsen@zeronorth.com"; + # Default signing key for work + signingKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILAzuPy7D/54GxMq9Zhz0CUjaDnEQ6RkQ/yqVYl7U55k"; + + # On work machine, we have separate includes for private and zeronorth + includes = [ + { + condition = "gitdir:~/Projects/private/"; + path = "~/Projects/private/.gitconfig"; + } + { + condition = "gitdir:~/Projects/zeronorth/"; + path = "~/Projects/zeronorth/.gitconfig"; + } + ]; + }; + + # ========================================================================== + # Git Files Configuration for Work Profile + # ========================================================================== + modules.gitFiles = { + enable = true; + + # Private projects config (~/Projects/private/.gitconfig) + # For personal projects on the work machine + private = { + enable = true; + email = "morten.olsen@zeronorth.com"; + signingKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILAzuPy7D/54GxMq9Zhz0CUjaDnEQ6RkQ/yqVYl7U55k"; + }; + + # Zeronorth projects config (~/Projects/zeronorth/.gitconfig) + # For work projects + zeronorth = { + enable = true; + email = "morten.olsen@zeronorth.com"; + signingKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKDbZITpz5QrVIxPn9gKVWMPK+3W3YZZGszFOQvO/h7M"; + }; + }; + + # ========================================================================== + # SSH Hosts for Work Profile + # ========================================================================== + modules.ssh = { + # Enable personal GitHub host (for personal projects on work machine) + enableGitHubPrivate = true; + # Enable work GitHub host + enableGitHubZeronorth = true; + # Enable Coder for remote development environments + enableCoder = true; + # Gitea is personal-only, not needed on work machine + enableGiteaPrivate = false; + }; + + # Work-only packages + # modules.packages.work = true; +} \ No newline at end of file diff --git a/hosts/personal/default.nix b/hosts/personal/default.nix new file mode 100644 index 0000000..1fd6de8 --- /dev/null +++ b/hosts/personal/default.nix @@ -0,0 +1,56 @@ +# Personal machine darwin configuration +# +# This file contains nix-darwin settings specific to the personal machine. +# It sets up system-level configuration and integrates with home-manager. +{ + config, + pkgs, + lib, + username, + ... +}: { + # Import darwin modules + imports = [ + ../../modules/darwin/homebrew.nix + ]; + + # Nix configuration + nix = { + settings = { + # Enable flakes and new nix command + experimental-features = ["nix-command" "flakes"]; + # Avoid unwanted garbage collection when using nix-direnv + keep-outputs = true; + keep-derivations = true; + }; + }; + + # Allow unfree packages + nixpkgs.config.allowUnfree = true; + + # Set the primary user for this machine (required for homebrew and other user-specific options) + system.primaryUser = username; + + users.users.${username} = { + name = username; + home = "/Users/${username}"; + }; + + # System-level programs + programs = { + # Enable zsh as it's the default macOS shell + zsh.enable = true; + }; + + # macOS system preferences will be configured in modules/darwin/system.nix + + # Homebrew configuration - enable with personal casks + modules.homebrew = { + enable = true; + casks.enablePersonal = true; + }; + + # Used for backwards compatibility, read the changelog before changing. + # $ darwin-rebuild changelog + system.stateVersion = 5; +} \ No newline at end of file diff --git a/hosts/work/default.nix b/hosts/work/default.nix new file mode 100644 index 0000000..01d3eb0 --- /dev/null +++ b/hosts/work/default.nix @@ -0,0 +1,68 @@ +# Work machine darwin configuration +# +# This file contains nix-darwin settings specific to the work machine. +# It sets up system-level configuration and integrates with home-manager. +{ + config, + pkgs, + lib, + username, + ... +}: { + # Import darwin modules + imports = [ + ../../modules/darwin/homebrew.nix + ]; + + # Nix configuration + nix = { + settings = { + # Enable flakes and new nix command + experimental-features = ["nix-command" "flakes"]; + # Avoid unwanted garbage collection when using nix-direnv + keep-outputs = true; + keep-derivations = true; + }; + }; + + # Allow unfree packages + nixpkgs.config.allowUnfree = true; + + # Set the primary user for this machine (required for homebrew and other user-specific options) + system.primaryUser = username; + + users.users.${username} = { + name = username; + home = "/Users/${username}"; + }; + + # System-level programs + programs = { + # Enable zsh as it's the default macOS shell + zsh.enable = true; + }; + + # Allow nix-darwin to manage /etc files that may already exist + # These hashes are from the existing system files that nix-darwin will replace + environment.etc = { + bashrc.knownSha256Hashes = [ + "8b5e3466922d1ae34bc145e21c7e53e7329a7a7b58b148b436bd954d5e651ac3" + ]; + zshrc.knownSha256Hashes = [ + "cf0f7b7775b4c058d6085d9e7e57d58c307ca43730f8e4d921a9ef4e530e7e16" + ]; + }; + + # macOS system preferences will be configured in modules/darwin/system.nix + + # Homebrew configuration - shared and work casks (no personal casks) + modules.homebrew = { + enable = true; + casks.enablePersonal = false; + casks.enableWork = true; + }; + + # Used for backwards compatibility, read the changelog before changing. + # $ darwin-rebuild changelog + system.stateVersion = 5; +} \ No newline at end of file diff --git a/modules/darwin/homebrew.nix b/modules/darwin/homebrew.nix new file mode 100644 index 0000000..a09524f --- /dev/null +++ b/modules/darwin/homebrew.nix @@ -0,0 +1,177 @@ +# Homebrew cask management via nix-darwin +# +# This module manages Homebrew packages declaratively through nix-darwin. +# It handles taps, formulae, and casks with separate options for shared +# and personal-only packages. +{ + config, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.modules.homebrew; +in { + options.modules.homebrew = { + enable = mkEnableOption "Homebrew management via nix-darwin"; + + # Cask categories + casks = { + shared = mkOption { + type = types.listOf types.str; + default = [ + # Password management + "1password" + "1password-cli" + + # Terminal & Development + "ghostty" + "dbeaver-community" + "lens" + + # Window management + "aerospace" + + # Productivity + "raycast" + "obsidian" + + # Media + "jellyfin-media-player" + "ollama-app" + + # Networking & IoT + "localsend" + "home-assistant" + ]; + description = "Homebrew casks to install on all machines"; + }; + + personal = mkOption { + type = types.listOf types.str; + default = [ + # Photography + "darktable" + + # Privacy & Security (Proton suite) + "proton-mail-bridge" + "proton-pass" + "protonvpn" + + # Communication + "signal" + + # Gaming + "steam" + ]; + description = "Homebrew casks to install only on personal machines"; + }; + + enablePersonal = mkOption { + type = types.bool; + default = false; + description = "Whether to install personal-only casks"; + }; + + work = mkOption { + type = types.listOf types.str; + default = [ + # Communication + "slack" + "pritunl" + ]; + description = "Homebrew casks to install only on work machines"; + }; + + enableWork = mkOption { + type = types.bool; + default = false; + description = "Whether to install work-only casks"; + }; + }; + + # Homebrew formulae (for packages not available or preferred from Homebrew) + brews = mkOption { + type = types.listOf types.str; + default = [ + # These are from custom taps or preferred from Homebrew + "coder/coder/coder" + "fluxcd/tap/flux" + "nvm" + "sst/tap/opencode" + "tree-sitter-cli" + ]; + description = "Homebrew formulae to install (for packages not in nixpkgs)"; + }; + + # Required taps + taps = mkOption { + type = types.listOf types.str; + default = [ + "coder/coder" + "felixkratz/formulae" + "fluxcd/tap" + "nikitabobko/tap" + "sst/tap" + ]; + description = "Homebrew taps to add"; + }; + + # Cleanup behavior + cleanup = mkOption { + type = types.enum ["none" "uninstall" "zap"]; + default = "zap"; + description = '' + Cleanup behavior for Homebrew packages: + - none: Don't remove anything + - uninstall: Remove packages not in the configuration + - zap: Remove packages and their associated files (most aggressive) + ''; + }; + }; + + config = mkIf cfg.enable { + # Enable Homebrew support in nix-darwin + homebrew = { + enable = true; + + # Activation settings + onActivation = { + # Auto-update Homebrew itself + autoUpdate = true; + # Upgrade outdated packages + upgrade = true; + # Cleanup behavior for unmanaged packages + cleanup = cfg.cleanup; + }; + + # Global settings + global = { + # Don't auto-update before every brew command + autoUpdate = false; + # Use Brewfile lockfile + brewfile = true; + }; + + # Taps (third-party repositories) + taps = cfg.taps; + + # Formulae (CLI tools from Homebrew) + brews = cfg.brews; + + # Casks (GUI applications) + casks = + cfg.casks.shared + ++ ( + if cfg.casks.enablePersonal + then cfg.casks.personal + else [] + ) + ++ ( + if cfg.casks.enableWork + then cfg.casks.work + else [] + ); + }; + }; +} diff --git a/modules/home/apps.nix b/modules/home/apps.nix new file mode 100644 index 0000000..ce61124 --- /dev/null +++ b/modules/home/apps.nix @@ -0,0 +1,172 @@ +# Application configuration module +# +# Manages configuration files for applications that don't have +# dedicated Home Manager modules. Uses home.file to place config files. +{ + config, + pkgs, + lib, + ... +}: let + cfg = config.modules.apps; +in { + options.modules.apps = { + enable = lib.mkEnableOption "application configurations"; + + aerospace = { + enable = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Enable Aerospace window manager configuration (macOS only)"; + }; + }; + + jellyfin-tui = { + enable = lib.mkOption { + type = lib.types.bool; + default = false; + description = "Enable Jellyfin TUI configuration"; + }; + + serverUrl = lib.mkOption { + type = lib.types.str; + default = ""; + description = "Jellyfin server URL"; + }; + + serverName = lib.mkOption { + type = lib.types.str; + default = "Home Server"; + description = "Display name for the Jellyfin server"; + }; + + username = lib.mkOption { + type = lib.types.str; + default = ""; + description = "Jellyfin username"; + }; + + passwordFile = lib.mkOption { + type = lib.types.str; + default = ""; + description = "Path to file containing Jellyfin password"; + }; + }; + }; + + config = lib.mkIf cfg.enable { + # Aerospace window manager configuration (macOS) + # Placed at ~/.aerospace.toml + home.file = lib.mkMerge [ + # Aerospace configuration + (lib.mkIf cfg.aerospace.enable { + ".aerospace.toml".text = '' + after-startup-command = [] + + start-at-login = true + + enable-normalization-flatten-containers = true + enable-normalization-opposite-orientation-for-nested-containers = true + + accordion-padding = 100 + + default-root-container-layout = 'tiles' + + default-root-container-orientation = 'auto' + on-focused-monitor-changed = ['move-mouse monitor-lazy-center'] + + automatically-unhide-macos-hidden-apps = true + + [[on-window-detected]] + if.app-name-regex-substring = 'elgato' + run = 'layout floating' + + [key-mapping] + preset = 'qwerty' + + [gaps] + inner.horizontal = 10 + inner.vertical = 10 + outer.left = 10 + outer.bottom = 10 + outer.top = 5 + outer.right = 10 + + [mode.main.binding] + alt-ctrl-f = 'fullscreen' + + alt-slash = 'layout tiles horizontal vertical' + alt-comma = 'layout accordion horizontal vertical' + + alt-cmd-h = 'focus left' + alt-cmd-j = 'focus down' + alt-cmd-k = 'focus up' + alt-cmd-l = 'focus right' + + cmd-shift-h = 'move left' + cmd-shift-j = 'move down' + cmd-shift-k = 'move up' + cmd-shift-l = 'move right' + + alt-minus = 'resize smart -50' + alt-equal = 'resize smart +50' + + alt-1 = 'workspace 1' + alt-2 = 'workspace 2' + alt-3 = 'workspace 3' + alt-4 = 'workspace 4' + alt-5 = 'workspace 5' + alt-6 = 'workspace 6' + + alt-shift-1 = 'move-node-to-workspace 1' + alt-shift-2 = 'move-node-to-workspace 2' + alt-shift-3 = 'move-node-to-workspace 3' + alt-shift-4 = 'move-node-to-workspace 4' + alt-shift-5 = 'move-node-to-workspace 5' + alt-shift-6 = 'move-node-to-workspace 6' + + alt-tab = 'workspace-back-and-forth' + alt-shift-tab = 'move-workspace-to-monitor --wrap-around next' + + alt-shift-comma = 'mode service' + alt-shift-enter = 'mode apps' + + alt-g = ['exec-and-forget open -a /Applications/Ghostty.app', 'mode main'] + + [mode.service.binding] + esc = ['reload-config', 'mode main'] + r = ['flatten-workspace-tree', 'mode main'] # reset layout + f = [ + 'layout floating tiling', + 'mode main', + ] # Toggle between floating and tiling layout + backspace = ['close-all-windows-but-current', 'mode main'] + + + h = ['join-with left', 'mode main'] + j = ['join-with down', 'mode main'] + k = ['join-with up', 'mode main'] + l = ['join-with right', 'mode main'] + + down = 'volume down' + up = 'volume up' + shift-down = ['volume set 0', 'mode main'] + + [mode.apps.binding] + g = ['exec-and-forget open -a /Applications/Ghostty.app', 'mode main'] + ''; + }) + + # Jellyfin TUI configuration (macOS uses ~/Library/Application Support/) + (lib.mkIf cfg.jellyfin-tui.enable { + "Library/Application Support/jellyfin-tui/config.yaml".text = '' + servers: + - name: ${cfg.jellyfin-tui.serverName} + password_file: ${cfg.jellyfin-tui.passwordFile} + url: ${cfg.jellyfin-tui.serverUrl} + username: ${cfg.jellyfin-tui.username} + ''; + }) + ]; + }; +} \ No newline at end of file diff --git a/modules/home/git-files.nix b/modules/home/git-files.nix new file mode 100644 index 0000000..cba67ee --- /dev/null +++ b/modules/home/git-files.nix @@ -0,0 +1,140 @@ +# Git Files module +# +# This module manages project-specific gitconfig files using home.file. +# These files are referenced by includeIf directives in the main git configuration. +# +# Based on the chezmoi configuration from: +# - private_Projects/dot_gitconfig +# - private_Projects/private_private/dot_gitconfig +# - private_Projects/private_zeronorth/dot_gitconfig +{ + config, + pkgs, + lib, + ... +}: +with lib; let + cfg = config.modules.gitFiles; + + # Helper function to generate gitconfig content + mkGitConfig = { + email, + signingKey, + urlRewrites ? {}, + }: '' + [user] + email = ${email} + name = Morten Olsen + signingkey = ${signingKey} + + [commit] + gpgsign = true + + [gpg] + format = ssh + + [gpg "ssh"] + program = "/Applications/1Password.app/Contents/MacOS/op-ssh-sign" + ${optionalString (urlRewrites != {}) (concatStringsSep "\n" (mapAttrsToList (name: value: '' + + [url "${name}"] + insteadOf = ${value}'') urlRewrites))} + ''; +in { + options.modules.gitFiles = { + enable = mkEnableOption "Project-specific git configuration files"; + + # Personal profile settings (used for ~/Projects/.gitconfig on personal machine) + personal = { + enable = mkEnableOption "Personal Projects gitconfig"; + + email = mkOption { + type = types.str; + default = "alice@personal.example.com"; + description = "Email for personal projects"; + }; + + signingKey = mkOption { + type = types.str; + description = "SSH signing key for personal projects"; + example = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI..."; + }; + }; + + # Private profile settings (used for ~/Projects/private/.gitconfig on work machine) + private = { + enable = mkEnableOption "Private Projects gitconfig"; + + email = mkOption { + type = types.str; + default = "alice@personal.example.com"; + description = "Email for private projects"; + }; + + signingKey = mkOption { + type = types.str; + description = "SSH signing key for private projects"; + example = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI..."; + }; + }; + + # Zeronorth profile settings (used for ~/Projects/zeronorth/.gitconfig on work machine) + zeronorth = { + enable = mkEnableOption "Zeronorth Projects gitconfig"; + + email = mkOption { + type = types.str; + default = "alice@work.example.com"; + description = "Email for zeronorth projects"; + }; + + signingKey = mkOption { + type = types.str; + description = "SSH signing key for zeronorth projects"; + example = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI..."; + }; + }; + }; + + config = mkIf cfg.enable { + home.file = mkMerge [ + # Personal Projects gitconfig (~/Projects/.gitconfig) + # Used on personal machine for all projects under ~/Projects/ + (mkIf cfg.personal.enable { + "Projects/.gitconfig".text = mkGitConfig { + email = cfg.personal.email; + signingKey = cfg.personal.signingKey; + urlRewrites = { + "ssh://git@ssh-gitea.olsen.cloud:2205/" = "https://gitea.olsen.cloud/"; + "git@github-private:" = "https://github.com/"; + }; + }; + }) + + # Private Projects gitconfig (~/Projects/private/.gitconfig) + # Used on work machine for personal projects under ~/Projects/private/ + (mkIf cfg.private.enable { + "Projects/private/.gitconfig".text = mkGitConfig { + email = cfg.private.email; + signingKey = cfg.private.signingKey; + urlRewrites = { + "ssh://git@ssh-gitea.olsen.cloud:2205/" = "https://gitea.olsen.cloud/"; + "git@github-private:" = "https://github.com/"; + }; + }; + }) + + # Zeronorth Projects gitconfig (~/Projects/zeronorth/.gitconfig) + # Used on work machine for work projects under ~/Projects/zeronorth/ + (mkIf cfg.zeronorth.enable { + "Projects/zeronorth/.gitconfig".text = mkGitConfig { + email = cfg.zeronorth.email; + signingKey = cfg.zeronorth.signingKey; + urlRewrites = { + "git@github-zeronorth:" = "https://github.com/"; + }; + }; + }) + ]; + }; +} \ No newline at end of file diff --git a/modules/home/git.nix b/modules/home/git.nix new file mode 100644 index 0000000..f55f939 --- /dev/null +++ b/modules/home/git.nix @@ -0,0 +1,172 @@ +# Git module +# +# This module configures Git with Home Manager's programs.git options. +# It supports both personal and work profiles with different signing keys +# and includeIf paths for project-specific configurations. +# +# Based on the chezmoi configuration from dot_gitconfig.tmpl +{ + config, + pkgs, + lib, + ... +}: +with lib; let + cfg = config.modules.git; +in { + options.modules.git = { + enable = mkEnableOption "Git configuration"; + + userName = mkOption { + type = types.str; + default = "Morten Olsen"; + description = "Git user name"; + }; + + userEmail = mkOption { + type = types.str; + description = "Git user email address"; + example = "alice@personal.example.com"; + }; + + signingKey = mkOption { + type = types.str; + description = "SSH signing key (public key content)"; + example = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI..."; + }; + + includes = mkOption { + type = types.listOf (types.submodule { + options = { + condition = mkOption { + type = types.str; + description = "The includeIf condition (e.g., gitdir:~/Projects/)"; + example = "gitdir:~/Projects/"; + }; + path = mkOption { + type = types.str; + description = "Path to the included gitconfig file"; + example = "~/Projects/.gitconfig"; + }; + }; + }); + default = []; + description = "List of conditional includes for project-specific git configurations"; + }; + }; + + config = mkIf cfg.enable { + # Global gitignore file + home.file.".gitignore_global".text = '' + .envrc + *._local.* + .DS_Store + *.tsbuildinfo + .env + ''; + + # Delta pager for better diffs (separate program in newer Home Manager) + programs.delta = { + enable = true; + enableGitIntegration = true; + options = { + navigate = true; + light = false; + side-by-side = true; + line-numbers = true; + }; + }; + + programs.git = { + enable = true; + + # User configuration + userName = cfg.userName; + userEmail = cfg.userEmail; + + # Signing configuration with 1Password + signing = { + key = cfg.signingKey; + signByDefault = true; + }; + + # Extra configuration + extraConfig = { + # Core settings (pager is set by programs.delta) + core = { + hooksPath = "/dev/null"; + excludesfile = "~/.gitignore_global"; + }; + + # Pull settings + pull = { + ff = "only"; + }; + + # Init settings + init = { + defaultBranch = "main"; + }; + + # Push settings + push = { + autoSetupRemote = true; + }; + + # GPG/SSH signing settings + gpg = { + format = "ssh"; + }; + + "gpg \"ssh\"" = { + program = "/Applications/1Password.app/Contents/MacOS/op-ssh-sign"; + }; + + # Commit settings (gpgsign is handled by signing.signByDefault) + commit = { + gpgsign = true; + }; + + # URL rewrites + "url \"https://\"" = { + insteadOf = "git://"; + }; + + # Git LFS + filter.lfs = { + clean = "git-lfs clean -- %f"; + smudge = "git-lfs smudge -- %f"; + process = "git-lfs filter-process"; + required = true; + }; + + # Difftool configuration + "difftool \"nvimdiff\"" = { + cmd = "nvim -d \"$LOCAL\" \"$REMOTE\""; + }; + }; + + # Aliases + aliases = { + graph = "log --graph --color --pretty=format:\"%C(yellow)%H%C(green)%d%C(reset)%n%x20%cd%n%x20%cn%C(blue)%x20(%ce)%x20%C(cyan)[gpg:%GK%x20%G?]%C(reset)%n%x20%s%n\""; + ll = "log --oneline"; + st = "status -sb"; + cm = "commit -m"; + append = "commit --amend --no-edit"; + sobmodules = "submodule update --init --recursive"; + df = "difftool -t nvimdiff -y"; + last = "log -1 --stat"; + br = "branch --format='%(HEAD) %(color:yellow)%(refname:short)%(color:reset) - %(contents:subject) %(color:green)(%(committerdate:relative)) [%(authorname)]' --sort=-committerdate"; + brr = "branch --remote --format='%(HEAD) %(color:yellow)%(refname:short)%(color:reset) - %(contents:subject) %(color:green)(%(committerdate:relative)) [%(authorname)]' --sort=-committerdate"; + undo = "reset HEAD~1 --mixed"; + unstage = "reset HEAD --"; + }; + + # Conditional includes for project-specific configurations + includes = map (inc: { + condition = inc.condition; + path = inc.path; + }) cfg.includes; + }; + }; +} \ No newline at end of file diff --git a/modules/home/packages.nix b/modules/home/packages.nix new file mode 100644 index 0000000..06de2af --- /dev/null +++ b/modules/home/packages.nix @@ -0,0 +1,168 @@ +# Packages module +# +# This module manages CLI tools and packages installed via Nix. +# GUI applications (casks) are managed via Homebrew in the darwin modules. +# +# Based on the Brewfile analysis from docs/migration-analysis.md +{ + config, + pkgs, + lib, + ... +}: { + home.packages = with pkgs; [ + # ======================================================================== + # Shell Tools + # ======================================================================== + # Note: zsh, atuin, starship, fzf, zoxide, direnv are configured via + # programs.* in shell.nix for better integration + + # ======================================================================== + # Modern CLI Replacements + # These replace traditional Unix tools with modern alternatives + # ======================================================================== + bat # Modern cat with syntax highlighting (aliased as cat) + eza # Modern ls with icons and git integration (aliased as ls) + fd # Modern find + ripgrep # Modern grep (aliased as grep) + delta # Modern diff with syntax highlighting (aliased as diff) + + # ======================================================================== + # File and Text Utilities + # ======================================================================== + jq # JSON processor + yq # YAML processor + tree # Directory tree viewer + rsync # File synchronization + unzip # Archive extraction + curl # HTTP client + wget # HTTP client + watch # Execute commands periodically + + # ======================================================================== + # Git and Version Control + # ======================================================================== + git # Version control + gh # GitHub CLI + # git-delta is included above as 'delta' + jujutsu # Git-compatible VCS (jj) + lazygit # Terminal UI for git + + # ======================================================================== + # Development Tools + # ======================================================================== + neovim # Text editor + tmux # Terminal multiplexer + + # Languages and Runtimes + nodejs_22 # Node.js LTS (replaces NVM) + deno # JavaScript/TypeScript runtime + rustup # Rust toolchain manager + python313 # Python 3.13 + # pyenv is configured in shell.nix + uv # Fast Python package installer + + # Build Tools + gnumake # Make build tool + cmake # Cross-platform build system + + # ======================================================================== + # Container and Kubernetes Tools + # ======================================================================== + docker # Container runtime (CLI only, daemon via colima) + docker-buildx # Docker build extensions + docker-compose # Multi-container Docker applications + colima # Container runtime for macOS (replaces Docker Desktop) + + # Kubernetes + kubectl # Kubernetes CLI + kubernetes-helm # Kubernetes package manager + helmfile # Declarative Helm chart management + k9s # Kubernetes TUI + istioctl # Istio service mesh CLI + fluxcd # GitOps toolkit + + # ======================================================================== + # Infrastructure and Cloud Tools + # ======================================================================== + terraform # Infrastructure as code + ansible # Configuration management + sshpass # Non-interactive SSH password auth (for ansible) + awscli2 # AWS CLI v2 + + # ======================================================================== + # Media Tools + # ======================================================================== + ffmpeg # Media processing + + # ======================================================================== + # Security Tools + # ======================================================================== + gnupg # GPG encryption + age # Modern encryption tool + sops # Secrets management + + # ======================================================================== + # Miscellaneous Tools + # ======================================================================== + graphviz # Graph visualization + tree-sitter # Parser generator (for neovim) + htop # Process viewer + ncdu # Disk usage analyzer + tldr # Simplified man pages + + # ======================================================================== + # Nix Tools + # ======================================================================== + nixfmt-rfc-style # Nix code formatter + nil # Nix language server + ]; + + # ======================================================================== + # Additional Program Configurations + # ======================================================================== + + # Note: Git is configured in modules/home/git.nix + # The git module handles all git configuration including delta integration + + # Bat configuration (modern cat replacement) + programs.bat = { + enable = true; + config = { + theme = "Catppuccin-mocha"; + style = "numbers,changes,header"; + }; + }; + + # Eza configuration (modern ls replacement) + programs.eza = { + enable = true; + enableZshIntegration = true; + icons = "auto"; + git = true; + }; + + # Ripgrep configuration + programs.ripgrep = { + enable = true; + arguments = [ + "--smart-case" + "--hidden" + "--glob=!.git/*" + ]; + }; + + # Htop configuration + programs.htop = { + enable = true; + settings = { + show_program_path = false; + tree_view = true; + }; + }; + + # Lazygit configuration + programs.lazygit = { + enable = true; + }; +} diff --git a/modules/home/shell.nix b/modules/home/shell.nix new file mode 100644 index 0000000..640e23c --- /dev/null +++ b/modules/home/shell.nix @@ -0,0 +1,227 @@ +# Shell configuration module +# +# This module configures the shell environment including: +# - Zsh with Home Manager's programs.zsh +# - Starship prompt +# - Atuin for shell history +# - Direnv for directory-specific environments +# - Zoxide for smart directory navigation +# - Shell aliases from the chezmoi configuration +# - Environment variables +# - NVM for Node.js version management +{ + config, + pkgs, + lib, + ... +}: { + # ========================================================================== + # Zsh Configuration + # ========================================================================== + programs.zsh = { + enable = true; + + # Enable zsh completions + enableCompletion = true; + + # Enable syntax highlighting + syntaxHighlighting.enable = true; + + # Enable autosuggestions + autosuggestion.enable = true; + + # History settings + history = { + size = 50000; + save = 50000; + ignoreDups = true; + ignoreAllDups = true; + ignoreSpace = true; + share = true; + }; + + # Session variables (migrated from dot_zshrc and 01-env.sh) + sessionVariables = { + # XDG runtime directory + XDG_RUNTIME_DIR = "\${XDG_RUNTIME_DIR:-$HOME/.cache}"; + + # Locale + LANG = "en_US.UTF-8"; + + # Temporary directory + TMPDIR = "\${TMPDIR:-/tmp}"; + + # Editor (from 01-nvim.sh) + EDITOR = "nvim"; + + # GPG TTY for SSH sessions + GPG_TTY = "$(tty)"; + + # FZF Catppuccin color scheme (from dot_zshrc) + FZF_DEFAULT_OPTS = lib.concatStringsSep " " [ + "--color=bg+:#313244,bg:#1e1e2e,spinner:#f5e0dc,hl:#f38ba8" + "--color=fg:#cdd6f4,header:#f38ba8,info:#cba6f7,pointer:#f5e0dc" + "--color=marker:#f5e0dc,fg+:#cdd6f4,prompt:#cba6f7,hl+:#f38ba8" + ]; + + # NVM directory + NVM_DIR = "$HOME/.nvm"; + }; + + # Shell aliases (migrated from 01-env.sh and 01-nvim.sh) + shellAliases = { + # Modern CLI replacements (from 01-env.sh) + ls = "eza"; + cat = "bat"; + grep = "rg"; + diff = "delta"; + less = "bat"; + + # Neovim alias (from 01-nvim.sh) + vim = "nvim"; + + # Git root navigation (from 01-env.sh) + gr = "if [ \"`git rev-parse --show-cdup`\" != \"\" ]; then cd `git rev-parse --show-cdup`; fi"; + }; + + # Additional initialization (initExtra) + # This runs after the shell is initialized + initContent = '' + # Source custom env file if it exists + [ -f ~/.env ] && source ~/.env + + # NVM initialization + # Load NVM if installed via Homebrew + if [ -s "/opt/homebrew/opt/nvm/nvm.sh" ]; then + source "/opt/homebrew/opt/nvm/nvm.sh" + fi + # Load NVM bash completion + if [ -s "/opt/homebrew/opt/nvm/etc/bash_completion.d/nvm" ]; then + source "/opt/homebrew/opt/nvm/etc/bash_completion.d/nvm" + fi + + # Pinentry configuration for SSH sessions + if [[ -n "$SSH_CONNECTION" ]]; then + export PINENTRY_USER_DATA="USE_CURSES=1" + fi + + # Welcome banner (from dot_zshrc) + # Only show if terminal is wide enough + if [ `tput cols` -gt "70" ]; then + function PRINT_CENTER { + COLS=`tput cols` + OFFSET=$(( ($COLS - $1) / 2 )) + PRE="" + for i in `seq $OFFSET`; do + PRE="$PRE " + done + while IFS= read -r line; do + echo "$PRE$line" + done <<< "$2" + } + PRINT_CENTER 60 " + + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ + █░▄▄▀█▀▄▄▀█░▄▄░█▄░▄█▀▄▀██░▄░██░██░█░▄▄█░▄▄░█ + █░▀▀▄█░██░█░▀▄░██░██░█▀█░▀▀░▀█░██░█▄▄▀███▄██ + █▄█▄▄██▄▄██░▀▀░██▄███▄█████░███▄▄▄█▄▄▄█░▀▀░█ + ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ + + ⣴⣶⣤⡤⠦⣤⣀⣤⠆ ⣈⣭⣿⣶⣿⣦⣼⣆ + ⠉⠻⢿⣿⠿⣿⣿⣶⣦⠤⠄⡠⢾⣿⣿⡿⠋⠉⠉⠻⣿⣿⡛⣦ + ⠈⢿⣿⣟⠦ ⣾⣿⣿⣷ ⠻⠿⢿⣿⣧⣄ + ⣸⣿⣿⢧ ⢻⠻⣿⣿⣷⣄⣀⠄⠢⣀⡀⠈⠙⠿⠄ + ⢠⣿⣿⣿⠈ ⣻⣿⣿⣿⣿⣿⣿⣿⣛⣳⣤⣀⣀ + ⢠⣧⣶⣥⡤⢄ ⣸⣿⣿⠘ ⢀⣴⣿⣿⡿⠛⣿⣿⣧⠈⢿⠿⠟⠛⠻⠿⠄ + ⣰⣿⣿⠛⠻⣿⣿⡦⢹⣿⣷ ⢊⣿⣿⡏ ⢸⣿⣿⡇ ⢀⣠⣄⣾⠄ + ⣠⣿⠿⠛ ⢀⣿⣿⣷⠘⢿⣿⣦⡀ ⢸⢿⣿⣿⣄ ⣸⣿⣿⡇⣪⣿⡿⠿⣿⣷⡄ + ⠙⠃ ⣼⣿⡟ ⠈⠻⣿⣿⣦⣌⡇⠻⣿⣿⣷⣿⣿⣿ ⣿⣿⡇ ⠛⠻⢷⣄ + ⢻⣿⣿⣄ ⠈⠻⣿⣿⣿⣷⣿⣿⣿⣿⣿⡟ ⠫⢿⣿⡆ + ⠻⣿⣿⣿⣿⣶⣶⣾⣿⣿⣿⣿⣿⣿⣿⣿⡟⢀⣀⣤⣾⡿⠃ + ▄▄▄ . ▌ ▐·▪ ▄▄▌ ▄▄▌ ▐ ▄▌▪ ▄▄▄▄▄ ▄ .▄ ▄• ▄▌▄▄▄▄▄ + ▀▄.▀·▪█·█▌██ ██• ██· █▌▐███ •██ ██▪▐█ ▄█▀▄ █▪██▌•██ + ▐▀▀▪▄▐█▐█•▐█·██ ▪ ██▪▐█▐▐▌▐█· ▐█.▪██▀▀█▐█▌.▐▌█▌▐█▌ ▐█.▪ + ▐█▄▄▌ ███ ▐█▌▐█▌ ▄ ▐█▌██▐█▌▐█▌ ▐█▌·██▌▐▀▐█▌.▐▌▐█▄█▌ ▐█▌· + ▀▀▀ . ▀ ▀▀▀.▀▀▀ ▀▀▀▀ ▀▪▀▀▀ ▀▀▀ ▀▀▀ · ▀█▄▀▪ ▀▀▀ ▀▀▀ + + > welcome x_x + " + fi + ''; + + # Profile extra (runs in .zprofile) + profileExtra = '' + # Add Rust/Cargo to PATH + export PATH="/opt/homebrew/opt/rustup/bin:$PATH:$HOME/.cargo/bin" + ''; + }; + + # ========================================================================== + # Starship Prompt + # ========================================================================== + programs.starship = { + enable = true; + enableZshIntegration = true; + # Starship configuration can be customized here + # settings = { }; + }; + + # ========================================================================== + # Atuin - Shell History + # Migrated from dot_config/atuin/config.toml + # ========================================================================== + # programs.atuin = { + # enable = true; + # enableZshIntegration = true; + + # settings = { + # force = true; + # # Compact style for history display + # style = "compact"; + + # # Vim keybindings in normal mode + # keymap_mode = "vim-normal"; + # }; + # }; + + # ========================================================================== + # Direnv - Directory-specific environments + # ========================================================================== + programs.direnv = { + enable = true; + enableZshIntegration = true; + + # Enable nix-direnv for better Nix integration + nix-direnv.enable = true; + }; + + # ========================================================================== + # Zoxide - Smart directory navigation + # ========================================================================== + programs.zoxide = { + enable = true; + enableZshIntegration = true; + }; + + # ========================================================================== + # FZF - Fuzzy finder + # ========================================================================== + programs.fzf = { + enable = true; + enableZshIntegration = true; + }; + + # ========================================================================== + # NVM - Node Version Manager + # ========================================================================== + # NVM is installed via Homebrew and sourced in the shell + # This allows managing multiple Node.js versions per project + + # ========================================================================== + # Pyenv - Python version management + # ========================================================================== + programs.pyenv = { + enable = true; + enableZshIntegration = true; + }; +} \ No newline at end of file diff --git a/modules/home/ssh.nix b/modules/home/ssh.nix new file mode 100644 index 0000000..7c1feef --- /dev/null +++ b/modules/home/ssh.nix @@ -0,0 +1,152 @@ +# SSH Configuration Module +# +# This module configures SSH with 1Password agent integration and +# profile-specific host configurations for GitHub and other services. +{ + config, + pkgs, + lib, + ... +}: +with lib; let + cfg = config.modules.ssh; +in { + options.modules.ssh = { + enable = mkEnableOption "SSH configuration"; + + # ========================================================================== + # Host Configuration Options + # ========================================================================== + enableGitHubPrivate = mkOption { + type = types.bool; + default = false; + description = "Enable github-private host configuration"; + }; + + enableGitHubZeronorth = mkOption { + type = types.bool; + default = false; + description = "Enable github-zeronorth host configuration"; + }; + + enableCoder = mkOption { + type = types.bool; + default = false; + description = "Enable Coder SSH host configuration"; + }; + + enableGiteaPrivate = mkOption { + type = types.bool; + default = false; + description = "Enable gitea-ssh.olsen.cloud host configuration"; + }; + + # ========================================================================== + # Key Configuration + # ========================================================================== + githubPrivateKeyPath = mkOption { + type = types.str; + default = "~/.ssh/keys/github-private.pub"; + description = "Path to the GitHub private identity file"; + }; + + githubZeronorthKeyPath = mkOption { + type = types.str; + default = "~/.ssh/keys/github-zeronorth.pub"; + description = "Path to the GitHub Zeronorth identity file"; + }; + }; + + config = mkIf cfg.enable { + programs.ssh = { + enable = true; + + # Disable default config to avoid deprecation warnings + enableDefaultConfig = false; + + # Include colima SSH config for container access + includes = ["~/.colima/ssh_config"]; + + # 1Password SSH agent integration (macOS) + extraConfig = '' + IdentityAgent "~/Library/Group Containers/2BUA8C4S2C.com.1password/t/agent.sock" + ''; + + # ========================================================================== + # Host Configurations + # ========================================================================== + matchBlocks = { + # Default settings for all hosts (replaces top-level options) + "*" = { + # Control master for connection sharing + controlMaster = "auto"; + controlPath = "/tmp/ssh-%r@%h:%p"; + controlPersist = "10m"; + # Forward SSH agent + forwardAgent = true; + }; + + # Default GitHub host (always enabled) + "github.com" = { + hostname = "ssh.github.com"; + user = "git"; + port = 443; + }; + + # GitHub private host (for personal projects) + "github-private" = mkIf cfg.enableGitHubPrivate { + hostname = "ssh.github.com"; + user = "git"; + port = 443; + identityFile = cfg.githubPrivateKeyPath; + identitiesOnly = true; + }; + + # Gitea private host (for personal self-hosted git) + "gitea-ssh.olsen.cloud" = mkIf cfg.enableGiteaPrivate { + hostname = "gitea-ssh.olsen.cloud"; + user = "git"; + port = 2202; + identityFile = cfg.githubPrivateKeyPath; + identitiesOnly = true; + }; + + # GitHub Zeronorth host (for work projects) + "github-zeronorth" = mkIf cfg.enableGitHubZeronorth { + hostname = "ssh.github.com"; + user = "git"; + port = 443; + identityFile = cfg.githubZeronorthKeyPath; + identitiesOnly = true; + }; + + # Coder hosts (for remote development environments) + "coder.*" = mkIf cfg.enableCoder { + extraOptions = { + ConnectTimeout = "0"; + StrictHostKeyChecking = "no"; + UserKnownHostsFile = "/dev/null"; + LogLevel = "ERROR"; + }; + proxyCommand = ''/opt/homebrew/bin/coder --global-config "~/Library/Application Support/coderv2" ssh --stdio --ssh-host-prefix coder. %h''; + }; + + "*.coder" = mkIf cfg.enableCoder { + extraOptions = { + ConnectTimeout = "0"; + StrictHostKeyChecking = "no"; + UserKnownHostsFile = "/dev/null"; + LogLevel = "ERROR"; + }; + }; + }; + }; + + # ========================================================================== + # SSH Key Files + # ========================================================================== + # Note: The actual private keys are managed by 1Password. + # These public key files are used for IdentityFile references. + # The public keys tell SSH which key to request from the 1Password agent. + }; +} \ No newline at end of file diff --git a/modules/home/tmux.nix b/modules/home/tmux.nix new file mode 100644 index 0000000..b996496 --- /dev/null +++ b/modules/home/tmux.nix @@ -0,0 +1,121 @@ +# Tmux configuration module +# +# Migrated from chezmoi dot_tmux.conf +# Uses Home Manager's programs.tmux for declarative configuration +{ + config, + pkgs, + lib, + ... +}: { + programs.tmux = { + enable = true; + + # Basic settings + mouse = true; + baseIndex = 1; + keyMode = "vi"; + escapeTime = 0; # From tmux-sensible + historyLimit = 50000; # From tmux-sensible + + # Terminal settings + terminal = "screen-256color"; + + # Plugins managed by Home Manager (replaces TPM) + plugins = with pkgs.tmuxPlugins; [ + sensible + yank + { + plugin = power-theme; + # Note: tmux-power theme is applied automatically + } + ]; + + # Extra configuration that can't be expressed via options + extraConfig = '' + # Terminal overrides for true color support + set -ag terminal-overrides ",xterm-256color:RGB" + + # Window settings + set -g renumber-windows on + set-option -g allow-rename off + + # Key bindings + bind q lock-client + + # Unbind defaults we're replacing + unbind '"' + unbind % + + # Vim-style pane navigation + bind h select-pane -L + bind j select-pane -D + bind k select-pane -U + bind l select-pane -R + + # Pane resizing with repeat + bind -r H resize-pane -L 10 + bind -r J resize-pane -D 10 + bind -r K resize-pane -U 10 + bind -r L resize-pane -R 10 + + # Attach to current path + bind o attach -c "#{pane_current_path}" + + # Open pane in vim + bind-key / capture-pane -S -102400 -J \; new-window 'vim -c ":read !tmux save-buffer - ; tmux delete-buffer;" -c ":normal gg" -c ":set buftype=nofile" -c ":silent! ChompWhitespace"' + + # Catppuccin theme settings (kept for reference if switching themes) + set -g @catppuccin_date_time "%Y-%m-%d %H:%M" + set -g @catppuccin_window_tabs_enabled "on" + + # Copy mode settings (vi mode is set via keyMode option) + bind Escape copy-mode + unbind [ + unbind p + bind p paste-buffer + bind -Tcopy-mode-vi v send -X begin-selection + bind -Tcopy-mode-vi y send -X copy-selection + bind -Tcopy-mode-vi Escape send -X cancel + + # Pane splitting (using current path) + bind - split-window -v -c "#{pane_current_path}" + bind | split-window -h -c "#{pane_current_path}" + bind C-z resize-pane -Z + bind g swap-pane -U + bind æ swap-pane -D + + # Smart pane switching with awareness of Vim splits + # See: https://github.com/christoomey/vim-tmux-navigator + is_vim="ps -o state= -o comm= -t '#{pane_tty}' \ + | grep -iqE '^[^TXZ ]+ +(\\S+\\/)?g?(view|n?vim?x?)(diff)?$'" + bind-key -n 'C-h' if-shell "$is_vim" 'send-keys C-h' 'select-pane -L' + bind-key -n 'C-j' if-shell "$is_vim" 'send-keys C-j' 'select-pane -D' + bind-key -n 'C-k' if-shell "$is_vim" 'send-keys C-k' 'select-pane -U' + bind-key -n 'C-l' if-shell "$is_vim" 'send-keys C-l' 'select-pane -R' + + # Alt + hjkl for resizing (with vim awareness) + bind -n 'M-h' if-shell "$is_vim" 'send-keys M-h' 'resize-pane -L 1' + bind -n 'M-j' if-shell "$is_vim" 'send-keys M-j' 'resize-pane -D 1' + bind -n 'M-k' if-shell "$is_vim" 'send-keys M-k' 'resize-pane -U 1' + bind -n 'M-l' if-shell "$is_vim" 'send-keys M-l' 'resize-pane -R 1' + + # Version-specific bindings for C-\ + tmux_version='$(tmux -V | sed -En "s/^tmux ([0-9]+(.[0-9]+)?).*/\1/p")' + if-shell -b '[ "$(echo "$tmux_version < 3.0" | bc)" = 1 ]' \ + "bind-key -n 'C-\\' if-shell \"$is_vim\" 'send-keys C-\\' 'select-pane -l'" + if-shell -b '[ "$(echo "$tmux_version >= 3.0" | bc)" = 1 ]' \ + "bind-key -n 'C-\\' if-shell \"$is_vim\" 'send-keys C-\\\\' 'select-pane -l'" + + # Copy mode pane switching + bind-key -T copy-mode-vi 'C-h' select-pane -L + bind-key -T copy-mode-vi 'C-j' select-pane -D + bind-key -T copy-mode-vi 'C-k' select-pane -U + bind-key -T copy-mode-vi 'C-l' select-pane -R + bind-key -T copy-mode-vi 'C-\' select-pane -l + + # Utils - lazygit popup + bind -r g display-popup -d '#{pane_current_path}' -w80% -h80% -E lazygit + ''; + }; +} \ No newline at end of file