- Add storage.WriteFileAtomic (temp + rename) and storage.CopyFile helpers
- Convert all 8 production config writers to atomic writes: config/state.yaml,
dnsmasq, nftables, domains, wireguard (config + secrets + peers + wg0.conf),
tunnel/cloudflared
- Add sync.Mutex to Reconciler to serialize concurrent Reconcile() calls
triggered by domain registration goroutines
- Add state.yaml backup (.bak) before every write; LoadState falls back to
backup if primary is corrupted
- Reconciler refuses to use empty config on corruption (only on first run
when no state file exists yet)
Port Castle's tunnel config generator to Go. Central can now manage
cloudflared ingress configuration for public service exposure.
Services with reach=public get projected at <subdomain>.<publicDomain>
with Host/SNI rewriting to the internal domain so the gateway's wildcard
cert validates and existing routing works unchanged.
- Locally-managed config.yml (not remotely-managed token tunnel)
- Public surface is exactly the set of reach=public services
- Auto-removes config when tunnel is disabled or no public services
- 7 tests covering basic generation, subdomain override, edge cases
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>