- Document client_credentials flow for service accounts - Add JWT claims structure (permissions, memory_*) - Document available service accounts - Add capabilities table with group mappings
homelab-frontend
Production API gateway for the homelab cluster. Single entry point (api.riotpiao.com) for all services: LLM inference, workflows, queues, memory, and cluster operations.
Status: Live in production. Replaced Kong OSS entirely.
Quick Links
- API Reference: See 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 │ CLI │
└──────┬──────┴──────┬───────┴────┬───────┘
│ │ │
└─────────────┼────────────┘
│
HTTPS/TLS
│
┌─────────────┼────────────┐
│ Cloudflare Edge │
│ (DDoS, caching) │
└──────────┬──────────────┘
│
ingress-nginx
(SSL termination)
│
┌─────────────────────────────┐
│ homelab-frontend Gateway │
│ (routing, auth, limits) │
└──────┬───────────────────────┘
│
┌──────┴──────────────────────────────────┐
│ │
/v1/* /workflow /sqs /
(LLM) (Temporal gRPC) (Queues) (X-Service)
│ │ │ │
▼ ▼ ▼ ▼
llm-serving temporal:7233 kmsvc/Kafka IAM, S3
(vLLM, Ollama) (WorkflowService) Memory
(TEI) (gRPC bridge) (poimen)
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
Services & Capabilities
| 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 |
How to Use
1. Get a token
Human (OIDC device code):
core auth login
export TOKEN=$(cat ~/.cache/talos/authentik_id_token)
Service account (client credentials):
core mwinit login --username sa-name --password secret
export TOKEN=$(cat ~/.talos/.riotpiao-auth)
2. Call any service
Chat:
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:
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:
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
Available Models
LLM (Chat & Reasoning)
reasoning— DeepSeek-R1-Distill-Qwen-32B (8 concurrent slots)ornith:35b— Ollama 35Bqwen2.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:
Authorization: Bearer <jwt-token>
Tokens validated via Authentik JWKS (runtime fetched, cached, auto-rotated).
Capabilities (RBAC):
llm:inference—/v1/*chat/embeddings/rerankworkflow:execute—/workflowoperationsmemory:read/memory:write— Memory operationssqs:access— Queue operationss3:access— S3 operationsiam:admin— User/group management
Error Handling
All errors return RFC 9457 application/problem+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:
# 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/
server/ Router, health checks
proxy/ Reverse proxy, streaming, timeouts
temporal/ Workflow handler + gRPC bridge
serviceadapter/ X-Service dispatcher (CRD-driven)
config/ Route + upstream configuration
auth/ Authentik JWT validation
observability/ Metrics, structured logging
k8s/
configmap.yaml Route definitions
rbac.yaml Service account, roles
deployment.yaml Pod spec
networkpolicy.yaml Ingress/egress rules
testdata/ Fixtures for offline tests
Deployment
Deployed to Kubernetes via ArgoCD:
# 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
# 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
kubectl -n api logs deployment/homelab-frontend
Debug config:
kubectl -n api get configmap homelab-frontend-config -o yaml
Restart pod:
kubectl -n api rollout restart deployment/homelab-frontend
Test endpoint directly:
kubectl -n api port-forward svc/homelab-frontend 8080:8080
curl http://localhost:8080/healthz
See Also
- API.md — Complete API reference with examples
internal/— Source code (handlers, routing, auth)k8s/— Kubernetes manifests