Architecture
Network
Eight VLANs, all /24 under 192.168.0.0/16. Each VLAN reserves its first ~10 IPs for network infrastructure and its tail for a DHCP pool; everything in between is registry-allocated.
| VLAN | ID | Subnet | Purpose |
|---|---|---|---|
| trusted | 1 | 192.168.1.0/24 | Primary network — trusted devices, workstations |
| mgmt | 5 | 192.168.5.0/24 | Network gear, hypervisors, platform services |
| proxmox | 10 | 192.168.10.0/24 | Proxmox cluster |
| transfer | 11 | 192.168.11.0/24 | Transfer network |
| speed | 12 | 192.168.12.0/24 | Speed network |
| kids | 20 | 192.168.20.0/24 | Content-filtered |
| iot | 30 | 192.168.30.0/24 | IoT, restricted outbound |
| guest | 50 | 192.168.50.0/24 | Guest wifi, internet only |
Naming: hostnames are dot-separated, general→specific (prod.primary.standard.dns); the FQDN appends the VLAN's DNS zone (….mgmt.home.arpa). VMIDs are always 1000 + host octet, so 192.168.5.95 ⇒ container 1095 — one number to remember per instance.
Tiers
Foundation deploys first and depends on nothing above it. Platform services are what apps build on. Physical hosts get SSH-CA trust and DNS only — no VM creation from that tier.
DNS flow
All DNS intent lives in the registry — service allocations produce A records, standalone records cover infrastructure devices, CNAMEs must point at declared A records. Foundation's full-dns-sync publishes the entire registry (both envs) to every Pi-hole via the API. Nothing edits Pi-hole by hand, and no stack owns DNS records through Terraform anymore.
State
Every stack's OpenTofu state is encrypted (PBKDF2/AES-GCM, passphrase in 1Password) and stored out-of-band via terraform-backend-git on a tfstate branch of its own repo — foundation on its branch, monorepo stacks on theirs, one state path per stack env (<tier>/<service>/<env>). A wrapper script (tofu-run.sh) supplies the passphrase, the backend deploy key, and a local backend proxy for every OpenTofu invocation; running bare tofu against these stacks fails by design.
Secrets
1Password is the only secret store. Interactive use goes through the desktop app; unattended use goes through the self-hosted Connect Server (which is itself a foundation service). Secrets enter tooling exclusively via op read in justfiles — Terraform sees them as TF_VAR_*, Ansible as extra-vars, and neither ever persists them.