Commit Graph

10 Commits

Author SHA1 Message Date
Paul Payne
548be903e7 feat: Fix HAProxy ACL ordering + Subdomains field
Rewrite HAProxy config generation with correct ACL ordering:
1. L7 HTTP exact matches first (central, wild-cloud app)
2. L4 exact matches (payne.io, civilsociety.dev)
3. L4 wildcard matches (*.cloud.payne.io) — only when subdomains:true
4. Default → L7 termination

Key changes:
- InstanceRoute: removed ExtraDomains, added Subdomains bool
- GenerateOpts: removed CentralDomain (Central is now just another
  HTTPRoute injected by reconciliation), renamed WildcardCert→CertsDir
- Central's domain comes from config, injected as HTTPRoute with
  the actual running port (no more hardcoded 5055)
- Added API.SetPort() so reconciliation knows the running port
- subdomains:false → exact match only (no *.domain shadowing)

New tests: TestGenerate_WithSubdomains, TestGenerate_ACLOrdering
(verifies L7 exact matches come before L4 wildcards).

Fixes: BUG 1 (central routing to wrong port), BUG 2 (wild-cloud
getting traefik cert), BUG 3 (*.payne.io too broad).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-07-09 20:29:30 +00:00
Paul Payne
8874bc6281 feat: Domain-keyed service registration model
Rewrite the service registration model per the architecture plan:

Model changes:
- Domain is the unique key (removed Name field)
- Removed ExtraDomains — each domain is its own registration
- Removed SourceNode, BackendStatic, ReachOff
- Added Subdomains bool for wildcard matching control
- Files stored as <domain_with_underscores>.yaml
- API routes: /services/{domain:.+} (regex for dots in path)
- Added DeregisterBySource for cleanup before re-registration

Reconciliation changes:
- No ExtraDomains iteration — each service IS one domain
- reach:internal → sets InternalDomain (generates local=/)
- reach:public → sets Domain (no local=/)
- tcp-passthrough → DNS points to backend IP
- http → DNS points to Central IP

All 22 tests pass (9 manager + 8 handler + 5 config/cert).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-07-09 20:23:11 +00:00
Paul Payne
57a597fe5a feat: Self-register Wild Central on startup
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>
2026-07-09 05:40:27 +00:00
Paul Payne
909527f060 feat: Add tests for config, secrets, services, and cloudflare endpoints
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>
2026-07-09 04:45:55 +00:00
Paul Payne
d9c152f044 fix: Preserve nested structure when redacting secrets
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>
2026-07-09 04:40:47 +00:00
Paul Payne
f066dd7e99 feat: Add Cloudflare token verification endpoint
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>
2026-07-09 04:34:03 +00:00
Paul Payne
5defc27f63 feat: Wire up service registration to networking reconciliation
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>
2026-07-09 03:45:51 +00:00
Paul Payne
060a68b601 fix: Use production config file paths as defaults
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>
2026-07-09 03:39:51 +00:00
Paul Payne
e936102eab feat: Add service registration API
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>
2026-07-08 23:33:30 +00:00
Paul Payne
beb643f76f feat: Extract Wild Central as standalone Go service
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>
2026-07-08 23:31:16 +00:00