Files
wild-central/docs/registrations.md
Paul Payne d009e095c0 docs: Add README and service registration reference
- README.md: project overview, two sources of truth (config vs
  registrations), quick start, development, architecture
- docs/registrations.md: complete API reference for service
  registrations — fields, types, defaults, what Central does
  per combination, examples for each use case

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-07-09 20:11:23 +00:00

5.7 KiB

Service Registrations

Wild Central provides DNS, proxy routing, TLS, and DDNS for services on the LAN. External consumers (Wild Cloud, Wild Works) register their domains with Central's API, and Central handles the networking.

Registration API

POST   /api/v1/services              Register a domain
GET    /api/v1/services              List all registrations
GET    /api/v1/services/{domain}     Get registration details
PATCH  /api/v1/services/{domain}     Update a registration
DELETE /api/v1/services/{domain}     Remove a registration

The domain is the unique key. One registration per domain.

Fields

Field Type Required Default Description
domain string yes FQDN to route. Unique key.
source string no "manual" Who registered this: wild-cloud, wild-works, manual.
backend.address string yes Target host:port (e.g., 192.168.8.240:443).
backend.type string yes tcp-passthrough or http.
subdomains bool no false If true, also routes *.domain traffic to this backend.
reach string yes internal or public.
tls string no inferred passthrough or terminate. Defaults based on type.

backend.type

tcp-passthrough — Layer 4. Central passes TLS traffic through to the backend without terminating it. The backend handles its own TLS (e.g., a k8s cluster with traefik and its own wildcard cert). HAProxy routes by SNI.

http — Layer 7. Central terminates TLS (using a certbot-provisioned certificate) and reverse-proxies HTTP to the backend. HAProxy routes by Host header.

subdomains

Controls whether *.domain traffic is also routed to this backend.

  • false (default): Only exact-match domain is routed. Use for custom domains like payne.io or civilsociety.dev where you only want that specific hostname.
  • true: Both domain and *.domain are routed. Use for instance primary domains like cloud.payne.io where apps are served at subdomains (matrix.cloud.payne.io, vaultwarden.cloud.payne.io).

reach

Controls DNS visibility and external access.

  • internal: Domain resolves only on the LAN (dnsmasq local=/ + address=/). Not added to DDNS. Not routable from the internet.
  • public: Domain resolves on the LAN (dnsmasq address=/) AND externally via DDNS A record. Routable from the internet through Central's HAProxy.

tls

  • passthrough: Central does not terminate TLS — the backend handles it. Default for tcp-passthrough.
  • terminate: Central provisions a TLS certificate (via certbot + Cloudflare DNS-01) and terminates TLS at HAProxy. Default for http.

What Central does per registration

tcp-passthrough http
Internal DNS address=/<domain>/<backend-IP> — LAN clients connect directly to the backend address=/<domain>/<central-IP> — LAN clients connect to Central's HAProxy
DNS (internal reach) Also local=/<domain>/ — prevents upstream DNS forwarding Same
External DNS (public reach) DDNS A record at Cloudflare Same
HAProxy L4 SNI → backend. subdomains: true adds *.domain matching. L7 Host header → reverse proxy to backend. Always exact match.
TLS Passthrough — backend handles TLS Terminate — Central provisions cert via certbot

Examples

Wild Cloud k8s instance (primary domain with subdomains)

{
  "domain": "cloud.payne.io",
  "source": "wild-cloud",
  "backend": {"address": "192.168.8.240:443", "type": "tcp-passthrough"},
  "subdomains": true,
  "reach": "public"
}

Result:

  • DNS: cloud.payne.io → 192.168.8.240, *.cloud.payne.io → 192.168.8.240
  • HAProxy: SNI cloud.payne.io and *.cloud.payne.io → passthrough to 192.168.8.240:443
  • DDNS: A record for cloud.payne.io → public IP
  • TLS: handled by k8s traefik (passthrough)

Wild Cloud k8s instance (internal-only domain)

{
  "domain": "internal.cloud.payne.io",
  "source": "wild-cloud",
  "backend": {"address": "192.168.8.240:443", "type": "tcp-passthrough"},
  "subdomains": true,
  "reach": "internal"
}

Result:

  • DNS: local=/internal.cloud.payne.io/ + address=/internal.cloud.payne.io/192.168.8.240
  • No DDNS record (internal only)
  • HAProxy: SNI matching for internal domain (in case traffic comes through Central)

Custom app domain (exact match, no subdomains)

{
  "domain": "payne.io",
  "source": "wild-cloud",
  "backend": {"address": "192.168.8.240:443", "type": "tcp-passthrough"},
  "subdomains": false,
  "reach": "public"
}

Result:

  • DNS: address=/payne.io/192.168.8.240
  • HAProxy: SNI exact match payne.io only (NOT *.payne.io)
  • DDNS: A record for payne.io → public IP

Wild Cloud app (HTTP reverse proxy, internal only)

{
  "domain": "wild-cloud.payne.io",
  "source": "wild-cloud",
  "backend": {"address": "127.0.0.1:5055", "type": "http"},
  "reach": "internal"
}

Result:

  • DNS: local=/wild-cloud.payne.io/ + address=/wild-cloud.payne.io/192.168.8.151
  • HAProxy: L7 Host wild-cloud.payne.io → reverse proxy to 127.0.0.1:5055
  • TLS: Central provisions cert via certbot
  • No DDNS (internal only)

What is NOT a registration

Central's own services are driven by Central config, not registrations:

  • Central's UI domain (cloud.central.domain in config)
  • VPN (cloud.vpn.* in config) — WireGuard endpoint, firewall port
  • Firewall rules (cloud.nftables.* in config)
  • DHCP (cloud.dnsmasq.dhcp.* in config)
  • CrowdSec, certbot credentials, DDNS provider config

These are managed through Central's own UI and config file. They don't go through the registration API.