- 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>
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-matchdomainis routed. Use for custom domains likepayne.ioorcivilsociety.devwhere you only want that specific hostname.true: Bothdomainand*.domainare routed. Use for instance primary domains likecloud.payne.iowhere 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 (dnsmasqlocal=/+address=/). Not added to DDNS. Not routable from the internet.public: Domain resolves on the LAN (dnsmasqaddress=/) 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 fortcp-passthrough.terminate: Central provisions a TLS certificate (via certbot + Cloudflare DNS-01) and terminates TLS at HAProxy. Default forhttp.
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.ioand*.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.ioonly (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.domainin 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.