From d009e095c00142a8619e4545287cf18b393f8edf Mon Sep 17 00:00:00 2001 From: Paul Payne Date: Thu, 9 Jul 2026 20:11:23 +0000 Subject: [PATCH] docs: Add README and service registration reference MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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) --- README.md | 71 +++++++++++++++++++++ docs/registrations.md | 145 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 216 insertions(+) create mode 100644 README.md create mode 100644 docs/registrations.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..f572c0c --- /dev/null +++ b/README.md @@ -0,0 +1,71 @@ +# Wild Central + +Wild Central is a networking platform for your LAN. It manages DNS, proxy routing, TLS certificates, VPN, firewall, and dynamic DNS — providing a unified networking layer for self-hosted services. + +## What it does + +Wild Central runs on a device on your LAN (e.g., a Raspberry Pi) and manages: + +- **DNS** (dnsmasq) — internal name resolution, split DNS, DHCP +- **Gateway** (HAProxy) — L4 SNI passthrough for k8s clusters, L7 reverse proxy for HTTP services +- **TLS** (certbot) — certificate provisioning via Let's Encrypt DNS-01 +- **VPN** (WireGuard) — remote access with peer management +- **Firewall** (nftables) — host firewall rules +- **DDNS** (Cloudflare) — dynamic DNS A record management +- **Security** (CrowdSec) — intrusion detection +- **NATS JetStream** — coordination bus for service registration and events + +## Two sources of truth + +**Central config** drives Central's own services — its UI domain, VPN, firewall, DHCP. These are managed through Central's web UI and config file. + +**Service registrations** drive external consumer services. Wild Cloud registers its k8s instance domains. Wild Works registers its program services. Each registration is a domain that needs routing through Central's networking stack. + +See [docs/registrations.md](docs/registrations.md) for the full registration API reference. + +## Quick start + +```bash +# Install +apt install wild-central + +# Start +systemctl enable --now wild-central + +# Access the web UI +open http://:5055 +``` + +## Development + +```bash +make dev # Run with live reloading (requires air) +make build # Build binary +make test # Run tests +make check # Lint + test +``` + +### Environment variables + +| Variable | Default | Description | +|----------|---------|-------------| +| `WILD_CENTRAL_ENV` | — | Set to `development` for dev mode (Vite proxy) | +| `WILD_CENTRAL_DATA_DIR` | `/var/lib/wild-central` | Data directory | +| `WILD_CENTRAL_PORT` | `5055` | API listen port | +| `WILD_CENTRAL_NATS_PORT` | `4222` | NATS JetStream port | +| `WILD_CENTRAL_VITE_URL` | `http://localhost:5173` | Vite dev server URL | + +## Architecture + +``` +Wild Central (this service) +├── Config-driven: DNS, VPN, firewall, DHCP, TLS for Central's own domain +├── Registration-driven: DNS, proxy, TLS, DDNS for consumer domains +├── NATS JetStream: coordination bus +└── Web UI: management interface + +Wild Cloud ──registers domains──→ Wild Central +Wild Works ──registers domains──→ Wild Central +``` + +Wild Cloud and Wild Works are separate services that register their domains with Central. Central handles all the networking — DNS resolution, proxy routing, TLS certificates, and external DNS records. diff --git a/docs/registrations.md b/docs/registrations.md new file mode 100644 index 0000000..cb099d8 --- /dev/null +++ b/docs/registrations.md @@ -0,0 +1,145 @@ +# 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=//` — LAN clients connect directly to the backend | `address=//` — LAN clients connect to Central's HAProxy | +| **DNS (internal reach)** | Also `local=//` — 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) + +```json +{ + "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) + +```json +{ + "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) + +```json +{ + "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) + +```json +{ + "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.