1288 lines
37 KiB
Markdown
1288 lines
37 KiB
Markdown
|
|
# 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. |