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
This commit is contained in:
parent
a0da5be8fc
commit
c2db28eb29
1 changed files with 97 additions and 134 deletions
231
AGENTS.md
231
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 .#<hostname> \
|
||||
--target-host <user>@<ip> --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 <hostname>
|
||||
|
||||
# Install NixOS on a new machine (run from NixOS live ISO)
|
||||
nix run .#install -- -n <hostname> -r <REPO_URL>
|
||||
|
||||
# Deploy to all configured hosts
|
||||
nix run .#deploy
|
||||
|
||||
# Rebuild NixOS/Home Manager configuration
|
||||
nix run .#rebuild -- nixos
|
||||
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 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/<host>/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/<host>/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/<host>/services/`
|
||||
- [ ] New modules registered in `modules/nixos/default.nix`
|
||||
- [ ] Documentation in English
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue