# CLAUDE.md

Private bash monorepo for home lab infrastructure. See README.md for structure and usage. See each domain's README.md for detailed docs.

## Config Pattern

Shared network config (HOSTS, DOMAIN) lives in root `config.sh`. Domain-specific settings stay inline in each script. Both scripts source config.sh the same way via SCRIPT_DIR/REPO_ROOT resolution. Do NOT put domain-specific settings in the shared config.

## Bash Conventions

- `set -euo pipefail` in all scripts
- All scripts must pass `bash -n`
- IMPORTANT: use `awk` for file manipulation, never `sed -i` (macOS BSD vs GNU incompatibility)
- Use ASCII-only markers for managed config blocks (no Unicode box-drawing chars — breaks macOS sed/awk)
- `printf '%s\n' "${ARRAY[@]}"` not `echo` for array iteration
- `while IFS='|' read -r ... done < <(printf '%s\n' "${ARRAY[@]}")` for delimited array parsing
- Exact-match loops for array membership checks, not `[[ =~ ]]` (substring false positives)

## SSH-Specific Gotchas

- IMPORTANT: ProxyJump must reference the `Host` alias (e.g., `pve-host-01`), NEVER the `HostName` (e.g., `pve-host-01.local`). The child SSH process matches against `Host` entries only.
- `@cert-authority` patterns in known_hosts must include bare hostnames AND IPs — patterns match the connection-time string, not the resolved address
- Always validate sshd_config with `sshd -t` before restarting sshd
- Use `run_ssh_verbose` (not `run_ssh`) when error output matters (e.g., config validation)
- ForwardAgent is always on for all hosts — the core use case is moving files between hosts via rsync/scp

## Naming

- 1Password format: `[User] - [Service] ([Host])`
  - Service credentials: `Krystian - Traefik Dashboard (lxc-gateway)`
  - Service credentials: `Krystian - Sonarr (vm-docker-media)`
  - Tokens/secrets: `Krystian - ACME DNS Token (lxc-gateway)`
  - SSH CA keys: `Krystian - SSH CA Private Key - User (home-nexus)`
- Git user: `krystosterone`
- SSH principals: `root,krystosterone`

## Commits

Conventional Commits. See /commit-message command for full format. Scopes use kebab-case domain names (e.g., `ssh-ca`, `gateway`). No backticks in commit messages — use single quotes.

## Service Placement

See INFRASTRUCTURE.md for full hardware specs and RAM budget. When proposing where a new service runs:

- Media/Neural (needs M2 or 30TB disk) → media-mac
- Personal/Data (life/work tools) → vm-docker-core (201)
- I/O & Traffic (downloads, large files) → vm-docker-media (301)

- Network Utility (lightweight, whole-house) → 100-series LXC
- Ingress/Routing (HTTP proxy) → lxc-gateway (110)

IMPORTANT: Always check RAM headroom against the budget in INFRASTRUCTURE.md before placing a new service. pve-host-01 has ~4608 MiB free.
