From c2db28eb29fd0dfbe476c3cef125d836f6901d20 Mon Sep 17 00:00:00 2001 From: steffen Date: Sat, 14 Mar 2026 15:36:18 +0100 Subject: [PATCH] rewrite AGENTS.md: comprehensive agent guidelines Rewritten from scratch with accurate project state: - Correct deployment strategy (Comin, not deploy-rs) - All 4 apps documented (create, deploy, install, rebuild) - Module pattern with inherit and assertions - Host service file pattern with constants usage - lib.utils helpers documented - Secret path naming convention - Complete directory structure with current file layout - Verification checklist including English docs requirement --- AGENTS.md | 231 +++++++++++++++++++++++------------------------------- 1 file changed, 97 insertions(+), 134 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 367ea5c..3cec9b0 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -2,37 +2,33 @@ ## Project Overview -This repository contains a NixOS configuration managed with Nix Flakes. It defines: -- **Hosts**: `cryodev-main` (x86_64 server), `cryodev-pi` (aarch64 Raspberry Pi) +NixOS infrastructure managed with Nix Flakes. Two hosts, reusable modules, SOPS secrets, Comin auto-deployment. + +- **Hosts**: `cryodev-main` (x86_64 server), `cryodev-pi` (aarch64 Raspberry Pi 4) - **Modules**: Reusable NixOS modules in `modules/nixos/` -- **Packages**: Custom packages in `pkgs/` +- **Apps**: `create`, `deploy`, `install`, `rebuild` in `apps/` - **Templates**: `raspberry-pi`, `generic-server` for bootstrapping new hosts ## Build & Development Commands -### Prerequisites -- **Nix** with Flakes enabled -- **Git** - -### Core Commands - ```bash -# Build host configuration +# Format code (required before committing, runs nixfmt via pre-commit) +nix fmt + +# Run all checks (formatting, package builds, overlay builds) +nix flake check + +# Quick evaluation test (faster than full build, use to validate changes) +nix eval .#nixosConfigurations.cryodev-main.config.system.build.toplevel.name +nix eval .#nixosConfigurations.cryodev-pi.config.system.build.toplevel.name + +# Full build nix build .#nixosConfigurations.cryodev-main.config.system.build.toplevel nix build .#nixosConfigurations.cryodev-pi.config.system.build.toplevel # Build Raspberry Pi SD image (requires binfmt on x86_64) nix build .#nixosConfigurations.cryodev-pi.config.system.build.sdImage -# Format code (required before committing) -nix fmt - -# Run all checks (lint, formatting) -nix flake check - -# Quick evaluation test (faster than full build) -nix eval .#nixosConfigurations.cryodev-main.config.system.build.toplevel.name - # Update flake inputs nix flake update @@ -42,176 +38,143 @@ nix develop ### Deployment -```bash -# Deploy all hosts via deploy app (uses deploy.json) -nix run .#deploy +Both hosts use **Comin** for automatic pull-based deployment (polls the git repo). +Manual deployment is only needed for initial setup or emergencies: -# Deploy a specific host +```bash +# Deploy via deploy app (uses deploy.json, SSH port 2299, asks sudo password) nix run .#deploy -- -n cryodev-main -# Manual deployment via SSH +# Manual deployment via nixos-rebuild NIX_SSHOPTS="-p 2299" nixos-rebuild switch --flake .# \ --target-host @ --sudo --ask-sudo-password ``` -> **Note:** Both hosts use Comin for automatic pull-based deployment. -> Manual deployment is only needed for the initial setup or emergencies. - ### Apps ```bash -# Create a new host from template -nix run .#create -- -t generic-server -n - -# Install NixOS on a new machine (run from NixOS live ISO) -nix run .#install -- -n -r - -# Deploy to all configured hosts -nix run .#deploy - -# Rebuild NixOS/Home Manager configuration -nix run .#rebuild -- nixos +nix run .#create -- -t generic-server -n # Scaffold new host +nix run .#install -- -n -r # Install from NixOS ISO +nix run .#deploy -- -n # Deploy to host +nix run .#rebuild -- nixos # Rebuild locally ``` ## Code Style & Conventions ### Formatting -- **Tool**: `nixfmt` via pre-commit hooks +- **Tool**: `nixfmt` via `git-hooks.nix` (pre-commit) - **Run**: `nix fmt` before every commit -- **Indentation**: 2 spaces -- **Line length**: 80-100 characters (follow formatter) +- **Indentation**: 2 spaces (enforced by formatter) -### Module Structure +### Naming Conventions + +| Type | Convention | Example | +|------|------------|---------| +| Files | kebab-case | `hardware-configuration.nix` | +| NixOS options | camelCase | `services.myService.enable` | +| Let bindings | camelCase | `let myValue = ...;` | +| Hosts | kebab-case | `cryodev-main`, `cryodev-pi` | +| Secret paths | kebab-case with `/` | `forgejo-runner/token` | + +### Module Pattern ```nix -# Standard module pattern -{ config, lib, pkgs, inputs, outputs, constants, ... }: +{ config, lib, ... }: let cfg = config.services.myService; + inherit (lib) mkDefault mkEnableOption mkIf mkOption types; in { options.services.myService = { - enable = lib.mkEnableOption "My service"; - port = lib.mkOption { - type = lib.types.port; + enable = mkEnableOption "My service"; + port = mkOption { + type = types.port; default = 8080; description = "Port to listen on"; }; }; - config = lib.mkIf cfg.enable { - # Implementation here + config = mkIf cfg.enable { + assertions = [ + { assertion = cfg.port > 1024; message = "Port must be > 1024"; } + ]; + # Implementation }; } ``` -### Naming Conventions -| Type | Convention | Example | -|------|------------|---------| -| Files | kebab-case | `hardware-configuration.nix` | -| Options | camelCase | `services.myService.enable` | -| Variables | camelCase | `let myValue = ...;` | -| Hosts | kebab-case | `cryodev-main`, `cryodev-pi` | +### Key Rules -### Imports +- **Use `lib.mkDefault`** for all module defaults (allows host-level overrides) +- **Use `constants.nix`** for domains, IPs, ports -- never hardcode these +- **Use `lib.utils`** helpers: `mkReverseProxyOption`, `mkVirtualHost`, `mkUrl` +- **Secrets via SOPS** only, never plaintext. Reference: `config.sops.secrets."path".path` +- **Imports**: relative paths for local files, `outputs.nixosModules.*` for shared modules, `inputs.*` for external +- **Assertions** for invalid configurations, `warnings` for non-critical issues +- **`lib.inherit`** pattern: extract needed functions in `let` block + +### Host Service Files + +Each service gets its own file in `hosts//services/`: ```nix -# Local modules: relative paths -imports = [ ./hardware.nix ./networking.nix ]; - -# Shared modules: via outputs -imports = [ outputs.nixosModules.common ]; - -# External inputs -imports = [ inputs.sops-nix.nixosModules.sops ]; -``` - -### Constants -Use `constants.nix` for domains, IPs, and ports: -```nix -{ constants, ... }: +# hosts/cryodev-main/services/myservice.nix +{ outputs, constants, ... }: { - services.nginx.virtualHosts."${constants.services.forgejo.fqdn}" = { ... }; + imports = [ outputs.nixosModules.myservice ]; + + services.myservice = { + enable = true; + port = constants.services.myservice.port; + }; + + services.nginx.virtualHosts."${constants.services.myservice.fqdn}" = { + forceSSL = true; + enableACME = true; + locations."/" = { + proxyPass = "http://127.0.0.1:${toString constants.services.myservice.port}"; + }; + }; } ``` -### Error Handling -```nix -config = lib.mkIf cfg.enable { - assertions = [ - { assertion = cfg.port > 1024; message = "Port must be > 1024"; } - ]; - warnings = lib.optional (cfg.debug) "Debug mode enabled!"; -}; -``` +### Special Args Available in Modules -### Option Conflicts -Use `lib.mkDefault` for default values that can be overridden: -```nix -services.nginx.enable = lib.mkDefault true; -``` +- `inputs` -- flake inputs (nixpkgs, sops-nix, comin, headplane, etc.) +- `outputs` -- this flake's outputs (nixosModules, packages) +- `constants` -- values from `constants.nix` (domain, hosts, services) +- `lib` -- nixpkgs.lib extended with `lib.utils` ## Directory Structure ``` . -├── flake.nix # Entry point, inputs/outputs +├── flake.nix # Entry point, inputs/outputs, mkNixosConfiguration ├── constants.nix # Central config (domains, IPs, ports) ├── hosts/ -│ ├── cryodev-main/ # x86_64 server -│ │ ├── default.nix # Host entry point -│ │ ├── hardware.nix # Hardware configuration -│ │ ├── services/ # Service configurations -│ │ └── secrets.yaml # SOPS-encrypted secrets -│ └── cryodev-pi/ # aarch64 Raspberry Pi -├── modules/nixos/ # Reusable modules -│ ├── common/ # Shared base configuration -│ ├── sops/ # Secret management -│ ├── forgejo/ # Git server -│ ├── headscale/ # VPN control server -│ └── ... -├── lib/utils.nix # Helper functions -├── apps/ # Nix apps (rebuild) +│ ├── cryodev-main/ # x86_64 server (services/, secrets.yaml, binfmt.nix) +│ └── cryodev-pi/ # aarch64 RPi (services/, secrets.yaml, sd-image.nix) +├── modules/nixos/ # Reusable modules (common, forgejo, headscale, ...) +├── users/ # User definitions (steffen, ralph, benjamin) +├── apps/ # Nix apps (create, deploy, install, rebuild) +├── lib/utils.nix # Helper functions (mkUrl, mkVirtualHost, ...) ├── pkgs/ # Custom packages ├── overlays/ # Nixpkgs overlays -├── templates/ # Host templates -├── scripts/ # Helper scripts (install.sh) -└── docs/ # Documentation +├── templates/ # Host templates (generic-server, raspberry-pi) +├── deploy.json # Deploy app config (hosts, SSH port) +├── .sops.yaml # SOPS encryption rules (age keys per host) +├── .forgejo/workflows/ # CI pipelines (ci.yml, deploy.yml) +└── docs/ # Documentation (English) ``` -## Key Patterns - -### Adding a New Raspberry Pi Host -1. Copy template: `cp -r templates/raspberry-pi hosts/new-pi` -2. Update `hosts/new-pi/networking.nix` (hostname) -3. Add to `flake.nix`: `new-pi = mkNixosConfiguration "aarch64-linux" [ ./hosts/new-pi ];` -4. Add to `.forgejo/workflows/build-pi-image.yml` matrix -5. Push → SD image is built automatically - -### SOPS Secrets -- Secrets encrypted with age using SSH host keys -- Config in `.sops.yaml`, secrets in `hosts//secrets.yaml` -- Reference: `config.sops.secrets."path/to/secret".path` - -### Special Args Available in Modules -- `inputs`: Flake inputs (nixpkgs, sops-nix, etc.) -- `outputs`: This flake's outputs (nixosModules, packages) -- `constants`: Values from `constants.nix` -- `lib`: Extended nixpkgs.lib with `lib.utils` - -## Deployment Workflows - -| Host | Strategy | Trigger | -|------|----------|---------| -| `cryodev-main` | Pull via Comin | Automatic polling | -| `cryodev-pi` | Pull via Comin | Automatic polling | -| SD Images | Built in CI | Push to main (for Pi hosts) | - ## Verification Checklist Before committing: - [ ] `nix fmt` passes -- [ ] `nix flake check` passes (or at least `nix eval` works) -- [ ] New hosts added to `flake.nix` +- [ ] `nix flake check` passes (or at least `nix eval` works for both hosts) +- [ ] New hosts added to `flake.nix` nixosConfigurations - [ ] Constants in `constants.nix`, not hardcoded - [ ] Secrets use SOPS, not plaintext +- [ ] New services have their own file in `hosts//services/` +- [ ] New modules registered in `modules/nixos/default.nix` +- [ ] Documentation in English