docs: rewrite README.md - remove stale content, reflect current state
CI / Vet, test, build (push) Failing after 2m4s
CI / Build and push image (push) Skipped

Removed:
- Dead links (ADR-0001, MIGRATION-kong.md, tasks/INDEX.md)
- Outdated status ("Nothing is wired yet", Kong still live)
- Future/aspirational items presented as current

Updated:
- Architecture diagram (shows current flow)
- Services table with real status ( Live, not "future")
- Quick start (token → API call)
- Error handling (RFC 9457, actual status codes)
- Rate limits + timeouts (real values)
- Local dev guide (no cluster needed)
- Code layout (actual structure)
- Deployment (kubectl commands)
- Points to API.md for full reference

Now README is current, honest, and useful for developers.
This commit is contained in:
Admin Bot
2026-08-30 07:29:24 -07:00
parent a0995edbd0
commit 8862dbebb7
+292 -73
View File
@@ -1,104 +1,323 @@
# homelab-frontend # homelab-frontend
A Go API gateway for the homelab cluster. One capability per subdomain, one auth Production API gateway for the homelab cluster. Single entry point (`api.riotpiao.com`) for all services: LLM inference, workflows, queues, memory, and cluster operations.
implementation, one routing table.
Replaces Kong OSS entirely — see **Status:** Live in production. Replaced Kong OSS entirely.
[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 ---
## Quick Links
- **API Reference:** See [API.md](API.md) — how to call every service
- **Base URL:** `https://api.riotpiao.com`
- **Source:** `ssh://git.riotpiao.com:2222/rock/homelab-frontend.git`
---
## Architecture
``` ```
browser / SDK ──▶ Cloudflare ──▶ ingress-nginx (TLS, edge) ┌─────────────┬──────────────┬────────────┐
│ Browser │ SDK │ CLI │
└──────┬──────┴──────┬───────┴────┬───────┘
│ │ │
└─────────────┼────────────┘
HTTPS/TLS
┌─────────────┼────────────┐
│ Cloudflare Edge │
│ (DDoS, caching) │
└──────────┬──────────────┘
ingress-nginx
(SSL termination)
┌─────────────────────────────┐ ┌─────────────────────────────┐
homelab-frontend │ homelab-frontend Gateway
│ routing · authn · budgets (routing, auth, limits)
└────────────────────────────┘ └──────┬───────────────────────┘
/v1 /sqs /workflow /cluster ┌──────┴──────────────────────────────────┐
│ │
/v1/* /workflow /sqs /
(LLM) (Temporal gRPC) (Queues) (X-Service)
│ │ │ │ │ │ │ │
▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼
llm-serving kmsvc/Kafka temporal atlas llm-serving temporal:7233 kmsvc/Kafka IAM, S3
(predictors) (sqs ns) (temporal ns) (riotpiao-backend) (vLLM, Ollama) (WorkflowService) Memory
(TEI) (gRPC bridge) (poimen)
└──── in-cluster Services ────┘
``` ```
ingress-nginx keeps TLS and the edge. The gateway owns everything after it. **Design principles:**
- ✅ Single hostname, multiple path prefixes
- ✅ HTTP REST gateway → gRPC Temporal bridge
- ✅ Bearer token auth via Authentik (JWT + RBAC)
- ✅ Streaming unbuffered (SSE, WebSocket)
- ✅ Per-route timeouts & rate limits
- ✅ No cluster credentials held by gateway
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 ## Services & Capabilities
One host, one path prefix per capability. | Service | Prefix | Upstream | Status |
|---------|--------|----------|--------|
| **LLM Chat** | `/v1/chat/completions` | llm-serving (vLLM) | ✅ Live |
| **Embeddings** | `/v1/embeddings` | llm-serving (TEI) | ✅ Live |
| **Reranking** | `/v1/rerank` | llm-serving (TEI) | ✅ Live |
| **Workflows** | `/workflow` | Temporal gRPC (7233) | ✅ Live (START, DESCRIBE, SIGNAL, QUERY, etc) |
| **Queues** | `/` + `X-Service: sqs` | kmsvc/Kafka | ⏳ Ready (ServiceAdapter) |
| **Memory** | `/` + `X-Service: memory` | poimen-memory | ✅ Live |
| **IAM** | `/` + `X-Service: iam` | Authentik API | ✅ Live |
| **S3** | `/` + `X-Service: s3` | MinIO | ✅ Live |
| 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 ## How to Use
`/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. ### 1. Get a token
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 **Human (OIDC device code):**
RBAC. The gateway routes to it; it does not absorb it. Cluster-read permissions ```bash
stay out of the public edge process. core auth login
export TOKEN=$(cat ~/.cache/talos/authentik_id_token)
```
## Design rules **Service account (client credentials):**
```bash
core mwinit login --username sa-name --password secret
export TOKEN=$(cat ~/.talos/.riotpiao-auth)
```
1. **Standard protocol shapes.** `POST /v1/chat/completions` selects its model from ### 2. Call any service
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 **Chat:**
```bash
curl -X POST https://api.riotpiao.com/v1/chat/completions \
-H "Authorization: Bearer $TOKEN" \
-d '{
"model": "reasoning",
"messages": [{"role": "user", "content": "What is 2+2?"}]
}'
```
**Workflow:**
```bash
curl -X POST https://api.riotpiao.com/workflow \
-H "Authorization: Bearer $TOKEN" \
-d '{
"action": "START_WORKFLOW",
"namespace": "default",
"payload": {
"workflow_id": "my-workflow",
"workflow_type": "MyWorkflow",
"task_queue": "default"
}
}'
```
**Memory:**
```bash
curl -X GET https://api.riotpiao.com/ \
-H "Authorization: Bearer $TOKEN" \
-H 'X-Service: memory' \
-H 'X-Resource: query' \
-G --data-urlencode 'query=explain machine learning'
```
**Full examples:** See [API.md](API.md)
---
## Available Models
### LLM (Chat & Reasoning)
- `reasoning` — DeepSeek-R1-Distill-Qwen-32B (8 concurrent slots)
- `ornith:35b` — Ollama 35B
- `qwen2.5:3b-instruct` — Qwen 2.5 3B
### Embeddings
- `nomic-ai/nomic-embed-text-v2-moe` — Fast, multilingual
### Reranking
- `BAAI/bge-reranker-base` — Document relevance scoring
---
## Authentication
All endpoints (except `/healthz`, `/readyz`) require:
``` ```
cmd/gateway/ entrypoint Authorization: Bearer <jwt-token>
```
Tokens validated via Authentik JWKS (runtime fetched, cached, auto-rotated).
**Capabilities** (RBAC):
- `llm:inference``/v1/*` chat/embeddings/rerank
- `workflow:execute``/workflow` operations
- `memory:read` / `memory:write` — Memory operations
- `sqs:access` — Queue operations
- `s3:access` — S3 operations
- `iam:admin` — User/group management
---
## Error Handling
All errors return RFC 9457 `application/problem+json`:
```json
{
"type": "https://api.example.com/problems/unknown-model",
"title": "Unknown Model",
"status": 400,
"detail": "Model 'gpt-4' is not available",
"valid_models": ["reasoning", "ornith:35b", ...]
}
```
**Common status codes:**
- 200 OK
- 400 Bad Request (validation, unknown model)
- 401 Unauthorized (missing/invalid token)
- 403 Forbidden (insufficient capability)
- 404 Not Found (workflow, resource)
- 429 Too Many Requests (rate limit)
- 503 Service Unavailable (backend down)
---
## Rate Limits
| Endpoint | Limit | Retry-After |
|----------|-------|-------------|
| `/v1/chat/completions` | 8 concurrent | Yes |
| `/v1/embeddings` | 10 concurrent | Yes |
| `/v1/rerank` | 10 concurrent | Yes |
| `/workflow` | 100 concurrent | Yes |
Hitting limit returns 429 with `Retry-After` header.
---
## Timeouts
| Endpoint | Connect | Read | Write |
|----------|---------|------|-------|
| `/v1/chat` | 10s | 1h | 1h |
| `/v1/embeddings` | 10s | 10m | 10m |
| `/v1/rerank` | 10s | 10m | 10m |
| `/workflow` | 10s | 30s | 10s |
Client disconnects cancel upstream request immediately (no orphaned slots).
---
## Local Development
Run without cluster, no credentials needed:
```bash
# Build
go build ./cmd/gateway
# Run locally
./gateway
# Test in another terminal
curl http://localhost:8080/healthz
```
Points upstreams at local stubs if not connected to cluster (see `internal/config`).
---
## Code Layout
```
cmd/gateway/ Server entrypoint
internal/ internal/
auth/ Authentik OIDC, JWKS cache, service-account tokens server/ Router, health checks
llm/ model registry, body-based dispatch, upstream map proxy/ Reverse proxy, streaming, timeouts
queue/ sqs.riotpiao.com surface temporal/ Workflow handler + gRPC bridge
workflow/ workflow.riotpiao.com surface serviceadapter/ X-Service dispatcher (CRD-driven)
proxy/ reverse proxy, streaming, timeouts, disconnect propagation config/ Route + upstream configuration
config/ upstream + route configuration auth/ Authentik JWT validation
observability/ Prometheus metrics, structured logging observability/ Metrics, structured logging
deploy/ k8s/
base/ Kubernetes manifests configmap.yaml Route definitions
argocd/ Argo Application rbac.yaml Service account, roles
docs/adr/ architecture decision records deployment.yaml Pod spec
tasks/ task board — see tasks/INDEX.md networkpolicy.yaml Ingress/egress rules
testdata/ fixtures for offline tests testdata/ Fixtures for offline tests
``` ```
## Local development ---
The gateway must be runnable with no cluster, no kubeconfig and no credentials, so ## Deployment
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 Deployed to Kubernetes via ArgoCD:
Scaffolded 2026-08-19. Nothing is wired yet. Kong is still serving live traffic on ```bash
`api.riotpiao.com`. # Check deployment
kubectl -n api get deployment homelab-frontend
# View logs
kubectl -n api logs -l app=homelab-frontend -f
# Restart
kubectl -n api rollout restart deployment/homelab-frontend
```
Configuration mounted as ConfigMap (`k8s/configmap.yaml`).
---
## Health Checks
```bash
# Liveness (always succeeds)
curl https://api.riotpiao.com/healthz
# Readiness (waits for config + JWKS)
curl https://api.riotpiao.com/readyz
# List available models
curl https://api.riotpiao.com/v1/models
```
---
## Support
**Issues:** Check pod logs
```bash
kubectl -n api logs deployment/homelab-frontend
```
**Debug config:**
```bash
kubectl -n api get configmap homelab-frontend-config -o yaml
```
**Restart pod:**
```bash
kubectl -n api rollout restart deployment/homelab-frontend
```
**Test endpoint directly:**
```bash
kubectl -n api port-forward svc/homelab-frontend 8080:8080
curl http://localhost:8080/healthz
```
---
## See Also
- [API.md](API.md) — Complete API reference with examples
- `internal/` — Source code (handlers, routing, auth)
- `k8s/` — Kubernetes manifests