Services registered via the service API have a domain but no internal
domain (that's a Wild Cloud instance concept). The dnsmasq generator
was producing broken entries like `local=//` and `address=//`.
Fix: skip internal domain entries when InternalDomain is empty, in
both config.go and config_modular.go. Also fix DNS entries to point
to Central's IP (where HAProxy listens) rather than the backend
address — all traffic flows through Central.
Added 3 tests covering: service-only, mixed services+instances.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When HTTP services are registered (where Central terminates TLS),
reconciliation now checks for and provisions TLS certificates:
- Derives gateway domain from central domain (e.g., payne.io)
- Checks for existing wildcard cert (*.payne.io)
- If no wildcard exists and Cloudflare token + operator email are
configured, provisions one via certbot DNS-01
- For services outside the gateway domain, provisions individual certs
- Services with TLS passthrough (k8s) are skipped (backend handles TLS)
- Graceful: skips silently if no Cloudflare token or email configured
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
On startup, Wild Central registers its own domain (from config
cloud.central.domain) as an HTTP service with itself. This creates
the DNS entry, proxy route, and TLS cert needed to access the
Central UI via its domain name.
Registration is idempotent — safe on every restart. Skips silently
if no domain is configured. Logs a warning if registration fails.
Verified: after startup, /api/v1/services shows wild-central
registered with domain central.payne.io.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Fix config endpoint to return wrapped { configured, config } response
that the web UI expects. Fix services handler to return stored version
with defaults applied.
New tests:
- GetGlobalConfig: wrapped response, empty/not-configured case
- GetGlobalSecrets: leaf redaction preserves structure, raw mode
- UpdateGlobalConfig: write and verify
- CloudflareVerify: no-token case
- Services: register, list, get, update, deregister, TCP passthrough
defaults, validation errors
15 new test cases, all passing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The secrets endpoint was flattening nested maps (e.g., cloudflare.apiToken)
to a single "********" string. The UI checks for nested keys to determine
if credentials are configured, so the structure must be preserved.
Now redactSecrets() recursively walks the map and only redacts leaf values.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add /api/v1/cloudflare/verify, /api/v1/cloudflare/zone endpoints
so the Central UI's Cloudflare page can verify tokens and manage
zone configuration. This was a missing endpoint from the extraction.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When a service is registered, updated, or deregistered, Central now
automatically regenerates HAProxy routes and dnsmasq DNS entries to
match the current set of registered services.
- L4 tcp-passthrough services (Wild Cloud) → HAProxy SNI routes
- L7 http services (Wild Works) → HAProxy Host-based reverse proxy
- All services with reach != off → dnsmasq DNS entries
- SSE events broadcast on config changes
- Reconciliation runs asynchronously (non-blocking)
This closes the loop: service registration is no longer inert — it
actually updates the networking stack.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The default paths for dnsmasq and nftables config files should match
the existing production layout (wild-cloud.conf, wild-cloud.nft) so
Wild Central works as a drop-in when reading from the production data
directory. These can be overridden via env vars for fresh installs.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add the service registration abstraction — the key contract between
Central and its consumers (Wild Cloud, Wild Works). Services register
with Central to get DNS, gateway routing, TLS, and public exposure.
- internal/services/manager.go: CRUD for service registrations stored
as YAML files, with reach model (off/internal/public), backend types
(tcp-passthrough/http/static), and TLS modes
- internal/api/v1/handlers_services.go: HTTP endpoints
POST/GET/PATCH/DELETE /api/v1/services
- Full test coverage for registration, update, deregister, validation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Extract the Central networking functionality from wild-cloud/api into
a standalone service. Wild Central manages DNS (dnsmasq), gateway
(HAProxy), firewall (nftables), VPN (WireGuard), TLS (certbot),
security (CrowdSec), and DDNS — all the network appliance concerns.
All Cloud-specific code (instances, clusters, nodes, apps, backups,
operations, kubectl/talosctl tooling) has been removed. The API struct
and route registration contain only Central endpoints. Tests updated
to match the new API signature.
Builds and all tests pass.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>