diff --git a/docs/architecture.md b/docs/architecture.md index ffc3450..25b2913 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -4,101 +4,178 @@ This document provides an overview of the repository architecture, explaining ho ## Overview -This NixOS configuration repository is built using [Nix Flakes](https://nixos.wiki/wiki/Flakes) and [Snowfall Lib](https://github.com/snowfallorg/lib) to provide a modular, maintainable configuration for multiple systems. +This NixOS configuration repository is built using [Nix Flakes](https://nixos.wiki/wiki/Flakes) and [Snowfall Lib](https://github.com/snowfallorg/lib) to provide a modular, maintainable configuration for multiple systems. The Snowfall namespace is `mjallen`, so all custom options are accessed as `mjallen..`. ## Directory Structure ``` . -├── checks/ # Pre-commit hooks and other checks -├── flake.nix # Main flake configuration -├── homes/ # Home-manager configurations for users -│ ├── aarch64-darwin/ # macOS home configurations -│ ├── aarch64-linux/ # ARM Linux home configurations -│ └── x86_64-linux/ # x86 Linux home configurations +├── flake.nix # Main flake — inputs, outputs, Snowfall config +├── flake.lock # Locked dependency versions +├── .sops.yaml # SOPS key management rules +├── treefmt.nix # Code formatter configuration +├── qemu.nix # QEMU VM testing config +│ +├── checks/ # Pre-commit hooks and CI checks +│ +├── docs/ # Documentation (this directory) +│ +├── homes/ # Home Manager configurations +│ ├── aarch64-darwin/ # macOS user configs +│ ├── aarch64-linux/ # ARM Linux user configs +│ └── x86_64-linux/ # x86 Linux user configs +│ +├── lib/ # Custom Nix library utilities +│ ├── module/ # mkModule, mkOpt, mkBoolOpt helpers +│ ├── file/ # File/path utilities +│ └── versioning/ # Package version pinning helpers +│ ├── modules/ # Reusable configuration modules -│ ├── home/ # Home-manager modules -│ └── nixos/ # NixOS system modules -│ ├── boot/ # Boot configuration modules -│ ├── desktop/ # Desktop environment modules -│ ├── hardware/ # Hardware-specific modules -│ ├── homeassistant/ # Home Assistant modules -│ ├── network/ # Network configuration modules -│ ├── services/ # Service configuration modules -│ └── ... # Other module categories +│ ├── home/ # Home Manager modules +│ ├── nixos/ # NixOS system modules +│ └── darwin/ # nix-darwin modules (macOS) +│ ├── overlays/ # Nixpkgs overlays +│ ├── packages/ # Custom package definitions -├── secrets/ # Encrypted secrets (managed with sops-nix) -└── systems/ # System-specific configurations - ├── aarch64-darwin/ # macOS system configurations - ├── aarch64-linux/ # ARM Linux system configurations - └── x86_64-linux/ # x86 Linux system configurations - ├── jallen-nas/ # NAS server configuration - ├── matt-nixos/ # Desktop configuration - ├── nuc-nixos/ # NUC configuration - └── ... # Other system configurations +│ +├── secrets/ # SOPS-encrypted secret files +│ +└── systems/ # Per-host system configurations + ├── aarch64-darwin/ # macOS (nix-darwin) hosts + ├── aarch64-linux/ # ARM Linux hosts + ├── x86_64-install-iso/# Install ISO configurations + └── x86_64-linux/ # x86_64 Linux hosts ``` -## Flake Structure +## Flake Inputs -The `flake.nix` file defines the inputs (external dependencies) and outputs (configurations) of this repository: +| Input | Source | Purpose | +|---|---|---| +| `nixpkgs-unstable` | `github:NixOS/nixpkgs/nixos-unstable` | Primary package set | +| `nixpkgs-stable` | `github:NixOS/nixpkgs/nixos-25.11` | Stable package set | +| `nixpkgs-otbr` | `github:mrene/nixpkgs` (fork) | OpenThread Border Router packages | +| `home-manager-unstable` | `github:nix-community/home-manager` | User environment management | +| `home-manager-stable` | `github:nix-community/home-manager/release-25.11` | Stable home-manager | +| `snowfall-lib` | `github:mjallen18/snowfall-lib` | Flake structure library (personal fork) | +| `impermanence` | `github:nix-community/impermanence` | Ephemeral root filesystem support | +| `lanzaboote` | `github:nix-community/lanzaboote/v1.0.0` | Secure Boot | +| `nixos-hardware` | `github:NixOS/nixos-hardware` | Hardware-specific NixOS configs | +| `sops-nix` | `github:Mic92/sops-nix` | Secret management | +| `disko` | `github:nix-community/disko` | Declarative disk partitioning | +| `cosmic` | `github:lilyinstarlight/nixos-cosmic` | COSMIC desktop environment | +| `jovian` | `github:Jovian-Experiments/Jovian-NixOS` | Steam Deck / handheld support | +| `nixos-apple-silicon` | `github:nix-community/nixos-apple-silicon` | Asahi Linux / Apple Silicon | +| `darwin` | `github:nix-darwin/nix-darwin` | macOS system configuration | +| `nix-homebrew` | `github:zhaofengli/nix-homebrew` | Declarative Homebrew (macOS) | +| `stylix` | `github:nix-community/stylix` | System-wide theming | +| `nix-vscode-extensions` | `github:nix-community/nix-vscode-extensions` | VS Code extension packages | +| `authentik-nix` | `github:nix-community/authentik-nix` | Authentik SSO | +| `nix-cachyos-kernel` | `github:xddxdd/nix-cachyos-kernel` | CachyOS optimised kernels | +| `lsfg-vk` | `github:pabloaul/lsfg-vk-flake` | Lossless Scaling frame generation (Linux) | +| `nix-index-database` | `github:nix-community/nix-index-database` | Pre-built nix-index database | +| `steam-rom-manager` | `github:mjallen18/nix-steam-rom-manager` | Steam ROM Manager package | +| `nix-plist-manager` | `github:sushydev/nix-plist-manager` | macOS plist management | +| `nix-rosetta-builder` | `github:cpick/nix-rosetta-builder` | Rosetta build support (macOS) | +| `pre-commit-hooks-nix` | `github:cachix/pre-commit-hooks.nix` | Pre-commit hooks | +| `treefmt-nix` | `github:numtide/treefmt-nix` | Code formatting | -### Inputs - -- **nixpkgs-unstable**: The unstable channel of Nixpkgs -- **nixpkgs-stable**: The stable channel of Nixpkgs (25.11) -- **home-manager**: User environment management -- **snowfall-lib**: Library for structuring flake repositories -- **impermanence**: Persistent state management -- **lanzaboote**: Secure boot implementation -- **nixos-hardware**: Hardware-specific configurations -- **sops-nix**: Secret management -- **disko**: Disk partitioning and formatting -- **And more specialized inputs** - -### Outputs - -The outputs are generated using Snowfall Lib's `mkFlake` function, which automatically discovers and assembles: - -- **NixOS system configurations**: For each system in the `systems/` directory -- **Home Manager configurations**: For each configuration in the `homes/` directory -- **Packages**: From the `packages/` directory -- **Modules**: From the `modules/` directory -- **Overlays**: From the `overlays/` directory +`nixpkgs` and `home-manager` are aliases pointing to the unstable variants. ## Module System -The module system uses a modular approach where: +### Structure -1. **Common modules** are defined in `modules/nixos/` and `modules/home/` -2. **System-specific modules** are defined in `systems///` +All modules follow a standard Snowfall Lib pattern and are automatically discovered. Each module exposes options under the `mjallen` namespace: -Each module follows the NixOS module pattern, with: -- `default.nix`: Main module implementation -- `options.nix`: Option declarations +```nix +# Enable a module +mjallen.services.jellyfin.enable = true; +mjallen.desktop.gnome.enable = true; +mjallen.hardware.amd.enable = true; +``` -## Integration with Snowfall Lib +### `mkModule` helper -Snowfall Lib provides: -1. **Automatic discovery** of modules, overlays, and packages -2. **Consistent structure** across the repository -3. **Common utilities** for working with flakes +Most service modules are built with `lib.mjallen.mkModule` (`lib/module/default.nix`), which provides a standard set of options: + +| Option | Default | Description | +|---|---|---| +| `enable` | `false` | Enable/disable the module | +| `port` | `80` | Service listen port | +| `listenAddress` | `"0.0.0.0"` | Bind address | +| `openFirewall` | `true` | Open firewall ports | +| `configDir` | `/var/lib/` | Config directory | +| `dataDir` | `/var/lib//data` | Data directory | +| `createUser` | `false` | Create a dedicated system user | +| `configureDb` | `false` | Create a PostgreSQL database | +| `environmentFile` | `null` | Path to an env-file | +| `reverseProxy.enable` | `false` | Add a Caddy reverse proxy block | +| `reverseProxy.subdomain` | `` | Caddy subdomain | +| `redis.enable` | `false` | Create a dedicated Redis instance | + +### NixOS modules (`modules/nixos/`) + +| Category | Paths | Description | +|---|---|---| +| Boot | `boot/common/`, `boot/lanzaboote/`, `boot/plymouth/`, `boot/systemd-boot/` | Bootloader configurations | +| Desktop | `desktop/gnome/`, `desktop/hyprland/`, `desktop/cosmic/` | Desktop environments | +| Development | `development/` | Dev tools, language support, containers | +| Hardware | `hardware/amd/`, `hardware/nvidia/`, `hardware/battery/`, `hardware/raspberry-pi/`, `hardware/openrgb/`, ... | Hardware-specific configs | +| Headless | `headless/` | Headless server profile (watchdog, no suspend) | +| Home Assistant | `homeassistant/` | Smart home automation suite | +| Impermanence | `impermanence/` | Ephemeral root + persistent state | +| Monitoring | `monitoring/` | Prometheus/Grafana metrics | +| Network | `network/` | Hostname, firewall, NetworkManager, static IP | +| Power | `power/` | UPS support | +| Programs | `programs/` | System-wide programs (nix-index, gnupg, etc.) | +| Security | `security/common/`, `security/tpm/` | Common hardening, TPM unlock | +| Services | `services//` | ~50 self-hosted service modules (see below) | +| SOPS | `sops/` | Secret management setup | +| System | `system/` | Miscellaneous system settings | +| User | `user/` | User account management | +| Virtualization | `virtualization/` | libvirt, containers | + +### Home Manager modules (`modules/home/`) + +| Category | Paths | Description | +|---|---|---| +| Desktop | `desktop/gnome/`, `desktop/theme/` | GNOME and theming | +| GPG | `gpg/` | GPG agent configuration | +| Programs | `programs/btop/`, `programs/git/`, `programs/zsh/`, `programs/kitty/`, `programs/waybar/`, `programs/hyprland/`, `programs/wofi/`, `programs/mako/`, `programs/wlogout/`, `programs/librewolf/`, `programs/opencode/`, `programs/update-checker/`, ... | User applications | +| Services | `services/pass/` | Password store integration | +| Shell | `shell-aliases/` | Common shell aliases | +| SOPS | `sops/` | User-level secret integration | +| Stylix | `stylix/` | System-wide theming | +| User | `user/` | User environment defaults | ## Secrets Management -Secrets are managed using [sops-nix](https://github.com/Mic92/sops-nix), with: -- Encrypted secret files in the `secrets/` directory -- `.sops.yaml` configuration file in the root -- Key management integrated into the configuration +Secrets are encrypted with [SOPS](https://github.com/getsops/sops) using age keys derived from each machine's SSH host key (`/etc/ssh/ssh_host_ed25519_key`). The `.sops.yaml` file maps secret file path patterns to the set of age recipients that can decrypt them. -## Deployment Process +Each host has its own secrets file: + +| File | Host | +|---|---| +| `secrets/secrets.yaml` | Shared (all hosts) | +| `secrets/nas-secrets.yaml` | jallen-nas | +| `secrets/pi5-secrets.yaml` | pi5 | +| `secrets/allyx-secrets.yaml` | allyx | +| `secrets/nuc-secrets.yaml` | nuc-nixos | +| `secrets/mac-secrets.yaml` | macbook-pro-nixos | +| `secrets/desktop-secrets.yaml` | matt-nixos | + +See the [Secrets Management](../README.md#secrets-management) section of the root README for full details on generating keys and adding secrets. + +## Deployment -Systems are built and deployed using: ```bash -nixos-rebuild switch --flake .#hostname -``` +# NixOS system +sudo nixos-rebuild switch --flake .#hostname -This command: -1. Evaluates the flake for the specified hostname -2. Builds the resulting configuration -3. Activates it on the current system \ No newline at end of file +# macOS (nix-darwin) +darwin-rebuild switch --flake .#hostname + +# Home Manager only +home-manager switch --flake .#username@hostname +``` diff --git a/docs/getting-started.md b/docs/getting-started.md index 68d799c..d6c1cb1 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -6,167 +6,170 @@ This guide will help you get started with this NixOS configuration repository. - Basic knowledge of NixOS and the Nix language - Git installed on your system -- Physical access to the machine you want to configure +- Physical or SSH access to the target machine -## Initial Setup - -### 1. Cloning the Repository - -Clone this repository to your local machine: +## Cloning the Repository ```bash git clone ssh://nix-apps@localhost:2222/mjallen/nix-config.git cd nix-config ``` -### 2. Setting Up a New System +## Installing on a New Machine -#### Option 1: Using an Existing Configuration +### Option 1: Using an existing system configuration -If you're setting up a new machine that should be identical to an existing configuration: +If the machine matches an existing configuration (e.g. reinstalling `jallen-nas`): -1. Boot from a NixOS installation media -2. Mount your target partitions to `/mnt` -3. Clone this repository: +1. Boot from a NixOS installation ISO +2. Partition and mount disks (or use `disko`): ```bash - nixos-enter - cd /mnt - mkdir -p /mnt/etc/nixos - git clone ssh://nix-apps@localhost:2222/mjallen/nix-config.git /mnt/etc/nixos + nix run github:nix-community/disko -- --mode disko /path/to/disko-config.nix ``` -4. Install NixOS with the desired system profile: +3. Clone this repo into the target: + ```bash + mkdir -p /mnt/etc/nixos + git clone /mnt/etc/nixos + ``` +4. Install: ```bash nixos-install --flake /mnt/etc/nixos#hostname ``` - Replace `hostname` with the target system name (e.g., `matt-nixos`, `jallen-nas`, etc.) -#### Option 2: Creating a New System Configuration +### Option 2: Adding a new system configuration -If you're adding a completely new system: - -1. Create a new directory for your system configuration: +1. **Create the system directory** under the appropriate architecture: ```bash - mkdir -p systems/$(uname -m)-linux/new-hostname + mkdir -p systems/x86_64-linux/new-hostname ``` - -2. Create the basic configuration files: - ```bash - cat > systems/$(uname -m)-linux/new-hostname/default.nix << EOF - { lib, pkgs, ... }: + +2. **Write the configuration** — at minimum a `default.nix`: + ```nix + { namespace, ... }: { - imports = [ - ./hardware-configuration.nix - # Add other needed module imports here - ]; - - networking.hostName = "new-hostname"; - - # Add your system-specific configuration here + mjallen = { + sops.enable = true; + network.hostName = "new-hostname"; + user.name = "admin"; + }; } - EOF ``` -3. Generate the hardware configuration: +3. **Generate hardware configuration** (on the target machine): ```bash - nixos-generate-config --no-filesystems --dir systems/$(uname -m)-linux/new-hostname/ + nixos-generate-config --no-filesystems --dir systems/x86_64-linux/new-hostname/ ``` -4. Add your new system to the flake by adding it to the `hosts` section in `flake.nix` +4. **Add SOPS secrets** for the new host — see [Secrets Management](../README.md#secrets-management). -5. Build and install the configuration: +5. **Build and switch**: ```bash sudo nixos-rebuild switch --flake .#new-hostname ``` -## Secret Management +## Day-to-Day Usage -### Setting Up Sops-Nix - -1. Create a GPG key if you don't already have one: - ```bash - gpg --full-generate-key - ``` - -2. Add your key to `.sops.yaml`: - ```bash - # Get your key fingerprint - gpg --list-secret-keys --keyid-format=long - - # Edit the .sops.yaml file to add your key - ``` - -3. Create a new encrypted secret: - ```bash - sops secrets/newsecret.yaml - ``` - -## Common Tasks - -### Updating the Repository +### Applying configuration changes ```bash -git pull -sudo nixos-rebuild switch --flake .#hostname +# On the local machine +sudo nixos-rebuild switch --flake .#$(hostname) + +# On a remote machine +nixos-rebuild switch --flake .#hostname --target-host user@host --use-remote-sudo ``` -### Adding a New Package +### Updating flake inputs -1. For standard packages, add them to your system or home configuration: +```bash +# Update all inputs +nix flake update + +# Update a single input +nix flake lock --update-input nixpkgs + +# Apply after updating +sudo nixos-rebuild switch --flake .#$(hostname) +``` + +### Garbage collection + +```bash +# Remove old generations and unreferenced store paths +sudo nix-collect-garbage -d + +# Keep the last N generations +sudo nix-collect-garbage --delete-older-than 30d +``` + +## Enabling a Module + +Most functionality is exposed through the `mjallen` namespace. To enable a module, set it in the system's `default.nix` (or a relevant sub-file): + +```nix +mjallen = { + desktop.gnome.enable = true; + hardware.amd.enable = true; + gaming.enable = true; + + services.jellyfin = { + enable = true; + port = 8096; + reverseProxy.enable = true; + }; +}; +``` + +See [Custom Modules](./modules/README.md) for the full list of available modules and options. + +## Adding a New Service Module + +1. **Create the module directory**: + ```bash + mkdir -p modules/nixos/services/my-service + ``` + +2. **Write `default.nix`** using the `mkModule` helper: ```nix - environment.systemPackages = with pkgs; [ - new-package - ]; - ``` - -2. For custom packages, add them to the `packages` directory: - ```bash - mkdir -p packages/new-package - # Create the necessary Nix files - ``` - -### Adding a New Module - -1. Create a new module directory: - ```bash - mkdir -p modules/nixos/new-module - ``` - -2. Create the module files: - ```bash - # Create options.nix - cat > modules/nixos/new-module/options.nix << EOF - { lib, namespace, ... }: - with lib; - { - options.${namespace}.new-module = { - enable = mkEnableOption "Enable new module"; - # Add other options here - }; - } - EOF - - # Create default.nix - cat > modules/nixos/new-module/default.nix << EOF - { config, lib, namespace, ... }: + { config, lib, namespace, pkgs, ... }: let - cfg = config.${namespace}.new-module; - in - { - imports = [ ./options.nix ]; - - config = lib.mkIf cfg.enable { - # Add your configuration here + name = "my-service"; + nebulaConfig = lib.${namespace}.mkModule { + inherit config name; + description = "my service description"; + options = { }; + moduleConfig = { + services.my-service = { + enable = true; + port = config.${namespace}.services.${name}.port; + }; + }; }; - } - EOF + in + { imports = [ nebulaConfig ]; } ``` -3. Import your module in your system configuration: +3. **Enable it** in a system configuration: ```nix - imports = [ - # ... - ../../../modules/nixos/new-module - ]; - - ${namespace}.new-module.enable = true; - ``` \ No newline at end of file + mjallen.services.my-service = { + enable = true; + port = 1234; + }; + ``` + +## Adding a New Package + +1. Create a directory under `packages/`: + ```bash + mkdir packages/my-package + ``` + +2. Write a `default.nix` that returns a derivation. The package will be available as `pkgs.mjallen.my-package` in all configurations. + +## Secrets + +See the [Secrets Management](../README.md#secrets-management) section of the root README for: +- How age keys are derived from SSH host keys +- Adding a new machine as a SOPS recipient +- Adding/editing secrets +- Generating Nebula VPN certificates diff --git a/docs/modules/README.md b/docs/modules/README.md index f45d021..ab32459 100644 --- a/docs/modules/README.md +++ b/docs/modules/README.md @@ -2,115 +2,294 @@ This directory contains documentation for the custom modules used in this NixOS configuration. -## Module Types +## Overview -The repository uses two main types of modules: +Modules are split into three categories: -1. **NixOS Modules** - System-level configurations in `modules/nixos/` -2. **Home Manager Modules** - User-level configurations in `modules/home/` +- **NixOS modules** (`modules/nixos/`) — system-level configuration +- **Home Manager modules** (`modules/home/`) — user-level configuration +- **Darwin modules** (`modules/darwin/`) — macOS-specific configuration + +All modules are auto-discovered by Snowfall Lib and expose options under the `mjallen` namespace. ## NixOS Modules -These modules configure the system-level aspects of NixOS: +### Boot (`modules/nixos/boot/`) -- [Boot Modules](./boot.md) - Boot loader and kernel configurations -- [Desktop Modules](./desktop.md) - Desktop environment configurations -- [Development Modules](./development.md) - Development tools and environments -- [Hardware Modules](./hardware.md) - Hardware-specific configurations -- [Home Assistant Modules](./homeassistant.md) - Home automation configuration -- [Networking Modules](./network.md) - Network configuration and services -- [Security Modules](./security.md) - Security-related configurations -- [Services Modules](./services.md) - Various service configurations -- [System Modules](./system.md) - General system configurations -- [Virtualization Modules](./virtualization.md) - Virtualization and containerization +| Module | Description | +|---|---| +| `boot/common/` | Shared boot defaults (quiet boot, Plymouth) | +| `boot/lanzaboote/` | Secure Boot via Lanzaboote | +| `boot/systemd-boot/` | systemd-boot (non-secure-boot systems) | +| `boot/plymouth/` | Plymouth splash screen | + +### Desktop (`modules/nixos/desktop/`) + +| Module | Description | +|---|---| +| `desktop/gnome/` | GNOME desktop environment | +| `desktop/hyprland/` | Hyprland compositor | +| `desktop/cosmic/` | COSMIC desktop environment | + +### Development (`modules/nixos/development/`) + +Enables development tools and language support. Options: + +```nix +mjallen.development = { + enable = true; + includeLanguages = [ "python" "c" ]; + includeContainers = true; +}; +``` + +### Hardware (`modules/nixos/hardware/`) + +| Module | Description | +|---|---| +| `hardware/amd/` | AMD GPU (AMDGPU driver, LACT) | +| `hardware/nvidia/` | NVIDIA GPU | +| `hardware/battery/` | Battery charge threshold management | +| `hardware/raspberry-pi/` | Raspberry Pi hardware support and DT overlays | +| `hardware/openrgb/` | OpenRGB for LED control | +| `hardware/btrfs/` | btrfs-specific settings | +| `hardware/common/` | Common hardware defaults | + +### Headless (`modules/nixos/headless/`) + +Server profile — disables suspend/hibernate, enables systemd watchdog, no display manager. + +```nix +mjallen.headless.enable = true; +``` + +### Home Assistant (`modules/nixos/homeassistant/`) + +Full smart home stack. See [Home Assistant docs](../home-assistant/README.md) for details. + +```nix +mjallen.services.home-assistant.enable = true; +``` + +### Impermanence (`modules/nixos/impermanence/`) + +Ephemeral root filesystem with explicit persistence declarations. + +```nix +mjallen.impermanence = { + enable = true; + extraDirectories = [ { directory = "/var/lib/myapp"; user = "myapp"; } ]; +}; +``` + +### Monitoring (`modules/nixos/monitoring/`) + +Prometheus metrics and Grafana dashboards. + +```nix +mjallen.monitoring.enable = true; +``` + +### Network (`modules/nixos/network/`) + +Hostname, firewall, NetworkManager profiles, static IP configuration. + +```nix +mjallen.network = { + hostName = "my-host"; + ipv4 = { + method = "manual"; + address = "10.0.1.5/24"; + gateway = "10.0.1.1"; + dns = "1.1.1.1"; + interface = "eth0"; + }; + firewall = { + enable = true; + allowedTCPPorts = [ 80 443 ]; + }; +}; +``` + +### Power (`modules/nixos/power/`) + +UPS (NUT) support. + +```nix +mjallen.power.ups.enable = true; +``` + +### Security (`modules/nixos/security/`) + +| Module | Description | +|---|---| +| `security/common/` | Common hardening (kernel params, etc.) | +| `security/tpm/` | TPM2 — Clevis disk unlock | + +### Services (`modules/nixos/services/`) + +~50 self-hosted service modules, all built with `mkModule`. Each exposes at minimum `enable`, `port`, `reverseProxy`, and `openFirewall`. Common usage pattern: + +```nix +mjallen.services.jellyfin = { + enable = true; + port = 8096; + reverseProxy.enable = true; +}; +``` + +Available services: + +`actual`, `ai`, `appimage`, `arrs`, `attic`, `authentik`, `authentikRac`, `booklore`, `caddy`, `calibre`, `calibre-web`, `cockpit`, `code-server`, `collabora`, `coturn`, `crowdsec`, `dispatcharr`, `free-games-claimer`, `gitea`, `glance`, `glances`, `grafana`, `guacd`, `headscale`, `immich`, `jellyfin`, `jellyseerr`, `lubelogger`, `manyfold`, `matrix`, `minecraft`, `mongodb`, `nebula`, `netbootxyz`, `nextcloud`, `ntfy`, `onlyoffice`, `opencloud`, `orca`, `paperless`, `paperless-ai`, `protonmail-bridge`, `restic`, `samba`, `sparky-fitness`, `sparky-fitness-server`, `sunshine`, `tdarr`, `termix`, `tunarr`, `unmanic`, `uptime-kuma`, `wyoming`, `your-spotify` + +#### Nebula VPN (`services/nebula/`) + +Unified module for both lighthouse and node roles: + +```nix +# Lighthouse +mjallen.services.nebula = { + enable = true; + isLighthouse = true; + port = 4242; + secretsPrefix = "pi5/nebula"; + secretsFile = lib.snowfall.fs.get-file "secrets/pi5-secrets.yaml"; + hostSecretName = "lighthouse"; +}; + +# Node +mjallen.services.nebula = { + enable = true; + port = 4242; + lighthouses = [ "10.1.1.1" ]; + staticHostMap = { "10.1.1.1" = [ "mjallen.dev:4242" ]; }; + secretsPrefix = "mymachine/nebula"; + secretsFile = lib.snowfall.fs.get-file "secrets/mymachine-secrets.yaml"; + hostSecretName = "mymachine"; +}; +``` + +See [Secrets Management](../../README.md#generating-nebula-vpn-certificates) for how to generate the required certificates. + +### SOPS (`modules/nixos/sops/`) + +Configures sops-nix to decrypt secrets using the machine's SSH host key as an age key. + +```nix +mjallen.sops = { + enable = true; + sshKeyPaths = [ "/etc/ssh/ssh_host_ed25519_key" ]; # default +}; +``` + +### User (`modules/nixos/user/`) + +System user account management. + +```nix +mjallen.user = { + name = "matt"; + mutableUsers = false; + extraGroups = [ "docker" "video" ]; +}; +``` + +--- ## Home Manager Modules -These modules configure user environments: +### Desktop -- [Applications](./home/applications.md) - User applications -- [Desktop](./home/desktop.md) - User desktop environments -- [Development](./home/development.md) - User development environments -- [Media](./home/media.md) - Media applications -- [Shell](./home/shell.md) - Shell configurations +| Module | Description | +|---|---| +| `desktop/gnome/` | GNOME user settings (extensions, keybindings, etc.) | +| `desktop/theme/` | Theme configuration | -## Module Structure +### Programs -Each module follows a standard structure: +| Module | Description | +|---|---| +| `programs/btop/` | btop system monitor | +| `programs/code/` | VS Code / VSCodium settings | +| `programs/git/` | Git user config | +| `programs/hyprland/` | Hyprland compositor config | +| `programs/kitty/` | Kitty terminal config | +| `programs/librewolf/` | LibreWolf browser settings | +| `programs/mako/` | Mako notification daemon | +| `programs/nwg-dock/` | nwg-dock panel | +| `programs/nwg-drawer/` | nwg-drawer app launcher | +| `programs/nwg-panel/` | nwg-panel bar | +| `programs/opencode/` | OpenCode AI coding assistant | +| `programs/update-checker/` | Automatic flake update checker | +| `programs/waybar/` | Waybar status bar | +| `programs/wlogout/` | Logout menu | +| `programs/wofi/` | Wofi launcher | +| `programs/zsh/` | Zsh shell config | -``` -modules/nixos/example-module/ -├── default.nix # Main implementation -├── options.nix # Option declarations -└── submodule/ # Optional submodules - └── default.nix # Submodule implementation -``` +### Other -### default.nix +| Module | Description | +|---|---| +| `gpg/` | GPG agent configuration | +| `services/pass/` | Password store | +| `shell-aliases/` | Common shell aliases | +| `sops/` | User-level SOPS secrets | +| `stylix/` | System-wide theming (colours, fonts, wallpaper) | +| `user/` | User environment defaults | -The `default.nix` file contains the main implementation of the module: +--- + +## Module Development + +### Using `mkModule` + +The `lib.mjallen.mkModule` helper (`lib/module/default.nix`) creates a fully-featured NixOS module from a minimal spec: ```nix -{ - config, - lib, - pkgs, - namespace, - ... -}: +{ config, lib, namespace, pkgs, ... }: let - cfg = config.${namespace}.example-module; -in -{ - imports = [ ./options.nix ]; + name = "my-service"; + cfg = config.${namespace}.services.${name}; - config = lib.mkIf cfg.enable { - # Module implementation when enabled + serviceConfig = lib.${namespace}.mkModule { + inherit config name; + description = "my service"; + options = { + # extra options beyond the standard set + myOption = lib.${namespace}.mkOpt lib.types.str "default" "Description"; + }; + moduleConfig = { + services.my-service = { + enable = true; + port = cfg.port; + }; + }; }; -} +in +{ imports = [ serviceConfig ]; } ``` -### options.nix +Standard options provided by `mkModule` for free: `enable`, `port`, `listenAddress`, `openFirewall`, `configDir`, `dataDir`, `createUser`, `configureDb`, `environmentFile`, `reverseProxy.*`, `redis.*`, `extraEnvironment`, `hashedPassword`, `puid`, `pgid`, `timeZone`. -The `options.nix` file declares the module's configuration options: +### Using `mkContainerService` + +For Podman/OCI container services, use `mkContainerService` instead: ```nix -{ lib, namespace, ... }: -with lib; -let - inherit (lib.${namespace}) mkOpt; -in -{ - options.${namespace}.example-module = { - enable = mkEnableOption "enable example module"; - # Other option declarations - }; -} +lib.${namespace}.mkContainerService { + inherit config name; + image = "ghcr.io/example/my-app:latest"; + internalPort = 8080; + volumes = [ "${cfg.configDir}:/config" ]; +}; ``` -## Using Modules - -To use a module in your system configuration: - -1. Enable the module in your system configuration: +### Option helpers ```nix -{ config, ... }: -{ - mjallen.example-module = { - enable = true; - # Other options - }; -} +lib.mjallen.mkOpt types.str "default" "description" +lib.mjallen.mkBoolOpt false "description" +lib.mjallen.mkOpt' types.int 80 # no description +lib.mjallen.enabled # { enable = true; } +lib.mjallen.disabled # { enable = false; } ``` - -## Creating New Modules - -To create a new module: - -1. Create a new directory in `modules/nixos/` or `modules/home/` -2. Create `default.nix` and `options.nix` files -3. Implement your module functionality -4. Import the module in your system configuration - -See the [Getting Started](../getting-started.md) guide for more details on creating modules. \ No newline at end of file diff --git a/docs/systems/README.md b/docs/systems/README.md index 19abbec..73fcf0a 100644 --- a/docs/systems/README.md +++ b/docs/systems/README.md @@ -4,19 +4,34 @@ This directory contains documentation for each system configuration in this repo ## Systems -- [Desktop (matt-nixos)](./matt-nixos.md) - Main desktop computer -- [NAS (jallen-nas)](./jallen-nas.md) - Home server and NAS -- [NUC (nuc-nixos)](./nuc-nixos.md) - Intel NUC -- [Raspberry Pi 5](./pi5.md) - Raspberry Pi 5 -- [MacBook Pro (nixOS)](./macbook-pro-nixos.md) - MacBook Pro running NixOS +| Host | Architecture | OS | Role | +|---|---|---|---| +| [matt-nixos](./matt-nixos.md) | x86_64-linux | NixOS | Primary AMD desktop | +| [jallen-nas](./jallen-nas.md) | x86_64-linux | NixOS | Home server / NAS | +| [nuc-nixos](./nuc-nixos.md) | x86_64-linux | NixOS | Intel NUC — Home Assistant hub | +| [allyx](./allyx.md) | x86_64-linux | NixOS | ASUS ROG Ally X handheld | +| [pi5](./pi5.md) | aarch64-linux | NixOS | Raspberry Pi 5 — network services | +| [macbook-pro-nixos](./macbook-pro-nixos.md) | aarch64-linux | NixOS (Asahi) | Apple Silicon MacBook Pro | +| [macbook-pro](./macbook-pro.md) | aarch64-darwin | nix-darwin | macOS on the same MacBook Pro | + +There are also two ISO targets (`x86_64-install-iso/graphical`, `x86_64-linux/iso-minimal`) used for installation media builds. + +## Network + +All hosts are on the `10.0.1.0/24` LAN with static IPs: + +| Host | LAN IP | Overlay (Nebula) | +|---|---|---| +| pi5 | 10.0.1.2 | 10.1.1.1 (lighthouse) | +| jallen-nas | 10.0.1.3 | 10.1.1.x (node) | +| nuc-nixos | 10.0.1.4 | — | ## Common Configuration -All systems share certain common configurations through the modules system. These include: +All systems share: +- SOPS secret management (age keys from SSH host keys) +- Impermanence (ephemeral root, explicit persistence) +- Nix flake-based configuration via Snowfall Lib +- The `mjallen` module namespace -- Base system configuration -- User management -- Network configuration -- Security settings - -Each system then adds its specific configurations on top of these common modules. \ No newline at end of file +Each system then layers its own modules and hardware configuration on top. diff --git a/docs/systems/allyx.md b/docs/systems/allyx.md new file mode 100644 index 0000000..8c90152 --- /dev/null +++ b/docs/systems/allyx.md @@ -0,0 +1,57 @@ +# ASUS ROG Ally X (allyx) + +`systems/x86_64-linux/allyx/` + +## Hardware + +- **Device**: ASUS ROG Ally X handheld gaming PC +- **CPU/GPU**: AMD (LACT, CoolerControl) +- **Disk**: NVMe with LUKS encryption +- **Security**: Lanzaboote (Secure Boot) + +## Key Features + +- Jovian NixOS for Steam Deck-compatible experience +- Steam auto-starts into Game Mode on boot +- Decky Loader for Steam Deck plugins +- Handheld Daemon for power/TDP/fan control +- GNOME available as a desktop session (selectable from Steam) +- SDDM (Wayland) as display manager — GDM disabled +- Gaming enabled (Gamemode, Gamescope, etc.) +- AMD GPU management via LACT +- CoolerControl for fan curves +- iwd as the Wi-Fi backend +- Impermanence (ephemeral root) + +## Jovian NixOS + +The allyx uses [Jovian NixOS](https://github.com/Jovian-Experiments/Jovian-NixOS) to provide Steam Deck compatibility: + +```nix +jovian.steam = { + enable = true; + autoStart = true; + desktopSession = "gnome"; # fall-through desktop session +}; + +jovian.decky-loader = { + enable = true; + extraPackages = [ pkgs.python3 pkgs.systemd ]; +}; +``` + +## Network + +- **Hostname**: allyx +- **Wi-Fi backend**: iwd (via NetworkManager) + +## Configuration Files + +| File | Purpose | +|---|---| +| `default.nix` | Main config — Jovian, gaming, hardware | +| `boot.nix` | Lanzaboote, kernel | + +## Secrets + +Secrets are in `secrets/allyx-secrets.yaml`, encrypted for: `matt`, `desktop`, `deck`, `steamdeck`, `admin`, `jallen-nas`, `matt_allyx`, `allyx`. diff --git a/docs/systems/jallen-nas.md b/docs/systems/jallen-nas.md index 00c749a..e501c95 100644 --- a/docs/systems/jallen-nas.md +++ b/docs/systems/jallen-nas.md @@ -1,101 +1,104 @@ # NAS Server (jallen-nas) -This document describes the configuration for the NAS server system. +`systems/x86_64-linux/jallen-nas/` ## Hardware -The NAS server is built on AMD hardware: +- **CPU**: AMD (x86_64) +- **GPU**: AMD (LACT for fan/power control) +- **Disk**: NVMe system drive + bcachefs NAS pool +- **Security**: TPM2 (Clevis disk unlock), Lanzaboote (Secure Boot) -- CPU: AMD processor -- Hardware-specific modules: - - `nixos-hardware.nixosModules.common-pc` - - `nixos-hardware.nixosModules.common-cpu-amd` - - `nixos-hardware.nixosModules.common-cpu-amd-pstate` - - `nixos-hardware.nixosModules.common-hidpi` +## Key Features -## Services +- bcachefs storage pool mounted at `/media/nas/main` +- Clevis-based TPM disk unlock at boot (no passphrase required) +- Impermanence — root is ephemeral; state persists to `/media/nas/main/persist` +- Samba shares (Windows file sharing, Time Machine) +- Nebula VPN node (overlay peer, lighthouse at pi5) +- ~40 self-hosted services behind a Caddy reverse proxy +- Authentik SSO protecting most web UIs +- CrowdSec for intrusion detection +- Restic backups -The NAS hosts various services: +## Network -### Media Services +- **LAN IP**: 10.0.1.3 (static, `enp197s0`) +- **Gateway**: 10.0.1.1 +- **Nebula**: overlay peer, lighthouse at `mjallen.dev:4242` -- **Jellyfin** - Media server -- **Jellyseerr** - Media request manager -- **Sonarr** - TV show management -- **Radarr** - Movie management -- **Lidarr** - Music management -- **Bazarr** - Subtitle management -- **Music Assistant** - Music streaming integration with Home Assistant +## Storage -### Download Services +| Mount | Filesystem | Description | +|---|---|---| +| `/media/nas/main` | bcachefs | Primary NAS pool (media, appdata, documents) | +| `/media/nas/test` | bcachefs | Secondary test pool | -- **Transmission** - Torrent client -- **NZBGet** - Usenet downloader -- **Prowlarr** - Indexer manager +### Samba Shares -### Document Management +| Share | Time Machine | +|---|---| +| `3d_printer` | no | +| `Backup` | no | +| `Documents` | no | +| `isos` | no | +| `app_data` | no | +| `TimeMachine` | yes (max 1 TB) | -- **Paperless-ngx** - Document management system +## Enabled Services -### File Sharing +| Service | Port | Notes | +|---|---|---| +| Caddy | 443/80 | Reverse proxy for all services | +| Authentik | 9000 | SSO / identity provider | +| Attic | 9012 | Nix binary cache (`cache.mjallen.dev`) | +| Immich | 2283 | Photo management | +| Jellyfin | 8096 | Media server | +| Jellyseerr | 5055 | Media request manager | +| Nextcloud | 9988 | Cloud storage | +| Paperless | 28981 | Document management | +| Paperless AI | 28982 | AI-assisted document tagging | +| Gitea | 3000 | Self-hosted Git | +| Matrix | 8448 | Matrix homeserver | +| Ntfy | 2586 | Push notifications | +| Glance | 5555 | Dashboard | +| Immich | 2283 | Photo library | +| Uptime Kuma | 3001 | Uptime monitoring | +| Code Server | 4444 | VS Code in the browser | +| Cockpit | 9090 | System management UI | +| Collabora | 9980 | Online office suite | +| CrowdSec | 8181 | Intrusion detection | +| Glances | 61208 | System stats | +| Coturn | 3478 | TURN/STUN server | +| Nebula | 4242 | Overlay VPN node | +| Restic | 8008 | Backup service | +| Sunshine | 47989 | Remote desktop (Moonlight) | +| Unmanic | 8265 | Media transcoding | +| Lubelogger | 6754 | Vehicle maintenance log | +| Manyfold | 3214 | 3D model library | +| Booklore | 6066 | Book library | +| Tunarr | 8000 | Virtual TV channels | +| Termix | 7777 | Web terminal | +| Sparky Fitness | 3004/3010 | Fitness tracking | +| Protonmail Bridge | 1025/1143 | SMTP/IMAP bridge | +| Arrs | various | Sonarr, Radarr, etc. | +| AI | various | Ollama, etc. | +| Wyoming | various | Voice assistant pipeline | -- **Samba** - Windows file sharing -- **Nextcloud** - Self-hosted cloud storage +## Configuration Files -### AI Services +| File | Purpose | +|---|---| +| `default.nix` | Main config — network, hardware, filesystems, packages | +| `apps.nix` | All service enable/disable declarations | +| `nas-defaults.nix` | Sets `configDir`/`dataDir` defaults for all services | +| `boot.nix` | Lanzaboote, kernel, initrd | +| `services.nix` | Home Assistant, samba, and other platform services | +| `users.nix` | User accounts (`admin`, `nix-apps`) | +| `sops.nix` | Secret declarations | +| `vpn.nix` | Nebula VPN configuration | +| `disabled.nix` | Services explicitly disabled | -- **Ollama** - Local AI model hosting +## Secrets -### Smart Home - -- **Home Assistant** - Smart home controller -- **Zigbee2MQTT** - Zigbee device integration -- **MQTT** - Message broker for IoT devices -- **Thread Border Router** - Thread network for smart home devices - -## Storage Configuration - -The NAS uses multiple storage devices: - -1. **System Drive** - For the operating system -2. **Data Drives** - Configured as a storage array for media and data - -## Network Configuration - -The NAS is configured with: - -- Static IP address -- Firewall rules for the various services -- Tailscale for secure remote access - -## Backup Strategy - -The NAS implements a comprehensive backup strategy: - -1. **System Backup** - Regular backups of the NixOS configuration -2. **Data Backup** - Backups of important data to secondary storage -3. **Off-site Backup** - Critical data is backed up off-site - -## Usage and Management - -### Accessing Services - -Most services are available through a reverse proxy, which provides: -- HTTPS access -- Authentication via Authentik -- Subdomain-based routing - -### Adding Storage - -To add additional storage to the NAS: - -1. Add the physical drive to the system -2. Update the disko configuration -3. Rebuild the system with `nixos-rebuild switch` - -### Monitoring - -The system can be monitored through: -- Prometheus metrics -- Grafana dashboards -- Home Assistant sensors \ No newline at end of file +Secrets are in `secrets/nas-secrets.yaml`, encrypted for: `matt`, `desktop`, `admin`, `jallen-nas`. diff --git a/docs/systems/macbook-pro-nixos.md b/docs/systems/macbook-pro-nixos.md new file mode 100644 index 0000000..064a235 --- /dev/null +++ b/docs/systems/macbook-pro-nixos.md @@ -0,0 +1,69 @@ +# MacBook Pro — NixOS / Asahi Linux (macbook-pro-nixos) + +`systems/aarch64-linux/macbook-pro-nixos/` + +## Hardware + +- **Device**: Apple Silicon MacBook Pro (M-series) +- **OS**: NixOS via [Asahi Linux](https://asahilinux.org/) (`nixos-apple-silicon`) +- **Boot**: Asahi boot chain (not traditional EFI) + +## Key Features + +- Asahi Linux kernel with full Apple Silicon support (sound, GPU, etc.) +- GNOME as the primary desktop; Hyprland available but disabled +- x86_64 emulation via binfmt (enables running x86 binaries) +- Waydroid and libvirtd available (Waydroid disabled by default) +- Battery management — charge threshold set via `macsmc-battery` +- Omnissa Horizon client (custom package) for remote desktop +- Distrobox for containerised Linux environments +- iwd as the Wi-Fi backend + +## x86_64 Emulation + +```nix +nix.settings.extra-platforms = [ "x86_64-linux" ]; +boot.binfmt.emulatedSystems = [ "x86_64-linux" ]; +``` + +This allows building and running x86_64 packages on the ARM host. + +## Asahi Hardware + +The Asahi hardware module provides: +- Firmware loading from `./firmware/` +- Sound setup (`setupAsahiSound = true`) +- Apple-specific kernel patches and device drivers + +Useful packages installed: +`asahi-bless`, `asahi-btsync`, `asahi-nvram`, `asahi-wifisync`, `apfs-fuse`, `apfsprogs`, `muvm`, `fex` + +## Network + +- **Hostname**: macbook-pro-nixos +- **Wi-Fi backend**: iwd (via NetworkManager) +- Firewall: extra rules for multicast (ports 1990, 2021) + +## Battery Management + +```nix +mjallen.hardware.battery = { + enable = true; + chargeLimitPath = "/sys/class/power_supply/macsmc-battery/charge_control_end_threshold"; +}; +``` + +## Configuration Files + +| File | Purpose | +|---|---| +| `default.nix` | Main config — Asahi hardware, users, network | +| `boot.nix` | Asahi boot configuration | +| `filesystems.nix` | Disk layout | +| `hardware-configuration.nix` | Generated hardware config | +| `services.nix` | logind, GDM, GNOME, Flatpak, power settings | +| `firmware/` | Asahi firmware blobs | + +## Secrets + +Secrets are in `secrets/mac-secrets.yaml`, encrypted for: `matt`, `matt_pi5`, `desktop`, `pi5`, `admin`, `jallen-nas`, `matt_macbook-pro`, `macbook-pro`. diff --git a/docs/systems/macbook-pro.md b/docs/systems/macbook-pro.md new file mode 100644 index 0000000..cb4e7a8 --- /dev/null +++ b/docs/systems/macbook-pro.md @@ -0,0 +1,40 @@ +# MacBook Pro — macOS / nix-darwin (macbook-pro) + +`systems/aarch64-darwin/macbook-pro/` + +## Overview + +This is the [nix-darwin](https://github.com/nix-darwin/nix-darwin) configuration for the same MacBook Pro running macOS. It provides declarative macOS system management alongside Homebrew. + +## Key Features + +- Touch ID for `sudo` +- Declarative Homebrew (casks and formulae managed via `nix-homebrew`) +- `nh` for easy NixOS/darwin rebuilds +- `attic-client` for accessing the Nix binary cache +- `macpm` for Apple Silicon power monitoring +- Rosetta builder available (disabled, on-demand) +- Linux builder available (disabled) + +## Configuration Files + +| File | Purpose | +|---|---| +| `default.nix` | Main config — packages, users, environment | +| `homebrew.nix` | Declarative Homebrew casks and formulae | +| `programs.nix` | macOS program settings | +| `system.nix` | System defaults (dock, finder, etc.) | + +## User + +- **Username**: `mattjallen` +- **Home**: `/Users/mattjallen` +- **Flake path**: `/Users/mattjallen/nix-config` (set via `NH_OS_FLAKE`) + +## Rebuilding + +```bash +darwin-rebuild switch --flake .#macbook-pro +# or using nh: +nh darwin switch +``` diff --git a/docs/systems/matt-nixos.md b/docs/systems/matt-nixos.md new file mode 100644 index 0000000..824a19d --- /dev/null +++ b/docs/systems/matt-nixos.md @@ -0,0 +1,50 @@ +# Desktop (matt-nixos) + +`systems/x86_64-linux/matt-nixos/` + +## Hardware + +- **CPU**: AMD +- **GPU**: AMD (LACT for fan/power control, OpenRGB) +- **Disk**: NVMe with LUKS encryption (disko) +- **Security**: TPM2, Lanzaboote (Secure Boot) + +## Key Features + +- GNOME as the primary desktop (Hyprland available but disabled) +- COSMIC available as a specialisation (`nixos-rebuild switch --specialisation cosmic`) +- Gaming — Steam, Gamemode, Gamescope, Lossless Scaling (`lsfg-vk`) +- AMD GPU management via LACT +- CoolerControl for fan curves +- Impermanence (ephemeral root) +- iwd as the Wi-Fi backend +- VSCodium as `$EDITOR`/`$VISUAL` + +## Desktop Specialisations + +| Specialisation | Description | +|---|---| +| *(default)* | GNOME | +| `cosmic` | COSMIC DE (enables `mjallen.desktop.cosmic`, disables GNOME/Hyprland) | + +## Network + +- **Hostname**: matt-nixos +- **Wi-Fi backend**: iwd (via NetworkManager) + +## Configuration Files + +| File | Purpose | +|---|---| +| `default.nix` | Main config | +| `boot.nix` | Lanzaboote, kernel | +| `filesystems.nix` | Disk layout | +| `sops.nix` | Secret declarations | +| `wifi-fixer.nix` | NetworkManager Wi-Fi workaround | +| `services/lsfg-vk/` | Lossless Scaling frame generation | +| `services/ratbagd/` | Gaming mouse config (libratbag) | +| `services/restic/` | Restic backup jobs | + +## Secrets + +Secrets are in `secrets/desktop-secrets.yaml`, encrypted for: `matt`, `desktop`, `admin`, `jallen-nas`. diff --git a/docs/systems/nuc-nixos.md b/docs/systems/nuc-nixos.md new file mode 100644 index 0000000..16d6059 --- /dev/null +++ b/docs/systems/nuc-nixos.md @@ -0,0 +1,57 @@ +# Intel NUC (nuc-nixos) + +`systems/x86_64-linux/nuc-nixos/` + +## Hardware + +- **Device**: Intel NUC +- **Disk**: btrfs with LUKS encryption +- **Security**: TPM2, Lanzaboote (Secure Boot) +- **Kernel**: CachyOS `linux-cachyos-lto` (x86_64-v4 build) + +## Key Features + +- Headless server (no display manager, watchdog enabled) +- Home Assistant — the primary smart home controller +- OpenThread Border Router (OTBR) for Matter/Thread devices +- Impermanence (ephemeral root, persistent state for HA and related services) +- btrfs filesystem (unlike the bcachefs-based NAS and Pi5) + +## Network + +- **LAN IP**: 10.0.1.4 (static, `enp2s0`) +- **Gateway / DNS**: 10.0.1.1 +- **Firewall**: 1883 (MQTT), 8880/8881 (OTBR), 8192 + +## Services + +| Service | Port | Description | +|---|---|---| +| Home Assistant | 8097 | Smart home controller | +| Mosquitto (MQTT) | 1883 | IoT message broker | +| Zigbee2MQTT | 8080 | Zigbee device bridge | +| Music Assistant | 8095 | Music streaming | +| OTBR | 8880/8881 | OpenThread Border Router (Matter/Thread) | +| ESPHome | — | ESP microcontroller firmware | +| PostgreSQL | — | HA database backend | + +## Persistent Directories + +The following directories survive reboots via impermanence: + +- `/esphome` +- `/var/lib/homeassistant` +- `/var/lib/mosquitto` +- `/var/lib/music-assistant` +- `/var/lib/postgresql` +- `/var/lib/zigbee2mqtt` + +## Configuration Files + +| File | Purpose | +|---|---| +| `default.nix` | All config in one file — HA, OTBR, network, hardware, impermanence | + +## Secrets + +Secrets are in `secrets/nuc-secrets.yaml`, encrypted for: `nuc`, `admin_nuc`, `matt`, `admin`, `jallen-nas`. diff --git a/docs/systems/pi5.md b/docs/systems/pi5.md new file mode 100644 index 0000000..cd12493 --- /dev/null +++ b/docs/systems/pi5.md @@ -0,0 +1,62 @@ +# Raspberry Pi 5 (pi5) + +`systems/aarch64-linux/pi5/` + +## Hardware + +- **Board**: Raspberry Pi 5 +- **Boot**: UEFI (via `rpi5-uefi`) +- **Storage**: bcachefs +- **Connectivity**: Ethernet (`end0`); Wi-Fi and Bluetooth disabled via device tree overlays + +## Key Features + +- Headless server (no display, no desktop) +- Nebula VPN **lighthouse** — the central relay for the `jallen-nebula` overlay network +- AdGuard Home DNS server (port 53) +- Docker +- Impermanence (ephemeral root) +- Extensive Raspberry Pi device tree overlays configured (I²C, SPI, UART, SDIO, etc.) + +## Network + +- **LAN IP**: 10.0.1.2 (static, `end0`) +- **Gateway**: 10.0.1.1 +- **DNS**: 1.1.1.1 +- **Nebula**: lighthouse at `10.1.1.1`, listening on UDP 4242 (public: `mjallen.dev:4242`) +- Firewall: TCP/UDP 53 open (DNS) + +## Nebula Lighthouse + +The pi5 acts as the Nebula VPN lighthouse for the whole network. All other Nebula nodes connect to it to discover peers. + +```nix +mjallen.services.nebula = { + enable = true; + isLighthouse = true; + port = 4242; + secretsPrefix = "pi5/nebula"; + secretsFile = lib.snowfall.fs.get-file "secrets/pi5-secrets.yaml"; + hostSecretName = "lighthouse"; +}; +``` + +## Services + +| Service | Port | Description | +|---|---|---| +| AdGuard Home | 53 | DNS ad-blocking | +| Nebula | 4242 (UDP) | VPN lighthouse | + +## Configuration Files + +| File | Purpose | +|---|---| +| `default.nix` | Main config | +| `boot.nix` | UEFI boot, kernel | +| `adguard.nix` | AdGuard Home configuration | +| `sops.nix` | Secret declarations (SSH keys, system keys) | + +## Secrets + +Secrets are in `secrets/pi5-secrets.yaml`, encrypted for: `matt`, `matt_pi5`, `desktop`, `pi5`, `admin`, `jallen-nas`. diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md index 8c80117..746eff5 100644 --- a/docs/troubleshooting.md +++ b/docs/troubleshooting.md @@ -1,213 +1,217 @@ # Troubleshooting Guide -This guide provides solutions for common issues that may arise when using this NixOS configuration. +Common issues and solutions for this NixOS configuration. -## System Issues +## Build Failures -### Failed System Build +### `nixos-rebuild switch` fails -**Problem**: `nixos-rebuild switch` fails with an error. +1. **Syntax error** — the error message includes the file and line number. Common causes: missing `;`, unmatched `{`, wrong type passed to an option. -**Solutions**: +2. **Evaluation error** — read the full error trace. Often caused by a module option receiving the wrong type, or a missing `cfg.enable` guard. -1. **Syntax Errors**: - - Check the error message for file and line number information - - Verify the syntax in the mentioned file - - Common issues include missing semicolons, curly braces, or mismatched quotes - -2. **Missing Dependencies**: - - If the error mentions a missing package or dependency: - ``` - git pull # Update to the latest version - nix flake update # Update the flake inputs - ``` - -3. **Conflicting Modules**: - - Look for modules that might be configuring the same options incompatibly - - Disable one of the conflicting modules or adjust their configurations - -4. **Disk Space Issues**: - - Check available disk space with `df -h` - - Clear old generations: `sudo nix-collect-garbage -d` - -### Boot Issues - -**Problem**: System fails to boot after a configuration change. - -**Solutions**: - -1. **Boot into a Previous Generation**: - - At the boot menu, select an older generation - - Once booted, revert the problematic change: - ``` - cd /etc/nixos - git revert HEAD # Or edit the files directly - sudo nixos-rebuild switch - ``` - -2. **Boot from Installation Media**: - - Boot from a NixOS installation media - - Mount your system: - ``` - sudo mount /dev/disk/by-label/nixos /mnt - sudo mount /dev/disk/by-label/boot /mnt/boot # If separate boot partition - ``` - - Chroot into your system: - ``` - sudo nixos-enter --root /mnt - cd /etc/nixos - git revert HEAD # Or edit the files directly - nixos-rebuild switch --install-bootloader - ``` - -## Home Assistant Issues - -### Home Assistant Fails to Start - -**Problem**: Home Assistant service fails to start. - -**Solutions**: - -1. **Check Service Status**: - ``` - systemctl status home-assistant - journalctl -u home-assistant -n 100 +3. **Fetch failure** — a flake input or package source can't be downloaded. Check network connectivity, or try: + ```bash + nix flake update --update-input ``` -2. **Database Issues**: - - Check PostgreSQL is running: `systemctl status postgresql` - - Verify database connection settings in Home Assistant configuration - -3. **Permission Issues**: - - Check ownership and permissions on config directory: - ``` - ls -la /var/lib/homeassistant - sudo chown -R hass:hass /var/lib/homeassistant - sudo chmod -R 750 /var/lib/homeassistant - ``` - -4. **Custom Component Issues**: - - Try disabling custom components to isolate the issue: - - Edit `modules/nixos/homeassistant/services/homeassistant/default.nix` - - Comment out the `customComponents` section - - Rebuild: `sudo nixos-rebuild switch` - -### Zigbee Device Connection Issues - -**Problem**: Zigbee devices fail to connect or are unstable. - -**Solutions**: - -1. **Verify Device Path**: - - Check the Zigbee coordinator is properly detected: - ``` - ls -la /dev/ttyUSB* - ``` - - Update the device path if needed: - - Edit your system configuration - - Set `mjallen.services.home-assistant.zigbeeDevicePath` to the correct path - - Rebuild: `sudo nixos-rebuild switch` - -2. **Interference Issues**: - - Move the Zigbee coordinator away from other wireless devices - - Try a USB extension cable to improve positioning - - Change Zigbee channel in Zigbee2MQTT configuration - -3. **Reset Zigbee2MQTT**: - ``` - systemctl restart zigbee2mqtt +4. **Disk space** — build sandbox fills up. Free space: + ```bash + sudo nix-collect-garbage -d + df -h /nix ``` -### Automation Issues +### Assertion failures -**Problem**: Automations don't run as expected. +If you see `assertion failed`, read the `message` field. For example: +``` +error: assertion failed at …/nebula/sops.nix + mjallen.services.nebula.secretsPrefix must be set +``` +Set the required option in the system configuration. -**Solutions**: +## Boot Issues -1. **Check Automation Status**: - - In Home Assistant UI, verify the automation is enabled - - Check Home Assistant logs for automation execution errors +### System won't boot after a config change -2. **Entity Issues**: - - Verify entity IDs are correct - - Check if entities are available/connected - - Test direct service calls to verify entity control works +1. At the boot menu, select a previous generation. +2. Once booted, revert the change: + ```bash + cd /etc/nixos + git revert HEAD + sudo nixos-rebuild switch --flake .#$(hostname) + ``` -3. **Trigger Issues**: - - Test the automation manually via Developer Tools > Services - - Use `automation.trigger` service with the automation's entity_id +### Booting from installation media to recover -## Flake Issues +```bash +# Mount the system (adjust device paths as needed) +sudo mount /dev/disk/by-label/nixos /mnt +sudo mount /dev/disk/by-label/boot /mnt/boot -### Flake Input Update Errors +# Chroot in +sudo nixos-enter --root /mnt +cd /etc/nixos -**Problem**: `nix flake update` fails or causes issues. +# Revert and rebuild +git revert HEAD +nixos-rebuild switch --flake .#hostname --install-bootloader +``` -**Solutions**: +### Lanzaboote / Secure Boot issues -1. **Selective Updates**: - - Update specific inputs instead of all at once: - ``` - nix flake lock --update-input nixpkgs - ``` +If Secure Boot enrolment fails or the system won't verify: -2. **Rollback Flake Lock**: - - If an update causes issues, revert to previous flake.lock: - ``` - git checkout HEAD^ -- flake.lock - ``` +```bash +# Check enrolled keys +sbctl status -3. **Pin to Specific Revisions**: - - In `flake.nix`, pin problematic inputs to specific revisions: - ```nix - nixpkgs-stable.url = "github:NixOS/nixpkgs/5233fd2ba76a3accb05f88b08917450363be8899"; - ``` +# Re-enrol if needed (run as root) +sbctl enrol-keys --microsoft -## Secret Management Issues +# Sign bootloader files manually +sbctl sign -s /boot/EFI/systemd/systemd-bootx64.efi +``` -### Sops Decryption Errors +## SOPS / Secrets Issues -**Problem**: Sops fails to decrypt secrets. +### `secret not found` or permission denied at boot -**Solutions**: +1. Verify the secret key path matches what's declared in the module's `sops.nix`. +2. Check the secret exists in the SOPS file: + ```bash + sops --decrypt secrets/nas-secrets.yaml | grep "the-key" + ``` +3. Check the `owner`/`group` set on the secret matches the service user. -1. **Key Issues**: - - Verify your GPG key is available and unlocked - - Check `.sops.yaml` includes your key fingerprint +### Can't decrypt — wrong age key -2. **Permission Issues**: - - Check file permissions on secret files - - Make sure the user running `nixos-rebuild` has access to the GPG key +The machine's age key is derived from `/etc/ssh/ssh_host_ed25519_key`. If the host key was regenerated, the age key changed and existing secrets can no longer be decrypted. + +To fix: re-encrypt the secrets file with the new public key: +```bash +# Get the new public key +nix-shell -p ssh-to-age --run 'ssh-to-age < /etc/ssh/ssh_host_ed25519_key.pub' + +# Update .sops.yaml with the new key, then: +sops updatekeys secrets/nas-secrets.yaml +``` + +### Adding a new secret to an existing file + +```bash +sops secrets/nas-secrets.yaml +# Editor opens with decrypted YAML — add your key, save, sops re-encrypts +``` + +## Nebula VPN Issues + +### Peers can't connect + +1. Verify the lighthouse is reachable on its public address: + ```bash + nc -zvu mjallen.dev 4242 + ``` +2. Check the nebula service on both hosts: + ```bash + systemctl status nebula@jallen-nebula + journalctl -u nebula@jallen-nebula -n 50 + ``` +3. Confirm the CA cert, host cert, and host key are all present and owned by the `nebula-jallen-nebula` user: + ```bash + ls -la /run/secrets/pi5/nebula/ + ``` +4. Verify the host cert was signed by the same CA as the other nodes: + ```bash + nebula-cert verify -ca ca.crt -crt host.crt + ``` + +### Certificate expired + +Re-sign the host certificate: +```bash +nebula-cert sign -name "hostname" -ip "10.1.1.x/24" \ + -ca-crt ca.crt -ca-key ca.key \ + -out-crt host.crt -out-key host.key +# Update SOPS, rebuild +``` + +## Impermanence Issues + +### Service fails because its data directory is missing after reboot + +If a service stores state in a path that isn't in the persistence list, it will be wiped on reboot. Add it to `impermanence.extraDirectories`: + +```nix +mjallen.impermanence.extraDirectories = [ + { directory = "/var/lib/my-service"; user = "my-service"; group = "my-service"; mode = "0750"; } +]; +``` + +Then move the existing data if needed: +```bash +cp -a /var/lib/my-service /persist/var/lib/my-service +``` + +## Flake Input Issues + +### Input update breaks a build + +Roll back the specific input: +```bash +git checkout HEAD^ -- flake.lock +``` + +Or pin the input to a specific revision in `flake.nix`: +```nix +nixpkgs-unstable.url = "github:NixOS/nixpkgs/abc123def"; +``` + +## Service Issues + +### Service won't start + +```bash +systemctl status +journalctl -u -n 100 --no-pager +``` + +### Caddy reverse proxy not routing + +1. Check that `reverseProxy.enable = true` is set on the service. +2. Verify the subdomain matches: `reverseProxy.subdomain = "myapp"` → `myapp.mjallen.dev`. +3. Check Caddy logs: + ```bash + journalctl -u caddy -n 50 + ``` + +### PostgreSQL database missing for a service + +If `configureDb = true` is set, the database is created automatically. If it's missing: +```bash +sudo -u postgres createdb my-service +sudo -u postgres psql -c "GRANT ALL ON DATABASE my-service TO my-service;" +``` ## Network Issues -### Firewall Blocks Services +### Firewall blocking a service -**Problem**: Services are not accessible due to firewall rules. +Check which ports are open: +```bash +sudo nft list ruleset | grep accept +``` -**Solutions**: +Add ports in the system config: +```nix +mjallen.network.firewall.allowedTCPPorts = [ 8080 ]; +``` -1. **Check Firewall Status**: - ``` - sudo nix-shell -p iptables --run "iptables -L" - ``` - -2. **Verify Firewall Configuration**: - - Check if ports are properly allowed in the configuration - - Add missing ports if necessary - -3. **Temporary Disable Firewall** (for testing only): - ``` - sudo systemctl stop firewall - # After testing - sudo systemctl start firewall - ``` +Or if using `mkModule`, set `openFirewall = true` (it's the default). ## Getting Help -If you encounter an issue not covered in this guide: - -1. Check the NixOS Wiki: https://nixos.wiki/ -2. Search the NixOS Discourse forum: https://discourse.nixos.org/ -3. Join the NixOS Matrix/Discord community for real-time help -4. File an issue in the repository if you believe you've found a bug \ No newline at end of file +- NixOS manual: `nixos-help` or https://nixos.org/manual/nixos/stable/ +- NixOS Wiki: https://nixos.wiki/ +- NixOS Discourse: https://discourse.nixos.org/ +- Nix package search: https://search.nixos.org/packages diff --git a/lib/README.md b/lib/README.md index 4f503b0..95a1068 100644 --- a/lib/README.md +++ b/lib/README.md @@ -4,41 +4,186 @@ Utility functions for the NixOS/nix-darwin configuration. Exposed via Snowfall L ## Directory Structure -- `default.nix`: Main entry point — exports `module`, `file`, and `versioning` -- `module/`: Module creation helpers (`mkModule`, `mkOpt`, `mkBoolOpt`, etc.) -- `file/`: File and path utilities -- `versioning/`: Multi-source version pinning helpers (used by packages) +- `default.nix` — Main entry point; exports `module`, `file`, and `versioning` +- `module/` — Module creation helpers (`mkModule`, `mkContainerService`, `mkSopsEnvFile`, `mkOpt`, etc.) +- `file/` — File and path utilities +- `versioning/` — Multi-source version pinning helpers (used by packages) + +--- ## Module Utilities (`lib.mjallen.module`) +### `mkModule` + +Creates a NixOS service module with a standard set of options. All config is gated behind `cfg.enable`. + +```nix +lib.mjallen.mkModule { + config # NixOS config attrset (pass-through from module args) + name # Service name — used for option path and systemd unit + description # Text for mkEnableOption (defaults to name) + options # Extra options merged into the submodule + moduleConfig # NixOS config body (applied when cfg.enable = true) + domain # Option namespace domain (default: "services") + serviceName # Systemd service name (default: name) +} +``` + +**Standard options provided for free:** + +| Option | Type | Default | Description | +|---|---|---|---| +| `enable` | bool | `false` | Enable/disable the service | +| `port` | int | `80` | Service listen port | +| `listenAddress` | str | `"0.0.0.0"` | Bind address | +| `openFirewall` | bool | `true` | Open TCP+UDP firewall ports | +| `configDir` | str | `/var/lib/` | Config directory | +| `dataDir` | str | `/var/lib//data` | Data directory | +| `createUser` | bool | `false` | Create a dedicated system user | +| `configureDb` | bool | `false` | Create a PostgreSQL database | +| `environmentFile` | str\|null | `null` | Path to an env-file | +| `extraEnvironment` | attrs | `{}` | Extra environment variables | +| `hashedPassword` | str\|null | `null` | Hashed password for web auth | +| `puid` / `pgid` | str | `"911"` / `"100"` | UID/GID for container services | +| `timeZone` | str | `"UTC"` | Timezone for container services | +| `redis.enable` | bool | `false` | Create a Redis instance for this service | +| `redis.port` | int | `6379` | Redis port | +| `reverseProxy.enable` | bool | `false` | Add a Caddy reverse proxy block | +| `reverseProxy.subdomain` | str | `` | Caddy subdomain | +| `reverseProxy.domain` | str | `"mjallen.dev"` | Caddy base domain | +| `reverseProxy.upstreamUrl` | str\|null | `null` | Override upstream URL | +| `reverseProxy.extraCaddyConfig` | lines | `""` | Extra Caddyfile directives | + +**Default behaviour when enabled:** + +- Adds Caddy reverse proxy block (if `reverseProxy.enable = true`) +- Opens firewall ports (if `openFirewall = true`) +- Creates system user/group (if `createUser = true`) +- Creates PostgreSQL database (if `configureDb = true`) +- Creates Redis instance (if `redis.enable = true`) +- Adds `RequiresMountsFor` systemd dependency for `configDir` and `dataDir` + +--- + +### `mkContainerService` + +Wraps `mkModule` for Podman/OCI container services. Generates the full container definition automatically. + +```nix +lib.mjallen.mkContainerService { + config # NixOS config attrset + name # Service/container name + image # OCI image reference (e.g. "ghcr.io/example/app:latest") + internalPort # Port the container listens on internally + description # Human-readable description (defaults to name) + options # Extra mkModule options + volumes # Extra volume mount strings + environment # Extra environment variables (merged with PUID/PGID/TZ) + environmentFiles # List of env-file paths (e.g. SOPS template paths) + extraOptions # Extra --opt strings for the container runtime + devices # Device mappings + extraConfig # Extra NixOS config merged into moduleConfig +} +``` + +The systemd service is named `podman-`, and the port binding is `:`. + +--- + +### `mkSopsEnvFile` + +Generates a SOPS secrets block and a SOPS template env-file in a single call. Useful for services that need secrets injected as environment variables. + +```nix +lib.mjallen.mkSopsEnvFile { + secrets # attrset: sops-key → extra attrs (owner, group, etc.) + name # Template file name, e.g. "myapp.env" + content # Template body (use config.sops.placeholder."key") + restartUnit # Systemd unit to restart when secrets change + owner # File owner (default: "nix-apps") + group # File group (default: "jallen-nas") + mode # File permissions (default: "660") + sopsFile # Default SOPS file for all secrets +} +``` + +--- + +### Option helpers + +| Function | Signature | Description | +|---|---|---| +| `mkOpt` | `type → default → description → mkOption` | Standard option shorthand | +| `mkOpt'` | `type → default → mkOption` | `mkOpt` without description | +| `mkBoolOpt` | `default → description → mkOption` | Boolean option shorthand | +| `mkBoolOpt'` | `default → mkOption` | Boolean option without description | +| `mkReverseProxyOpt` | `name → attrset` | Standard Caddy reverse proxy sub-options | + +--- + +### Convenience shorthands + +| Value | Expands to | +|---|---| +| `enabled` | `{ enable = true; }` | +| `disabled` | `{ enable = false; }` | + +--- + +### Attribute utilities + | Function | Description | |---|---| -| `mkModule` | Create a NixOS module with standard options (enable, port, reverseProxy, firewall, user, postgresql, redis) | -| `mkOpt` | `type → default → description → mkOption` shorthand | -| `mkOpt'` | `mkOpt` without description | -| `mkBoolOpt` | Boolean `mkOpt` shorthand | -| `mkBoolOpt'` | Boolean `mkOpt` without description | -| `mkReverseProxyOpt` | Standard Caddy reverse proxy sub-options | -| `enabled` | `{ enable = true; }` shorthand | -| `disabled` | `{ enable = false; }` shorthand | -| `capitalize` | Capitalise the first character of a string | -| `boolToNum` | Convert a boolean to 0 or 1 | | `default-attrs` | Apply `lib.mkDefault` to every value in an attrset | | `force-attrs` | Apply `lib.mkForce` to every value in an attrset | | `nested-default-attrs` | Apply `default-attrs` one level deeper | | `nested-force-attrs` | Apply `force-attrs` one level deeper | -| `enableForSystem` | Filter a module list to only those that match a given system string | -## File Utilities (`lib.mjallen.file`) +--- + +### String utilities | Function | Description | |---|---| -| `getFile` | Resolve a path relative to the flake root | -| `safeImport` | Import a Nix file with a fallback on error | -| `scanDir` | Return a list of directory names under a path | -| `importModulesRecursive` | Recursively discover and import all `default.nix` files under a directory | +| `capitalize` | Capitalise the first character of a string | + +--- + +### Boolean utilities + +| Function | Description | +|---|---| +| `boolToNum` | `true → 1`, `false → 0` | + +--- + +## Home Manager Utilities (`lib.mjallen.module`) + +### `mkHomeModule` + +Creates a Home Manager module with a standard `enable` option. + +```nix +lib.mjallen.mkHomeModule { + config # HM config attrset + domain # Option namespace domain, e.g. "programs" or "desktop" + name # Module name, e.g. "btop" + description # Text for mkEnableOption (defaults to name) + options # Extra options merged into the submodule + moduleConfig # HM config body (applied when cfg.enable = true) +} +``` + +--- + +## File Utilities (`lib.mjallen.file`) + +Helpers for resolving paths relative to the flake root. Primarily used internally by modules and packages. + +--- ## Versioning Utilities (`lib.mjallen.versioning`) -Used by packages that track multiple upstream variants (e.g. `linux-rpi`, `proton-cachyos`). -See `lib/versioning/default.nix` for the full API. +Used by packages that track multiple upstream variants (e.g. `proton-cachyos`, `linux-rpi`). Reads a `version.json` file and resolves sources with optional variable substitution and per-variant overrides. + +See `lib/versioning/default.nix` for the full API and `docs/version.schema.json` for the `version.json` schema. diff --git a/modules/nixos/services/cockpit/default.nix b/modules/nixos/services/cockpit/default.nix index b174893..80480fd 100644 --- a/modules/nixos/services/cockpit/default.nix +++ b/modules/nixos/services/cockpit/default.nix @@ -1,6 +1,7 @@ { config, lib, + pkgs, namespace, ... }: @@ -16,6 +17,16 @@ let enable = true; port = cfg.port; openFirewall = cfg.openFirewall; + allowed-origins = [ + "https://10.0.1.3:${toString cfg.port}" + "https://jallen-nas:${toString cfg.port}" + "https://jallen-nas.local:${toString cfg.port}" + ]; + plugins = with pkgs.${namespace}; [ + # cockpit-benchmark + cockpit-podman + cockpit-machines + ]; }; }; }; diff --git a/modules/nixos/services/nebula-lighthouse/default.nix b/modules/nixos/services/nebula-lighthouse/default.nix deleted file mode 100644 index d2bac1b..0000000 --- a/modules/nixos/services/nebula-lighthouse/default.nix +++ /dev/null @@ -1,74 +0,0 @@ -{ - config, - lib, - pkgs, - namespace, - ... -}: -with lib; -let - name = "nebula-lighthouse"; - cfg = config.${namespace}.services.${name}; - ca = config.sops.secrets."pi5/nebula/ca-cert".path; - cert = config.sops.secrets."pi5/nebula/lighthouse-cert".path; - key = config.sops.secrets."pi5/nebula/lighthouse-key".path; - - nebulaConfig = lib.${namespace}.mkModule { - inherit config name; - description = "nebula"; - options = { }; - moduleConfig = { - environment.systemPackages = with pkgs; [ nebula ]; - services.nebula.networks = { - jallen-nebula = { - enable = true; - enableReload = true; - isLighthouse = true; - ca = ca; - cert = cert; - key = key; - lighthouse = { - dns = { - enable = false; - host = "localhost"; - port = 53; - }; - }; - listen = { - host = cfg.listenAddress; - port = cfg.port; - }; - # lighthouses = [ - # "10.1.1.1" - # ]; - settings = { - firewall = { - outbound = [ - { - # Allow all outbound traffic from this node - port = "any"; - proto = "any"; - host = "any"; - } - ]; - inbound = [ - { - # Allow all outbound traffic from this node - port = "any"; - proto = "any"; - host = "any"; - } - ]; - }; - }; - }; - }; - }; - }; -in -{ - imports = [ - nebulaConfig - ./sops.nix - ]; -} diff --git a/modules/nixos/services/nebula-lighthouse/sops.nix b/modules/nixos/services/nebula-lighthouse/sops.nix deleted file mode 100644 index 58351ef..0000000 --- a/modules/nixos/services/nebula-lighthouse/sops.nix +++ /dev/null @@ -1,45 +0,0 @@ -{ - config, - lib, - namespace, - ... -}: -with lib; -let - cfg = config.${namespace}.services.nebula-lighthouse; -in -{ - - config = mkIf cfg.enable { - sops = { - secrets = { - "pi5/nebula/ca-cert" = { - sopsFile = (lib.snowfall.fs.get-file "secrets/pi5-secrets.yaml"); - owner = "nebula-jallen-nebula"; - group = "nebula-jallen-nebula"; - restartUnits = [ "nebula@jallen-nebula.service" ]; - }; - - "pi5/nebula/ca-key" = { - sopsFile = (lib.snowfall.fs.get-file "secrets/pi5-secrets.yaml"); - owner = "nebula-jallen-nebula"; - group = "nebula-jallen-nebula"; - restartUnits = [ "nebula@jallen-nebula.service" ]; - }; - - "pi5/nebula/lighthouse-cert" = { - sopsFile = (lib.snowfall.fs.get-file "secrets/pi5-secrets.yaml"); - owner = "nebula-jallen-nebula"; - group = "nebula-jallen-nebula"; - restartUnits = [ "nebula@jallen-nebula.service" ]; - }; - "pi5/nebula/lighthouse-key" = { - sopsFile = (lib.snowfall.fs.get-file "secrets/pi5-secrets.yaml"); - owner = "nebula-jallen-nebula"; - group = "nebula-jallen-nebula"; - restartUnits = [ "nebula@jallen-nebula.service" ]; - }; - }; - }; - }; -} diff --git a/modules/nixos/services/nebula/default.nix b/modules/nixos/services/nebula/default.nix index 8894356..6fa0db3 100644 --- a/modules/nixos/services/nebula/default.nix +++ b/modules/nixos/services/nebula/default.nix @@ -8,54 +8,105 @@ with lib; let name = "nebula"; + cfg = config.${namespace}.services.${name}; - ca = config.sops.secrets."jallen-nas/nebula/ca-cert".path; - cert = config.sops.secrets."jallen-nas/nebula/nas-cert".path; - key = config.sops.secrets."jallen-nas/nebula/nas-key".path; + ca = config.sops.secrets."${cfg.secretsPrefix}/ca-cert".path; + cert = config.sops.secrets."${cfg.secretsPrefix}/${cfg.hostSecretName}-cert".path; + key = config.sops.secrets."${cfg.secretsPrefix}/${cfg.hostSecretName}-key".path; nebulaConfig = lib.${namespace}.mkModule { inherit config name; - description = "nebula"; - options = { }; + description = "nebula overlay network node"; + options = { + # ----------------------------------------------------------------------- + # Role + # ----------------------------------------------------------------------- + isLighthouse = lib.${namespace}.mkBoolOpt false "Act as a Nebula lighthouse"; + isRelay = lib.${namespace}.mkBoolOpt false "Act as a Nebula relay node"; + + # ----------------------------------------------------------------------- + # Network identity + # ----------------------------------------------------------------------- + networkName = + lib.${namespace}.mkOpt types.str "jallen-nebula" + "Nebula network name (used as the systemd service suffix and interface name)"; + + # ----------------------------------------------------------------------- + # SOPS secret location + # + # secretsPrefix — key path prefix inside the SOPS file, e.g. "pi5/nebula" + # The module expects three secrets under this prefix: + # /ca-cert + # /host-cert + # /host-key + # + # secretsFile — path to the SOPS-encrypted YAML that holds the secrets + # ----------------------------------------------------------------------- + secretsPrefix = lib.${namespace}.mkOpt types.str "" "SOPS secret key prefix, e.g. \"pi5/nebula\""; + secretsFile = + lib.${namespace}.mkOpt types.str "" + "Path to the SOPS secrets YAML file for this host"; + + # hostSecretName — the middle segment of the cert/key secret names. + # Secrets are looked up as /-cert and + # /-key. Defaults to "host"; set to + # e.g. "nas", "lighthouse", or the machine hostname to match existing + # SOPS YAML keys without renaming them. + hostSecretName = + lib.${namespace}.mkOpt types.str "host" + "Secret name segment for cert/key (e.g. \"nas\" → looks for nas-cert / nas-key)"; + + # ----------------------------------------------------------------------- + # Peer addressing (ignored on lighthouse nodes) + # ----------------------------------------------------------------------- + lighthouses = + lib.${namespace}.mkOpt (types.listOf types.str) [ ] + "Nebula overlay IPs of lighthouse nodes (leave empty on lighthouses)"; + + staticHostMap = lib.${namespace}.mkOpt (types.attrsOf ( + types.listOf types.str + )) { } "Static host map: overlay IP → list of public addr:port strings"; + + # ----------------------------------------------------------------------- + # Firewall rules inside the overlay + # ----------------------------------------------------------------------- + inboundRules = lib.${namespace}.mkOpt (types.listOf types.attrs) [ + { + port = "any"; + proto = "any"; + host = "any"; + } + ] "Nebula inbound firewall rules"; + + outboundRules = lib.${namespace}.mkOpt (types.listOf types.attrs) [ + { + port = "any"; + proto = "any"; + host = "any"; + } + ] "Nebula outbound firewall rules"; + }; moduleConfig = { environment.systemPackages = with pkgs; [ nebula ]; - services.nebula.networks = { - jallen-nebula = { - enable = true; - enableReload = true; - isLighthouse = false; - isRelay = false; - ca = ca; - cert = cert; - key = key; - lighthouses = [ - "10.1.1.1" - ]; - staticHostMap = { - "10.1.1.1" = [ - "mjallen.dev:4242" - ]; - }; - settings = { - firewall = { - outbound = [ - { - # Allow all outbound traffic from this node - port = "any"; - proto = "any"; - host = "any"; - } - ]; - inbound = [ - { - # Allow all outbound traffic from this node - port = "any"; - proto = "any"; - host = "any"; - } - ]; - }; - }; + + services.nebula.networks.${cfg.networkName} = { + enable = true; + enableReload = true; + isLighthouse = cfg.isLighthouse; + isRelay = cfg.isRelay; + inherit ca cert key; + + lighthouses = cfg.lighthouses; + staticHostMap = cfg.staticHostMap; + + listen = { + host = cfg.listenAddress; + port = cfg.port; + }; + + settings.firewall = { + inbound = cfg.inboundRules; + outbound = cfg.outboundRules; }; }; }; diff --git a/modules/nixos/services/nebula/sops.nix b/modules/nixos/services/nebula/sops.nix index 035c11a..f3e9bdf 100644 --- a/modules/nixos/services/nebula/sops.nix +++ b/modules/nixos/services/nebula/sops.nix @@ -7,39 +7,34 @@ with lib; let cfg = config.${namespace}.services.nebula; + sopsFile = cfg.secretsFile; + nebulaUser = "nebula-${cfg.networkName}"; + nebulaUnit = "nebula@${cfg.networkName}.service"; + + mkSecret = _key: { + inherit sopsFile; + owner = nebulaUser; + group = nebulaUser; + restartUnits = [ nebulaUnit ]; + }; in { - config = mkIf cfg.enable { - sops = { - secrets = { - "jallen-nas/nebula/ca-cert" = { - sopsFile = (lib.snowfall.fs.get-file "secrets/nas-secrets.yaml"); - owner = "nebula-jallen-nebula"; - group = "nebula-jallen-nebula"; - restartUnits = [ "nebula@jallen-nebula.service" ]; - }; + assertions = [ + { + assertion = cfg.secretsPrefix != ""; + message = "mjallen.services.nebula.secretsPrefix must be set (e.g. \"pi5/nebula\")"; + } + { + assertion = cfg.secretsFile != ""; + message = "mjallen.services.nebula.secretsFile must be set to the path of the SOPS secrets YAML"; + } + ]; - "jallen-nas/nebula/ca-key" = { - sopsFile = (lib.snowfall.fs.get-file "secrets/nas-secrets.yaml"); - owner = "nebula-jallen-nebula"; - group = "nebula-jallen-nebula"; - restartUnits = [ "nebula@jallen-nebula.service" ]; - }; - - "jallen-nas/nebula/nas-cert" = { - sopsFile = (lib.snowfall.fs.get-file "secrets/nas-secrets.yaml"); - owner = "nebula-jallen-nebula"; - group = "nebula-jallen-nebula"; - restartUnits = [ "nebula@jallen-nebula.service" ]; - }; - "jallen-nas/nebula/nas-key" = { - sopsFile = (lib.snowfall.fs.get-file "secrets/nas-secrets.yaml"); - owner = "nebula-jallen-nebula"; - group = "nebula-jallen-nebula"; - restartUnits = [ "nebula@jallen-nebula.service" ]; - }; - }; + sops.secrets = { + "${cfg.secretsPrefix}/ca-cert" = mkSecret "ca-cert"; + "${cfg.secretsPrefix}/${cfg.hostSecretName}-cert" = mkSecret "host-cert"; + "${cfg.secretsPrefix}/${cfg.hostSecretName}-key" = mkSecret "host-key"; }; }; } diff --git a/packages/cockpit-benchmark/default.nix b/packages/cockpit-benchmark/default.nix index cc65931..16d55df 100644 --- a/packages/cockpit-benchmark/default.nix +++ b/packages/cockpit-benchmark/default.nix @@ -6,6 +6,7 @@ nodejs, npmHooks, fetchNpmDeps, + requireFile, stdenv, }: let @@ -16,6 +17,87 @@ let selected = selectVariant versionSpec null null; sources = mkAllSources selected; inherit (selected.variables) version; + + # These three packages are published exclusively to GitHub Packages + # (npm.pkg.github.com) which requires authentication — they cannot be + # fetched in the Nix sandbox without a token. Add them to the Nix store + # manually first: + # + # TOKEN= + # for pkg in \ + # "download/@45Drives/cockpit-css/0.1.12/42ecb717da68e4c31454b5bc6019e3a4fe7dcd6c" \ + # "download/@45Drives/cockpit-helpers/0.1.19/29920cb9a0866220cfda58b9db4f34c8435f8960" \ + # "download/@45Drives/cockpit-vue-components/0.1.0-beta2/a216666b3e9eb97a30cf47c0b947b4e5661798db" + # do + # curl -L -u "user:$TOKEN" \ + # "https://npm.pkg.github.com/$pkg" \ + # -o "$(basename $pkg).tgz" + # done + # nix store add-file *.tgz + cockpitCssTarball = requireFile { + name = "cockpit-css-0.1.12.tgz"; + # sha512 integrity from yarn.lock + hash = "sha512-RgyedX/5GuriDvLruvmbqb++cIBqIF5py2E4G8/wMtQfUf6QnJqsRLYNp7uzKcLpE2+kjm9pGASdvwV2I2b/YA=="; + message = '' + cockpit-benchmark requires @45drives/cockpit-css which is only available + on GitHub Packages (requires authentication). Download it manually: + + curl -L -u "user:" \ + "https://npm.pkg.github.com/download/@45Drives/cockpit-css/0.1.12/42ecb717da68e4c31454b5bc6019e3a4fe7dcd6c" \ + -o cockpit-css-0.1.12.tgz + nix store add-file cockpit-css-0.1.12.tgz + ''; + }; + + cockpitHelpersTarball = requireFile { + name = "cockpit-helpers-0.1.19.tgz"; + hash = "sha512-dyS3V+XG/9rLGGhFJEA2b+ohrZiGsHteOEktJk9mM9c6WsXJywrlnrHm0YrKksoTUPwXtBY44QrXJNd3zKtvKQ=="; + message = '' + cockpit-benchmark requires @45drives/cockpit-helpers which is only available + on GitHub Packages (requires authentication). Download it manually: + + curl -L -u "user:" \ + "https://npm.pkg.github.com/download/@45Drives/cockpit-helpers/0.1.19/29920cb9a0866220cfda58b9db4f34c8435f8960" \ + -o cockpit-helpers-0.1.19.tgz + nix store add-file cockpit-helpers-0.1.19.tgz + ''; + }; + + cockpitVueComponentsTarball = requireFile { + name = "cockpit-vue-components-0.1.0-beta2.tgz"; + hash = "sha512-QvKCuUlCA9LLrwdGKf1iveiKUvJaExmNZxOUyhxg63BvQwn9rs26uN/MFS2mweCi++Mv6kyN+dJG62L7qCSOXQ=="; + message = '' + cockpit-benchmark requires @45drives/cockpit-vue-components which is only + available on GitHub Packages (requires authentication). Download it manually: + + curl -L -u "user:" \ + "https://npm.pkg.github.com/download/@45Drives/cockpit-vue-components/0.1.0-beta2/a216666b3e9eb97a30cf47c0b947b4e5661798db" \ + -o cockpit-vue-components-0.1.0-beta2.tgz + nix store add-file cockpit-vue-components-0.1.0-beta2.tgz + ''; + }; + + # Rewrite the package-lock.json to point the three GitHub Packages deps at + # their local store paths so fetchNpmDeps / npm ci can find them offline. + patchedPackageLock = + let + lockFile = "${sources.src}/benchmark/package-lock.json"; + in + stdenv.mkDerivation { + name = "cockpit-benchmark-package-lock-patched.json"; + nativeBuildInputs = [ jq ]; + buildCommand = '' + jq \ + --arg cssPath "${cockpitCssTarball}" \ + --arg helpersPath "${cockpitHelpersTarball}" \ + --arg vuePath "${cockpitVueComponentsTarball}" \ + ' + .packages["node_modules/@45drives/cockpit-css"].resolved = ("file://" + $cssPath) | + .packages["node_modules/@45drives/cockpit-helpers"].resolved = ("file://" + $helpersPath) | + .packages["node_modules/@45drives/cockpit-vue-components"].resolved = ("file://" + $vuePath) + ' ${lockFile} > $out + ''; + }; in stdenv.mkDerivation (finalAttrs: { pname = "cockpit-benchmark"; @@ -25,6 +107,7 @@ stdenv.mkDerivation (finalAttrs: { npmDeps = fetchNpmDeps { src = "${finalAttrs.src}/benchmark"; + packageLock = patchedPackageLock; hash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; }; @@ -33,19 +116,21 @@ stdenv.mkDerivation (finalAttrs: { nodejs npmHooks.npmConfigHook npmHooks.npmBuildHook - npmHooks.npmInstallHook ]; # fio is the runtime benchmark tool invoked by the plugin's backend script. passthru.cockpitPath = [ fio ]; - # npmConfigHook expects to run in the directory with package.json preConfigure = '' - cd benchmark + # npmConfigHook expects package.json and node_modules in the working dir + cp -r benchmark/. . + # replace package-lock.json with the patched version that uses store paths + cp ${patchedPackageLock} package-lock.json + chmod u+w package-lock.json ''; - # Write version.js before vite build (mirrors what the Makefile does) preBuild = '' + # Write version.js before vite build (mirrors what the Makefile does) pluginVersion="$(jq -r '.version' ../manifest.json)-$(jq -r '.build_number' ../manifest.json)" echo "export const pluginVersion = \"''${pluginVersion}\";" > src/version.js ''; diff --git a/packages/cockpit-benchmark/version.json b/packages/cockpit-benchmark/version.json index b3c8b94..7ffd6e9 100644 --- a/packages/cockpit-benchmark/version.json +++ b/packages/cockpit-benchmark/version.json @@ -9,7 +9,7 @@ "owner": "45Drives", "repo": "cockpit-benchmark", "tag": "v${version}", - "hash": "sha256-YmOCdqAdYPnNcXqCccvI0nVhR/EdYXdkeenmy4DdGK0=" + "hash": "sha256-AYGaTwNNAH4rl+AdYAoktkjVBcrBqYcfaihYAEq2Dic=" } } } diff --git a/packages/cockpit-machines/version.json b/packages/cockpit-machines/version.json index 48dd326..49f4d57 100644 --- a/packages/cockpit-machines/version.json +++ b/packages/cockpit-machines/version.json @@ -11,14 +11,14 @@ "owner": "cockpit-project", "repo": "cockpit-machines", "tag": "${version}", - "hash": "sha256-jP/ffo0kwju0xxKMHhDwOsNKO7HjOaJZ/uXYijsElNg=" + "hash": "sha256-HAXOjk77y17vI/x4kFCZVNnRYrZFpqhw8PsVKmnznBM=" }, "nodeModules": { "fetcher": "github", "owner": "cockpit-project", "repo": "node-cache", "rev": "${nodeCacheRev}", - "hash": "sha256-yonEQ7hRskbFDnW/Pc3aOeV7bgu1LCYpi1Ok5/aHeC8=" + "hash": "sha256-VZwFbOxtUVYXpIeCpAR3bUOjacf6DXgO6EBHPFWczQU=" }, "cockpitLib": { "fetcher": "github", diff --git a/packages/cockpit-podman/default.nix b/packages/cockpit-podman/default.nix index f525d42..9a30c52 100644 --- a/packages/cockpit-podman/default.nix +++ b/packages/cockpit-podman/default.nix @@ -1,55 +1,55 @@ { fetchFromGitHub, lib, + namespace, nodejs, stdenv, }: +let + inherit (lib.trivial) importJSON; + inherit (lib.${namespace}) mkAllSources selectVariant; -stdenv.mkDerivation (finalAttrs: { + versionSpec = importJSON ./version.json; + selected = selectVariant versionSpec null null; + sources = mkAllSources selected; + inherit (selected.variables) version; +in +stdenv.mkDerivation { pname = "cockpit-podman"; - version = "123"; + inherit version; - src = fetchFromGitHub { - owner = "cockpit-project"; - repo = "cockpit-podman"; - tag = finalAttrs.version; - hash = "sha256-N5nhJU9XUsxLWq3mk3bSyorHEM4zSLHt9I+zkdgU2Vk="; - }; + src = sources.src; # Pre-vendored node_modules from cockpit-project/node-cache, pinned via the # node_modules submodule reference in the source tree. - nodeModules = fetchFromGitHub { - owner = "cockpit-project"; - repo = "node-cache"; - rev = "e39ef3621b5aefa5bf1c2de7e66a5918fcef620c"; - hash = "sha256-+yhHsGEN1IqIxPY7vQysp1ZczcHzXRoNIVN3DyVgwB8="; - }; + inherit (sources) nodeModules; - # pkg/lib is checked out from the main cockpit repo at the pinned commit - # referenced in the Makefile (COCKPIT_REPO_COMMIT). - cockpitLib = fetchFromGitHub { - owner = "cockpit-project"; - repo = "cockpit"; - rev = "5fb84eaefbc5ff4433a21bc452270af8d09e1ab7"; - hash = "sha256-FR6TIKQ+3GuDMOMEivDxEx6E/SVIAXh9Cg36JJ694Wc="; - }; + # pkg/lib checked out from the main cockpit repo at the commit pinned in + # the Makefile (COCKPIT_REPO_COMMIT). + inherit (sources) cockpitLib; nativeBuildInputs = [ nodejs ]; - # cockpit-podman uses passthru.cockpitPath for the NixOS cockpit module to - # add runtime dependencies to the cockpit service's PATH. + # passthru.cockpitPath is used by the NixOS cockpit module to add runtime + # dependencies to the cockpit service's PATH. passthru.cockpitPath = [ ]; configurePhase = '' runHook preConfigure - # Wire up the vendored node_modules - cp -r ${finalAttrs.nodeModules} node_modules + # Replace the empty git submodule placeholder with the real vendored modules. + # Use dotglob so hidden entries (.bin, .package-lock.json, etc.) are included. + rm -rf node_modules + mkdir node_modules + (shopt -s dotglob; cp -r $nodeModules/* node_modules/) chmod -R u+w node_modules - # Wire up pkg/lib from the pinned cockpit repo + # Node needs package-lock.json at the project root to resolve modules. + cp node_modules/.package-lock.json package-lock.json + + # Wire up pkg/lib from the pinned cockpit repo. mkdir -p pkg - cp -r ${finalAttrs.cockpitLib}/pkg/lib pkg/lib + cp -r $cockpitLib/pkg/lib pkg/lib chmod -R u+w pkg runHook postConfigure @@ -75,9 +75,9 @@ stdenv.mkDerivation (finalAttrs: { meta = { description = "Cockpit UI for Podman containers"; homepage = "https://github.com/cockpit-project/cockpit-podman"; - changelog = "https://github.com/cockpit-project/cockpit-podman/releases/tag/${finalAttrs.version}"; + changelog = "https://github.com/cockpit-project/cockpit-podman/releases/tag/${version}"; license = lib.licenses.lgpl21Plus; platforms = lib.platforms.linux; maintainers = [ ]; }; -}) +} diff --git a/systems/aarch64-linux/pi5/default.nix b/systems/aarch64-linux/pi5/default.nix index f32a83e..78e4712 100644 --- a/systems/aarch64-linux/pi5/default.nix +++ b/systems/aarch64-linux/pi5/default.nix @@ -3,6 +3,7 @@ # https://search.nixos.org/options and in the NixOS manual (`nixos-help`). { + lib, namespace, ... }: @@ -103,9 +104,13 @@ # ################################################### services = { - nebula-lighthouse = { + nebula = { enable = true; + isLighthouse = true; port = 4242; + secretsPrefix = "pi5/nebula"; + secretsFile = lib.snowfall.fs.get-file "secrets/pi5-secrets.yaml"; + hostSecretName = "lighthouse"; }; }; diff --git a/systems/x86_64-linux/jallen-nas/apps.nix b/systems/x86_64-linux/jallen-nas/apps.nix index 9f110ab..4033531 100755 --- a/systems/x86_64-linux/jallen-nas/apps.nix +++ b/systems/x86_64-linux/jallen-nas/apps.nix @@ -158,6 +158,13 @@ in nebula = { enable = true; port = 4242; + lighthouses = [ "10.1.1.1" ]; + staticHostMap = { + "10.1.1.1" = [ "mjallen.dev:4242" ]; + }; + secretsPrefix = "jallen-nas/nebula"; + secretsFile = lib.snowfall.fs.get-file "secrets/nas-secrets.yaml"; + hostSecretName = "nas"; }; netbootxyz = { enable = false; diff --git a/systems/x86_64-linux/jallen-nas/nas-defaults.nix b/systems/x86_64-linux/jallen-nas/nas-defaults.nix index 4f88896..0d204c0 100644 --- a/systems/x86_64-linux/jallen-nas/nas-defaults.nix +++ b/systems/x86_64-linux/jallen-nas/nas-defaults.nix @@ -63,7 +63,6 @@ in "minecraft" "mongodb" "nebula" - "nebula-lighthouse" "netbootxyz" "nextcloud" "ntfy"