# 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 DELETE /api/v1/services/deregister?source=X&backend=Y Batch cleanup ``` 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:80`). | | `backend.type` | string | yes* | — | `tcp-passthrough` or `http`. | | `backend.health` | string | no | — | Health check path for L7 services (e.g., `/health`). | | `subdomains` | bool | no | `false` | If true, also routes `*.domain` traffic. | | `public` | bool | no | `false` | If true, domain gets public DNS + internet exposure. | | `tls` | string | no | inferred | `passthrough` or `terminate`. Defaults based on backend type. | | `routes` | array | no* | — | Path-based multi-backend routing (see below). | *A service must have either `backend` (simple) or `routes` (multi-backend), not both. ## Simple vs multi-backend services ### Simple (single backend) Most services use a single backend. All traffic for the domain goes to one place: ```json { "domain": "my-api.payne.io", "backend": {"address": "192.168.8.60:9001", "type": "http"}, "tls": "terminate" } ``` ### Multi-backend (path-based routing) When different URL paths on the same domain need to reach different backends, use `routes`. Each route maps path prefixes to a backend with its own L7 options. Routes are evaluated in order — first match wins. A route with empty `paths` is a catch-all (should be last). ```json { "domain": "payne.io", "public": true, "tls": "terminate", "routes": [ { "paths": ["/_matrix", "/.well-known/matrix"], "backend": {"address": "192.168.8.60:8008", "type": "http", "health": "/health"} }, { "backend": {"address": "192.168.8.240:80", "type": "http"} } ] } ``` Here `/_matrix` and `/.well-known/matrix` go to a Matrix server, everything else goes to a website. Both backends can be on different hosts. ### Route fields Each route in the `routes` array supports: | Field | Type | Required | Description | |-------|------|----------|-------------| | `paths` | []string | no | Path prefixes to match (empty = catch-all). | | `backend.address` | string | yes | Target host:port. | | `backend.type` | string | yes | `http` (routes only support L7). | | `backend.health` | string | no | Health check path. | | `headers.request` | map | no | Request headers to set (e.g., `X-Forwarded-Proto: https`). | | `headers.response` | map | no | Response headers to set (e.g., CORS headers). | | `ipAllow` | []string | no | CIDR whitelist — deny requests not matching (e.g., `["10.0.0.0/8"]`). | ## User-facing concepts The API fields map to three user-visible controls: ### Public / Private `public: true` or `public: false` - **Private** (default): Domain resolves only on the LAN. No public DNS record. Accessible only from your local network or VPN. - **Public**: Domain resolves on the LAN AND has a public DNS A record. Accessible from the internet through Central's HAProxy. ### Subdomains `subdomains: true` or `subdomains: false` - **Off** (default): Only the exact domain is routed (`payne.io` routes, but `foo.payne.io` does not). - **On**: Both the domain and all subdomains are routed (`cloud.payne.io` AND `matrix.cloud.payne.io`, `vaultwarden.cloud.payne.io`, etc.). ### TLS handling `tls: "passthrough"` or `tls: "terminate"` - **Passthrough**: Central forwards encrypted traffic directly to the backend. The backend handles its own TLS certificates. Maps to `backend.type: "tcp-passthrough"`. - **Terminate**: Central provisions a TLS certificate and handles HTTPS. Traffic is decrypted at Central and proxied as HTTP to the backend. Maps to `backend.type: "http"`. ## What Central does per registration When a service is registered, Central automatically: | Effect | Passthrough | Terminate | |--------|-------------|-----------| | **LAN DNS** | `address=//` — direct to backend | `address=//` — through Central | | **LAN DNS (private)** | Also `local=//` — prevents upstream forwarding | Same | | **Public DNS** | DDNS A record if public | Same | | **Proxy** | L4 SNI passthrough. Subdomains adds `*.domain` matching. | L7 HTTP reverse proxy by Host header. Routes add path-based ACLs. | | **TLS** | Backend handles — Central passes through | Central provisions cert via Let's Encrypt | ## Examples ### Wild Cloud k8s cluster (terminate, public, wildcard) Wild Cloud registers its instances as HTTP terminate services. Central handles TLS and forwards plain HTTP to the cluster's Traefik ingress controller. ```json { "domain": "cloud.payne.io", "source": "wild-cloud", "backend": {"address": "192.168.8.240:80", "type": "http"}, "subdomains": true, "public": true } ``` Central creates: LAN DNS → Central IP, public DNS A record, HAProxy L7 TLS terminate + wildcard `*.cloud.payne.io`, cert via certbot. ### Internal web app (terminate, private) ```json { "domain": "wild-cloud.payne.io", "source": "wild-cloud", "backend": {"address": "127.0.0.1:5055", "type": "http"} } ``` Central creates: LAN DNS → Central IP (with `local=/`), HAProxy L7 reverse proxy, TLS cert via certbot. No public DNS. ### Service with custom response headers ```json { "domain": "keila.cloud.payne.io", "source": "wild-cloud", "routes": [{ "backend": {"address": "192.168.8.240:80", "type": "http"}, "headers": { "response": { "Access-Control-Allow-Origin": "https://cloud.payne.io" } } }], "public": true } ``` ### Service with IP whitelisting (internal only) ```json { "domain": "headlamp.internal.cloud.payne.io", "source": "wild-cloud", "routes": [{ "backend": {"address": "192.168.8.240:80", "type": "http"}, "ipAllow": ["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"] }] } ``` ### L4 passthrough (non-HTTP services) For services that handle their own TLS (e.g., standalone apps with built-in cert management): ```json { "domain": "payne.io", "source": "wild-cloud", "backend": {"address": "192.168.8.240:443", "type": "tcp-passthrough"}, "public": true } ``` Central creates: LAN DNS → backend IP, public DNS A record, HAProxy L4 SNI passthrough. TLS handled by backend. ## Wild Cloud integration Wild Cloud instances with `central.registration: per-app` in their instance config use a client-go Ingress watcher that automatically registers domains with Central: - One wildcard registration per instance domain (e.g., `*.cloud.payne.io` with `subdomains: true`) - One wildcard registration per internal domain (e.g., `*.internal.cloud.payne.io`) - Individual registrations for apps with special routing needs (custom headers, IP whitelisting, extra domains) - Apps define special routing via a `central` section in their manifest config ## What is NOT a registration Central's own services come from config, not the registration API: - Central's UI domain (`cloud.central.domain`) - VPN (`cloud.vpn.*`) - Firewall rules (`cloud.nftables.*`) - DHCP (`cloud.dnsmasq.dhcp.*`) - CrowdSec, certbot credentials, DDNS provider config