Files
nix-los/ARCHITECTURE.md

391 lines
12 KiB
Markdown
Raw Permalink Normal View History

2026-04-07 02:34:03 +02:00
# Architecture & Design Principles
An overview of how your portable NixOS configuration is structured and why.
## Core Design Goals
1. **Portability**: Deploy to new machines, existing NixOS, or non-NixOS with minimal effort
2. **Modularity**: Enable/disable features independently without tight coupling
3. **Reproducibility**: Flakes + lock files ensure identical environments across machines
4. **Maintainability**: Clear separation of concerns (system vs home, shared vs per-machine)
5. **Secrets Management**: Encrypted, portable secrets with sops-nix
6. **Scalability**: Easy to add new machines and modules
## Architecture Overview
```
nix-los/
├── flake.nix # Single source of truth for all configurations
│ # Defines: inputs, outputs, configurations
├── hosts/ # Per-machine specific overrides
│ ├── laptop/ # Machine-specific hardware, features
│ └── server/ # (template for new machines)
├── nixos/ # System-level modules (NixOS only)
│ ├── default.nix # Imports all modules
│ └── modules/ # Toggleable features
│ ├── system.nix # Users, sudo, locale
│ ├── development.nix # Languages, tools
│ ├── shell.nix # Shell config
│ └── secrets-example.nix (inactive)
├── home/ # User-level configuration (portable)
│ ├── default.nix # Imports all modules, can run standalone
│ └── modules/ # User-level features
│ ├── shell.nix # Zsh, direnv, starship
│ ├── editor.nix # Neovim, VSCode
│ ├── git.nix # Git configuration
│ └── dev-tools.nix # Tmux, utilities
├── secrets/ # Encrypted credentials
│ ├── .sops.yaml # Encryption keys (DO NOT COMMIT UNENCRYPTED)
│ └── secrets.yaml # Encrypted secrets
├── flake.lock # Pinned versions (COMMIT THIS)
└── docs/
├── README.md # Getting started
├── SETUP.md # Detailed setup instructions
├── QUICKREF.md # Command reference
├── CUSTOMIZATION.md # Extension patterns
└── ARCHITECTURE.md # This file
```
## Data Flow
### On a NixOS System
```
┌─────────────────────────────────────────┐
│ flake.nix (configuration) │
│ - Defines all inputs (nixpkgs, etc) │
│ - Specifies nixosConfigurations │
└──────────────────┬──────────────────────┘
┌──────────┴──────────┬─────────────────┐
│ │ │
┌────▼────┐ ┌─────▼──────┐ ┌─────▼──────┐
│ hosts/* │ │ nixos/* │ │ home/* │
│(device) │ ──────► │(system cfg)│ │(user cfg) │
└────┬────┘ └─────┬──────┘ └─────┬──────┘
│ │ │
└────────────────────┼─────────────────┘
┌────────────▼──────────────┐
│ nixos-rebuild switch │
│ (applies both system+home)│
└───────────┬────────────────┘
┌───────────────────┴───────────────────┐
│ │
┌────▼──────────────┐ ┌──────────────▼─────┐
│ NixOS System │ │ Home Manager │
│ /etc/nixos/* │ │ ~/.config/* │
│ Services, kernel │ │ Packages, dotfiles │
└───────────────────┘ └────────────────────┘
```
### On Non-NixOS System
```
┌──────────────────────────────────────┐
│ flake.nix (homeConfigurations only) │
└──────────────────┬───────────────────┘
┌──────────▼──────────────┐
│ home/* (user config) │
└──────────┬───────────────┘
┌──────────▼──────────────────┐
│ home-manager switch --flake │
└──────────┬───────────────────┘
┌──────────▼──────────────────┐
│ Home Manager │
│ ~/.config, ~/.local/share │
│ Packages, dotfiles │
└────────────────────────────┘
```
## Module Design Pattern
Each module follows this pattern for maximum flexibility:
```nix
{ config, lib, pkgs, ... }:
{
# 1. Define options (schema)
options.custom.feature = {
enable = lib.mkEnableOption "Feature";
setting1 = lib.mkOption { /* ... */ };
};
# 2. Conditionally implement
config = let
cfg = config.custom.feature;
in lib.mkIf cfg.enable {
# Implementation here
environment.systemPackages = [ /* ... */ ];
};
}
```
**Why this pattern:**
-**Declarative**: Clear what can be configured
-**Composable**: Modules don't interfere
-**Overridable**: Host can override any setting
-**Conditional**: Only included when enabled
## Separation of Concerns
### System Level (nixos/) - Run once, affects all users
- OS-level packages (compilers, tools)
- Services (SSH, web servers)
- Bootloader, kernel, hardware
- User creation and permissions
- Firewall, networking
- System-wide environment variables
**When to put config here:**
- Affects the entire system
- Required by multiple users
- Needs root/sudo privileges
### User Level (home/) - Per-user, portable
- Shell configuration and aliases
- Editor settings and plugins
- User-installed packages
- Dotfiles (~/.config/*)
- Environment variables (user-specific)
- Git, SSH client configuration
**When to put config here:**
- Only one user needs it
- Doesn't require system privileges
- Configurable per-user
- Want to port to non-NixOS systems
## Configuration Hierarchy
Settings are applied in this order (later overrides earlier):
1. **nixos/default.nix** - Base system defaults
2. **nixos/modules/*.nix** - System feature modules
3. **hosts/hostname/default.nix** - Machine-specific overrides
4. **home/default.nix** - User base defaults
5. **home/modules/*.nix** - User feature modules
Example with `custom.development.languages`:
```nix
# Start: undefined
# nixos/modules/development.nix:
# options.custom.development.languages = lib.mkOption { default = []; ... };
# hosts/laptop/default.nix:
custom.development.languages = [ "rust" "python" ]; # Override
# Result: [ "rust" "python" ]
```
## Flakes Architecture
### Inputs (Dependencies)
```nix
inputs = {
nixpkgs = "..."; # Stable packages
nixpkgs-unstable = "..."; # Cutting-edge packages
home-manager = "..."; # User config management
sops-nix = "..."; # Secrets management
disko = "..."; # Disk partitioning
};
```
**Why these inputs:**
- **nixpkgs + unstable**: Mix stable (secure) with latest (features)
- **home-manager**: User config separate from system
- **sops-nix**: Encrypted secrets, portable
- **disko**: Declarative disk setup for new machines
### Outputs (What's available)
```nix
outputs = {
nixosConfigurations = {
laptop = nixosSystem { ... }; # Full system config
server = nixosSystem { ... };
};
homeConfigurations = {
"myuser@linux" = homeManagerConfiguration { ... }; # Standalone HM
};
devShells = {
default = mkShell { ... }; # Development environment
};
apps = {
installer = { ... }; # Bootstrap helper
};
}
```
## Secrets Flow
```
secrets/
├── .sops.yaml # Key configuration (WHO can decrypt)
└── secrets.yaml (encrypted)
├─ SSH keys
├─ API tokens
└─ Passwords
When applied:
Sops decrypts (using ~/.config/sops/age/keys.txt)
sops.secrets.* paths available in Nix
Placed in /run/secrets/* at boot
Referenced in system/home config
```
**Key insight**: Secrets are encrypted on disk, decrypted at boot, never stored in nix store in plaintext.
## Multi-Machine Scaling
To support N machines:
1. Add new host in `hosts/newhost/default.nix`
2. Register in `flake.nix` under `nixosConfigurations.newhost`
3. Deploy with: `sudo nixos-rebuild switch --flake .#newhost`
Each machine:
- Shares `nixos/` modules
- Shares `home/` modules
- Has unique `hosts/` overrides
- Uses same `secrets.yaml` (all machines can decrypt)
```
Common Base Per-Machine Override
───────────── ──────────────────
nixos/default.nix → hosts/laptop/default.nix
nixos/modules/ → (machine-specific features)
home/modules/ → (shared everywhere)
```
## Update Strategy
```
Stable ← └─ Pinned via flake.lock
├─ Low risk, predictable
└─ Most system components
Unstable → Override in modules when needed
├─ Fast-moving
└─ For specific packages (bleeding edge editor, tools)
```
Update process:
```bash
# Pin current state
git commit flake.lock
# Update specific input
nix flake update nixpkgs
# Test
sudo nixos-rebuild test --flake .#laptop
# Deploy
sudo nixos-rebuild switch --flake .#laptop
# Save new state
git commit flake.lock
```
## Extension Points
### Add Feature: Create a new module
1. Create `nixos/modules/myfeature.nix` or `home/modules/myfeature.nix`
2. Follow module pattern (options + config)
3. Import in `nixos/default.nix` or `home/default.nix`
4. Enable in host config: `custom.myfeature.enable = true`
### Add Machine: Copy host template
1. Create `hosts/newmachine/default.nix`
2. Customize for that machine
3. Add to `flake.nix` under `nixosConfigurations`
4. Deploy with `sudo nixos-rebuild switch --flake .#newmachine`
### Add Dependency: Update flake inputs
1. Edit `flake.nix` inputs section
2. Run `nix flake update`
3. Commit `flake.lock`
### Add Secret: Edit secrets.yaml
1. Run `sops secrets/secrets.yaml`
2. Add key-value pair
3. Reference in module with `config.sops.secrets."key".path`
## Testing & Safety
### Before Deploying
```bash
nix flake check # Syntax validation
nix flake show # Check all outputs
sudo nixos-rebuild test # Build without activating
```
### Rollback if Issues
```bash
# NixOS (automatic previous generation)
sudo nixos-rebuild --rollback switch
# Home Manager (manual)
home-manager switch --flake .#myuser@linux ~/.local/state/home-manager/previous
```
### Validate Changes
```bash
# What changed?
sudo nixos-rebuild diff --flake .#laptop
# What will happen?
sudo nixos-rebuild test --flake .#laptop
# Apply
sudo nixos-rebuild switch --flake .#laptop
```
## Performance Considerations
- **Flakes**: Slow evaluation (1-2 seconds) but reproducible
- **Modularity**: More files but faster edits
- **Secrets**: Decryption only at boot (fast)
- **Unstable**: More binary cache misses (compilation time)
## See Also
- **README.md** - Getting started
- **SETUP.md** - Step-by-step installation
- **QUICKREF.md** - Common commands
- **CUSTOMIZATION.md** - Extension patterns