cryodev/AGENTS.md
steffen c2db28eb29 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
2026-03-14 15:36:18 +01:00

6 KiB

Agent Guidelines for NixOS Configuration

Project Overview

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/
  • Apps: create, deploy, install, rebuild in apps/
  • Templates: raspberry-pi, generic-server for bootstrapping new hosts

Build & Development Commands

# 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

# Update flake inputs
nix flake update

# Enter development shell
nix develop

Deployment

Both hosts use Comin for automatic pull-based deployment (polls the git repo). Manual deployment is only needed for initial setup or emergencies:

# Deploy via deploy app (uses deploy.json, SSH port 2299, asks sudo password)
nix run .#deploy -- -n cryodev-main

# Manual deployment via nixos-rebuild
NIX_SSHOPTS="-p 2299" nixos-rebuild switch --flake .#<hostname> \
  --target-host <user>@<ip> --sudo --ask-sudo-password

Apps

nix run .#create -- -t generic-server -n <hostname>   # Scaffold new host
nix run .#install -- -n <hostname> -r <REPO_URL>       # Install from NixOS ISO
nix run .#deploy -- -n <hostname>                      # Deploy to host
nix run .#rebuild -- nixos                             # Rebuild locally

Code Style & Conventions

Formatting

  • Tool: nixfmt via git-hooks.nix (pre-commit)
  • Run: nix fmt before every commit
  • Indentation: 2 spaces (enforced by formatter)

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

{ config, lib, ... }:

let
  cfg = config.services.myService;
  inherit (lib) mkDefault mkEnableOption mkIf mkOption types;
in
{
  options.services.myService = {
    enable = mkEnableOption "My service";
    port = mkOption {
      type = types.port;
      default = 8080;
      description = "Port to listen on";
    };
  };

  config = mkIf cfg.enable {
    assertions = [
      { assertion = cfg.port > 1024; message = "Port must be > 1024"; }
    ];
    # Implementation
  };
}

Key Rules

  • 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/<host>/services/:

# hosts/cryodev-main/services/myservice.nix
{ outputs, constants, ... }:
{
  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}";
    };
  };
}

Special Args Available in Modules

  • 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, mkNixosConfiguration
├── constants.nix          # Central config (domains, IPs, ports)
├── hosts/
│   ├── 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 (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)

Verification Checklist

Before committing:

  • nix fmt passes
  • 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/<host>/services/
  • New modules registered in modules/nixos/default.nix
  • Documentation in English