# homelab-frontend A Go API gateway for the homelab cluster. One capability per subdomain, one auth implementation, one routing table. Replaces Kong OSS entirely — see [ADR-0001](docs/adr/ADR-0001-retire-kong-for-go-gateway.md) for why, and [docs/MIGRATION-kong.md](docs/MIGRATION-kong.md) for the cutover. ## Position in the stack ``` browser / SDK ──▶ Cloudflare ──▶ ingress-nginx (TLS, edge) │ ▼ ┌─────────────────────────────┐ │ homelab-frontend │ │ routing · authn · budgets │ └──────────────┬──────────────┘ │ /v1 /sqs /workflow /cluster │ │ │ │ ▼ ▼ ▼ ▼ llm-serving kmsvc/Kafka temporal atlas (predictors) (sqs ns) (temporal ns) (riotpiao-backend) └──── in-cluster Services ────┘ ``` ingress-nginx keeps TLS and the edge. The gateway owns everything after it. Backend services are reached through the gateway rather than published individually — a single place for authentication, budgets, timeouts and observability, and a single hostname surface to reason about. ## Capability map One host, one path prefix per capability. | Prefix on `api.riotpiao.com` | Backs onto | Status | |---|---|---| | `/v1/*` | `llm-serving` predictors (vLLM, Ollama, TEI) | migrating off Kong | | `/sqs/*` | kmsvc management-service + Kafka/Strimzi (`sqs` ns) | future | | `/workflow/*` | Temporal (`temporal` ns) | future | | `/cluster/*` | atlas — cluster topology / Argo delivery (separate repo) | future | | `/db/*` | CloudNativePG, MinIO, monitoring/metrics reads | future | `/v1/*` is reserved for the OpenAI-compatible surface. An SDK expects `/v1/chat/completions` at the base URL, so that prefix cannot be repurposed. Paths rather than subdomains: one DNS record, one tunnel hostname, one Ingress. Promoting a prefix to its own subdomain later is additive and can run alongside the path — the reverse is not, because clients hardcode hostnames. atlas lives in its own repo (`riotpiao-backend`) and keeps its own informers and RBAC. The gateway routes to it; it does not absorb it. Cluster-read permissions stay out of the public edge process. ## Design rules 1. **Standard protocol shapes.** `POST /v1/chat/completions` selects its model from the request body, like every OpenAI-compatible server. No path-per-model, no bespoke client configuration. Kong OSS could not do this; that limitation does not survive into the replacement. 2. **Bearer tokens, validated against Authentik.** JWKS is fetched at runtime and cached, so key rotation needs no runbook and no pinned PEM. 3. **Policy lives where the state is.** GPU slot semaphores, per-caller budgets, queue depth and disconnect propagation are application concerns. They belong here, not in a proxy plugin. 4. **Streaming is first-class.** SSE and WebSocket pass through unbuffered, and a client disconnect cancels the upstream request rather than orphaning it. 5. **The gateway holds no cluster credentials.** It proxies to services that do. ## Layout ``` cmd/gateway/ entrypoint internal/ auth/ Authentik OIDC, JWKS cache, service-account tokens llm/ model registry, body-based dispatch, upstream map queue/ sqs.riotpiao.com surface workflow/ workflow.riotpiao.com surface proxy/ reverse proxy, streaming, timeouts, disconnect propagation config/ upstream + route configuration observability/ Prometheus metrics, structured logging deploy/ base/ Kubernetes manifests argocd/ Argo Application docs/adr/ architecture decision records tasks/ task board — see tasks/INDEX.md testdata/ fixtures for offline tests ``` ## Local development The gateway must be runnable with no cluster, no kubeconfig and no credentials, so that changes can be verified in a closed loop before touching live traffic. Upstreams are configuration, so pointing them at local stubs is the whole mechanism. See [tasks/INDEX.md](tasks/INDEX.md). ## Status Scaffolded 2026-08-19. Nothing is wired yet. Kong is still serving live traffic on `api.riotpiao.com`.