This commit is contained in:
Morten Olsen
2025-12-15 11:42:42 +01:00
committed by Morten Olsen
commit f42a092b60
21 changed files with 4695 additions and 0 deletions

177
modules/darwin/homebrew.nix Normal file
View File

@@ -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 []
);
};
};
}

172
modules/home/apps.nix Normal file
View File

@@ -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}
'';
})
];
};
}

140
modules/home/git-files.nix Normal file
View File

@@ -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/";
};
};
})
];
};
}

172
modules/home/git.nix Normal file
View File

@@ -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;
};
};
}

168
modules/home/packages.nix Normal file
View File

@@ -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;
};
}

227
modules/home/shell.nix Normal file
View File

@@ -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;
};
}

152
modules/home/ssh.nix Normal file
View File

@@ -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.
};
}

121
modules/home/tmux.nix Normal file
View File

@@ -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
'';
};
}