# Athenix Secrets System Design

## Overview

The Athenix secrets management system integrates ragenix (agenix) with automatic host discovery based on the repository's fleet inventory structure. It provides a seamless workflow for managing encrypted secrets across all systems.

## Architecture

### Auto-Discovery Module (`sw/secrets.nix`)

**Purpose**: Automatically load and configure secrets at system deployment time.

**Features**:
- Discovers `.age` encrypted files from `secrets/` directories
- Loads global secrets from `secrets/global/` on ALL systems
- Loads host-specific secrets from `secrets/{hostname}/` on matching hosts
- Auto-configures decryption keys based on `.pub` files in directories
- Supports custom secret configuration via `default.nix` in each directory

**Key Behaviors**:
- Secrets are decrypted to `/run/agenix/{name}` at boot
- Identity paths include: system SSH keys + global keys + host-specific keys
- Host-specific secrets override global secrets with the same name

### Dynamic Recipients Configuration (`secrets/secrets.nix`)

**Purpose**: Generate ragenix recipient configuration from directory structure.

**Features**:
- Automatically discovers hosts from `secrets/` subdirectories
- Reads age public keys from `.age.pub` files (converted from SSH keys)
- Generates recipient lists based on secret location:
  - `secrets/global/*.age` → ALL hosts + admins
  - `secrets/{hostname}/*.age` → that host + global keys + admins
- Supports admin keys in `secrets/admins/` for secret editing

**Key Behaviors**:
- No manual recipient list maintenance required
- Adding a new host = create directory + add .pub key + run `update-age-keys.sh`
- Works with ragenix CLI: `ragenix -e`, `ragenix -r`

## Workflow

### Adding a New Host

1. **Capture SSH host key**:
   ```bash
   # From the running system
   cat /etc/ssh/ssh_host_ed25519_key.pub > secrets/new-host/ssh_host_ed25519_key.pub
   ```

2. **Convert to age format**:
   ```bash
   cd secrets/
   ./update-age-keys.sh
   ```

3. **Re-key existing secrets** (if needed):
   ```bash
   ragenix -r
   ```

### Creating a New Secret

1. **Choose location**:
   - `secrets/global/` → all systems can decrypt
   - `secrets/{hostname}/` → only that host can decrypt

2. **Create/edit secret**:
   ```bash
   ragenix -e secrets/global/my-secret.age
   ```
   
3. **Recipients are auto-determined** from `secrets.nix`:
   - Global secrets: all host keys + admin keys
   - Host-specific: that host + global keys + admin keys

### Cross-Host Secret Management

Any Athenix host can manage secrets for other hosts because:
- All public keys are in the repository (`*.age.pub` files)
- `secrets/secrets.nix` auto-generates recipient lists
- Hosts decrypt using their own private keys (not shared)

Example: From `nix-builder`, create a secret for `usda-dash`:
```bash
ragenix -e secrets/usda-dash/database-password.age
# Encrypted for usda-dash's public key + admins
# usda-dash will decrypt using its private key at /etc/ssh/ssh_host_ed25519_key
```

## Directory Structure

```
secrets/
├── secrets.nix              # Auto-generated recipient config
├── update-age-keys.sh       # Helper to convert SSH → age keys
├── README.md                # User documentation
├── DESIGN.md                # This file
│
├── global/                  # Secrets for ALL hosts
│   ├── *.pub                # SSH public keys
│   ├── *.age.pub            # Age public keys (generated)
│   ├── *.age                # Encrypted secrets
│   └── default.nix          # Optional: custom secret config
│
├── {hostname}/              # Host-specific secrets
│   ├── *.pub
│   ├── *.age.pub
│   ├── *.age
│   └── default.nix
│
└── admins/                  # Admin keys for editing
    └── *.age.pub
```

## Security Model

1. **Public keys in git**: Safe to commit (only public keys, `.age.pub` and `.pub`)
2. **Private keys on hosts**: Never leave the system (`/etc/ssh/ssh_host_*_key`, `/etc/age/identity.key`)
3. **Encrypted secrets in git**: Safe to commit (`.age` files)
4. **Decrypted secrets**: Only in memory/tmpfs (`/run/agenix/*`)

## Integration Points

### With NixOS Configuration

```nix
# Access decrypted secrets in any NixOS module
config.age.secrets.my-secret.path  # => /run/agenix/my-secret

# Example usage
services.myapp.passwordFile = config.age.secrets.database-password.path;
```

### With Inventory System

The system automatically matches `secrets/{hostname}/` to hostnames from `inventory.nix`. No manual configuration needed.

### With External Modules

External user/system modules can reference secrets:
```nix
# In external module
{ config, ... }:
{
  programs.git.extraConfig.credential.helper = 
    "store --file ${config.age.secrets.git-credentials.path}";
}
```

## Advantages

1. **Zero manual recipient management**: Just add directories and keys
2. **Cross-host secret creation**: Any host can manage secrets for others
3. **Automatic host discovery**: Syncs with inventory structure
4. **Flexible permission model**: Global vs host-specific + custom configs
5. **Version controlled**: All public data in git, auditable history
6. **Secure by default**: Private keys never shared, secrets encrypted at rest

## Limitations

1. **Requires age key conversion**: SSH keys must be converted to age format (automated by script)
2. **Bootstrap chicken-egg**: Need initial host key before encrypting secrets (capture from first boot or generate locally)
3. **No secret rotation automation**: Must manually re-key with `ragenix -r`
4. **Git history contains old encrypted versions**: Rotating keys doesn't remove old ciphertexts from history

## Future Enhancements

- Auto-run `update-age-keys.sh` in pre-commit hook
- Integrate with inventory.nix to auto-generate host directories
- Support for multiple identity types per host
- Automated secret rotation scheduling
- Integration with hardware security modules (YubiKey, etc.)
