# API Reference: api.riotpiao.com Gateway to all homelab services. Single entry point for LLM inference, workflows, queues, memory, and cluster operations. **Base URL:** `https://api.riotpiao.com` --- ## Table of Contents 1. [Health & Status](#health--status) 2. [LLM Services](#llm-services) 3. [Workflow Services (Temporal)](#workflow-services-temporal) 4. [Queue Services (SQS)](#queue-services-sqs) 5. [Memory Services](#memory-services) 6. [IAM Services](#iam-services) 7. [S3 Services](#s3-services) 8. [Authentication](#authentication) 9. [Error Handling](#error-handling) --- ## Health & Status ### GET /healthz Liveness probe. Returns immediately without checks. ```bash curl https://api.riotpiao.com/healthz ``` **Response:** ```json { "status": "alive" } ``` **Status Code:** 200 --- ### GET /readyz Readiness probe. Returns 200 when gateway is ready (config loaded, connections available). ```bash curl https://api.riotpiao.com/readyz ``` **Response:** ```json { "status": "ready" } ``` **Status Code:** 200 (ready) or 503 (not ready) --- ## LLM Services ### GET /v1/models List all available models for inference. ```bash curl https://api.riotpiao.com/v1/models ``` **Response:** ```json { "object": "list", "data": [ { "id": "reasoning", "object": "model", "owned_by": "api.riotpiao.com", "created": 1700000000 }, { "id": "ornith:35b", "object": "model", "owned_by": "api.riotpiao.com", "created": 1700000000 }, { "id": "qwen2.5:3b-instruct", "object": "model", "owned_by": "api.riotpiao.com", "created": 1700000000 }, { "id": "nomic-ai/nomic-embed-text-v2-moe", "object": "model", "owned_by": "api.riotpiao.com", "created": 1700000000 }, { "id": "BAAI/bge-reranker-base", "object": "model", "owned_by": "api.riotpiao.com", "created": 1700000000 } ] } ``` **Status Code:** 200 --- ### POST /v1/chat/completions Chat with an LLM model. Supports streaming, tool calling, and multi-turn conversations. ```bash curl -X POST https://api.riotpiao.com/v1/chat/completions \ -H 'Content-Type: application/json' \ -d '{ "model": "reasoning", "messages": [ {"role": "user", "content": "What is 2+2?"} ] }' ``` **Request Body:** ```json { "model": "reasoning", "messages": [ { "role": "user", "content": "string or array" } ], "temperature": 0.7, "top_p": 1.0, "max_tokens": 2048, "stream": false, "tools": [ { "type": "function", "function": { "name": "function_name", "description": "What it does", "parameters": { "type": "object", "properties": {}, "required": [] } } } ] } ``` **Response (non-streaming):** ```json { "id": "chatcmpl-123", "object": "chat.completion", "created": 1704000000, "model": "reasoning", "choices": [ { "index": 0, "message": { "role": "assistant", "content": "2+2=4" }, "finish_reason": "stop" } ], "usage": { "prompt_tokens": 10, "completion_tokens": 5, "total_tokens": 15 } } ``` **Response (streaming, SSE format):** ``` data: {"id":"...","object":"chat.completion.chunk","choices":[{"delta":{"content":"2"}}]} data: {"id":"...","object":"chat.completion.chunk","choices":[{"delta":{"content":"+"}}]} data: {"id":"...","object":"chat.completion.chunk","choices":[{"delta":{"content":"2"}}]} data: {"id":"...","object":"chat.completion.chunk","choices":[{"delta":{"content":"="}}]} data: {"id":"...","object":"chat.completion.chunk","choices":[{"delta":{"content":"4"}}]} data: [DONE] ``` **Status Codes:** - 200 OK - 400 Bad Request (invalid model, missing fields, invalid JSON) - 500 Internal Server Error (upstream issue) **Available Models:** - `reasoning` — DeepSeek-R1-Distill reasoning model (8 concurrent slots) - `ornith:35b` — Ollama 35B model - `qwen2.5:3b-instruct` — Qwen 2.5 3B model --- ### POST /v1/embeddings Generate text embeddings. ```bash curl -X POST https://api.riotpiao.com/v1/embeddings \ -H 'Content-Type: application/json' \ -d '{ "model": "nomic-ai/nomic-embed-text-v2-moe", "input": ["text to embed", "another text"] }' ``` **Request Body:** ```json { "model": "nomic-ai/nomic-embed-text-v2-moe", "input": "string or array of strings", "encoding_format": "float" } ``` **Response:** ```json { "object": "list", "data": [ { "object": "embedding", "embedding": [0.1, 0.2, 0.3, ...], "index": 0 }, { "object": "embedding", "embedding": [0.4, 0.5, 0.6, ...], "index": 1 } ], "model": "nomic-ai/nomic-embed-text-v2-moe", "usage": { "prompt_tokens": 20, "total_tokens": 20 } } ``` **Status Codes:** - 200 OK - 400 Bad Request (invalid model or input) - 500 Internal Server Error --- ### POST /v1/rerank Rerank documents by relevance to a query. ```bash curl -X POST https://api.riotpiao.com/v1/rerank \ -H 'Content-Type: application/json' \ -d '{ "model": "BAAI/bge-reranker-base", "query": "machine learning", "texts": [ "Machine learning is AI", "Python is a language", "Deep learning is ML" ], "top_k": 2 }' ``` **Request Body:** ```json { "model": "BAAI/bge-reranker-base", "query": "search query", "texts": ["text1", "text2", "text3"], "top_k": 2, "return_documents": true } ``` **Response:** ```json { "results": [ { "index": 0, "score": 0.95, "text": "Machine learning is AI" }, { "index": 2, "score": 0.85, "text": "Deep learning is ML" } ] } ``` **Status Codes:** - 200 OK - 400 Bad Request - 500 Internal Server Error --- ## Workflow Services (Temporal) Base path: `/workflow` All operations use REST with JSON body. Internally translated to gRPC (port 7233). ### POST /workflow Execute a workflow operation via action dispatch. ```bash curl -X POST https://api.riotpiao.com/workflow \ -H 'Content-Type: application/json' \ -d '{ "action": "START_WORKFLOW", "namespace": "default", "payload": { "workflow_id": "order-123", "workflow_type": "ProcessOrder", "task_queue": "orders_queue", "input": { "order_id": "123", "amount": 99.99 } } }' ``` **Supported Actions:** #### START_WORKFLOW Start a new workflow execution. **Payload:** ```json { "workflow_id": "unique-id", "workflow_type": "WorkflowName", "task_queue": "queue_name", "input": {"field": "value"}, "options": { "workflow_execution_timeout": 3600, "workflow_run_timeout": 1800, "workflow_task_timeout": 300 } } ``` **Response:** ```json { "success": true, "action": "START_WORKFLOW", "data": { "workflow_id": "order-123", "run_id": "abc123def", "start_time": "2026-08-27T..." } } ``` #### DESCRIBE_WORKFLOW Get workflow execution details. **Payload:** ```json { "workflow_id": "order-123", "run_id": "abc123def" } ``` **Response:** ```json { "success": true, "action": "DESCRIBE_WORKFLOW", "data": { "workflow_id": "order-123", "run_id": "abc123def", "status": "RUNNING", "start_time": "2026-08-27T..." } } ``` #### LIST_WORKFLOWS List workflow executions with filtering. **Payload:** ```json { "status": "RUNNING", "page_size": 50, "next_page_token": "" } ``` #### GET_WORKFLOW_HISTORY Retrieve workflow execution history (events). **Payload:** ```json { "workflow_id": "order-123", "run_id": "abc123def", "max_events": 100 } ``` #### SIGNAL_WORKFLOW Send a signal to a running workflow (trigger handler). **Payload:** ```json { "workflow_id": "order-123", "run_id": "abc123def", "signal_name": "payment_received", "input": {"amount": 99.99} } ``` #### QUERY_WORKFLOW Query workflow state without modifying it (read-only). **Payload:** ```json { "workflow_id": "order-123", "run_id": "abc123def", "query_type": "get_status" } ``` #### CANCEL_WORKFLOW Request graceful cancellation. **Payload:** ```json { "workflow_id": "order-123", "run_id": "abc123def" } ``` #### TERMINATE_WORKFLOW Stop workflow immediately. **Payload:** ```json { "workflow_id": "order-123", "run_id": "abc123def", "reason": "User cancelled" } ``` #### Other Actions - RESET_WORKFLOW - UPDATE_WORKFLOW - HEARTBEAT_ACTIVITY - COMPLETE_ACTIVITY - FAIL_ACTIVITY **Status Codes:** - 200 OK - 400 Bad Request (invalid action or fields) - 404 Not Found (workflow not found) - 503 Service Unavailable (Temporal unreachable) --- ## Queue Services (SQS) Base path: `/sqs` (via X-Service header routing) Operations via ServiceAdapter CRD. Use header-based dispatch: ```bash curl -X GET https://api.riotpiao.com/ \ -H 'X-Service: sqs' \ -H 'X-Resource: queue/my-queue/messages' ``` **Supported Resources:** - `queue` — List queues - `queue/{name}/message` — Send/receive messages - `queue/{name}/messages` — Batch operations --- ## Memory Services Base path: `/` (via X-Service header) Query and manage semantic memory for knowledge retrieval. ```bash curl -X GET https://api.riotpiao.com/ \ -H 'X-Service: memory' \ -H 'X-Resource: query' \ -G --data-urlencode 'query=explain machine learning' \ --data-urlencode 'level=L1,L2' ``` **Supported Resources:** - `query` — Search memory (GET with params) - `skill` — List/retrieve skills - `project` — List/describe projects - `ingest` — Add new documents (POST, requires memory:write capability) --- ## IAM Services Base path: `/` (via X-Service header) Manage Authentik users, groups, and permissions. ```bash curl -X GET https://api.riotpiao.com/ \ -H 'Authorization: Bearer ' \ -H 'X-Service: iam' \ -H 'X-Resource: user' ``` **Supported Resources:** - `user` — List/create users - `user/{id}` — Get/update/delete specific user - `role` (group) — Manage groups/roles - `permission` — List permissions - `flow` — List authentication flows --- ## S3 Services Base path: `/` (via X-Service header) Object storage operations via MinIO. ```bash curl -X GET https://api.riotpiao.com/ \ -H 'X-Service: s3' \ -H 'X-Resource: bucket/my-bucket/objects' ``` **Supported Resources:** - `bucket` — List buckets - `bucket/{name}/objects` — List objects in bucket - `bucket/{name}/object/{key}` — Get/put/delete object --- ## Authentication ### Bearer Token (JWT) All operations except `/healthz` and `/readyz` require authentication via Authentik-issued JWT. ```bash curl -H 'Authorization: Bearer ' \ https://api.riotpiao.com/v1/models ``` ### Obtaining Tokens **Service Account (client_credentials flow):** For programmatic access (agents, CI, scripts), use a service account: ```bash # Get credentials from k8s secret (once) CLIENT_ID=$(kubectl -n portfolio get secret portfolio-agent-oidc -o jsonpath='{.data.CLIENT_ID}' | base64 -d) CLIENT_SECRET=$(kubectl -n portfolio get secret portfolio-agent-oidc -o jsonpath='{.data.CLIENT_SECRET}' | base64 -d) # Exchange for access token TOKEN=$(curl -s -X POST https://authentik.riotpiao.com/application/o/token/ \ -d "grant_type=client_credentials" \ -d "client_id=$CLIENT_ID" \ -d "client_secret=$CLIENT_SECRET" \ -d "scope=openid profile groups permissions memory" | jq -r '.access_token') # Use token curl -H "Authorization: Bearer $TOKEN" \ https://api.riotpiao.com/v1/models ``` **Available service accounts:** - `portfolio-agent` — Portfolio app (memory:read on homelab/portfolio projects) - `memory-agent` — Memory service internal (memory:* on all projects) **Human login (authorization_code flow):** Browser-based OAuth2 via Authentik. Used by web apps, not CLI. ``` 1. Redirect to: https://authentik.riotpiao.com/application/o/authorize/ ?client_id=&response_type=code&redirect_uri=&scope=openid+profile+groups+permissions 2. User authenticates 3. Exchange code for token at /application/o/token/ ``` ### JWT Claims Authentik tokens include these custom claims: ```json { "sub": "hashed-user-id", "groups": ["homelab-admins", "llm-admins"], "permissions": ["llm:inference", "llm:read", "llm:write"], "memory_projects": ["homelab", "portfolio"], "memory_visibility": "public", "memory_role": "user" } ``` ### Service Account Auth (Recommended for Services) Service accounts use **roles** stored in user attributes. Simpler than groups. **JWT claims (client_credentials flow):** ```json { "azp": "portfolio-agent", "roles": ["llm:inference", "memory:read"] } ``` **Gateway checks `roles` claim:** ```go if hasRole(claims.Roles, "llm:inference") { // allow } ``` **Available roles:** | Role | Grants access to | |------|------------------| | `llm:inference` | `/v1/chat/completions`, `/v1/embeddings`, `/v1/rerank` | | `memory:read` | Memory queries | | `memory:write` | Memory ingest | | `s3:read` | S3/MinIO read | | `s3:write` | S3/MinIO write | --- ### Onboarding a New Service 1. **Add to provisioning script** (`homelab/scripts/iam/authentik-provision.py`): ```python SERVICE_ACCOUNTS = { # ... existing ... "my-new-service": { "roles": ["llm:inference", "memory:read"], # what APIs it can call "attributes": { "memory_projects": ["my-project"], # optional: memory access scope "memory_visibility": "public", }, "secret_ns": "my-namespace", # k8s namespace for credentials "secret_name": "my-service-oidc", # k8s secret name }, } ``` 2. **Run provisioning:** ```bash export AUTHENTIK_BOOTSTRAP_TOKEN=$(kubectl -n iam get secret authentik-secrets \ -o jsonpath='{.data.AUTHENTIK_BOOTSTRAP_TOKEN}' | base64 -d) python3 scripts/iam/authentik-provision.py ``` 3. **Use credentials in your service:** ```bash # Credentials stored in k8s secret kubectl -n my-namespace get secret my-service-oidc -o yaml # Contains: CLIENT_ID, CLIENT_SECRET, TOKEN_URL, ISSUER # Get token TOKEN=$(curl -s -X POST $TOKEN_URL \ -d "grant_type=client_credentials" \ -d "client_id=$CLIENT_ID" \ -d "client_secret=$CLIENT_SECRET" \ -d "scope=openid roles" | jq -r '.access_token') # Call API curl -H "Authorization: Bearer $TOKEN" https://api.riotpiao.com/v1/models ``` --- ### User Auth (Groups → Permissions) Human users use **groups** which map to **permissions**. Used for browser-based apps. **JWT claims (authorization_code flow):** ```json { "groups": ["homelab-admins", "llm-admins"], "permissions": ["*", "llm:inference", "llm:read", "llm:write"] } ``` | Group | Permissions granted | |-------|---------------------| | `homelab-admins` | `*` (everything) | | `llm-admins` | `llm:inference`, `llm:read`, `llm:write` | | `llm-users` | `llm:inference` | | `memory-users` | `memory:read` | | `memory-writers` | `memory:read`, `memory:write` | | `poimen-memory-admins` | `memory:*`, `memory:admin` | **Memory-specific claims:** | Claim | Description | |-------|-------------| | `memory_projects` | Projects user can access (`["*"]` = all) | | `memory_visibility` | Max visibility level (`public`, `private`) | | `memory_role` | Role for memory service (`user`, `admin`, `portfolio-agent`) | --- ## Error Handling ### Problem Details (RFC 9457) All error responses use standardized JSON format: ```json { "type": "https://api.example.com/problems/unknown-model", "title": "Unknown Model", "status": 400, "detail": "Model 'gpt-4' is not available. See valid_models for available options.", "valid_models": ["reasoning", "ornith:35b", ...] } ``` ### HTTP Status Codes | Status | Meaning | |--------|---------| | 200 | Success | | 400 | Bad Request (validation error, invalid model, missing fields) | | 401 | Unauthorized (invalid/missing token) | | 403 | Forbidden (insufficient permissions/capability) | | 404 | Not Found (workflow, resource) | | 409 | Conflict (duplicate workflow_id) | | 429 | Too Many Requests (rate limit exceeded) | | 503 | Service Unavailable (backend unreachable) | | 504 | Gateway Timeout (request exceeded timeout) | ### Error Examples **Unknown Model:** ```bash curl -X POST https://api.riotpiao.com/v1/chat/completions \ -d '{"model":"gpt-4","messages":[]}' # Returns 400 { "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", "qwen2.5:3b-instruct", ...] } ``` **Temporal Unavailable:** ```bash curl -X POST https://api.riotpiao.com/workflow \ -d '{"action":"START_WORKFLOW",...}' # Returns 503 if Temporal server unreachable { "success": false, "error": "TEMPORAL_UNAVAILABLE", "message": "Temporal server connection not available" } ``` --- ## Examples ### Complete RAG Pipeline 1. **Query embedding:** ```bash curl -X POST https://api.riotpiao.com/v1/embeddings \ -H 'Authorization: Bearer $TOKEN' \ -d '{ "model": "nomic-ai/nomic-embed-text-v2-moe", "input": "how do neural networks work" }' | jq '.data[0].embedding' ``` 2. **Rerank documents:** ```bash curl -X POST https://api.riotpiao.com/v1/rerank \ -H 'Authorization: Bearer $TOKEN' \ -d '{ "model": "BAAI/bge-reranker-base", "query": "how do neural networks work", "texts": ["doc1", "doc2", "doc3"], "top_k": 2 }' ``` 3. **Chat with context:** ```bash curl -X POST https://api.riotpiao.com/v1/chat/completions \ -H 'Authorization: Bearer $TOKEN' \ -d '{ "model": "reasoning", "messages": [ { "role": "user", "content": "Based on these documents: [top-2 from rerank]\nQuestion: how do neural networks work?" } ] }' ``` ### Workflow Orchestration ```bash # Start workflow WORKFLOW_ID="order-$(date +%s)" curl -X POST https://api.riotpiao.com/workflow \ -H 'Authorization: Bearer $TOKEN' \ -d "{ \"action\": \"START_WORKFLOW\", \"namespace\": \"default\", \"payload\": { \"workflow_id\": \"$WORKFLOW_ID\", \"workflow_type\": \"ProcessOrder\", \"task_queue\": \"orders\", \"input\": {\"order_id\": \"123\"} } }" # Monitor execution curl -X POST https://api.riotpiao.com/workflow \ -H 'Authorization: Bearer $TOKEN' \ -d "{ \"action\": \"DESCRIBE_WORKFLOW\", \"namespace\": \"default\", \"payload\": {\"workflow_id\": \"$WORKFLOW_ID\"} }" # Send signal when payment received curl -X POST https://api.riotpiao.com/workflow \ -H 'Authorization: Bearer $TOKEN' \ -d "{ \"action\": \"SIGNAL_WORKFLOW\", \"namespace\": \"default\", \"payload\": { \"workflow_id\": \"$WORKFLOW_ID\", \"signal_name\": \"payment_received\", \"input\": {\"amount\": 99.99} } }" ``` --- ## Rate Limits & Quotas - `/v1/chat/completions` — 8 concurrent slots (reasoning model) - `/v1/embeddings` — 10 concurrent - `/v1/rerank` — 10 concurrent - `/workflow` — 100 concurrent operations - Retry-After header set on 429 responses --- ## Timeouts | Endpoint | Connect | Read | Write | |----------|---------|------|-------| | `/v1/chat/completions` | 10s | 1h | 1h | | `/v1/embeddings` | 10s | 10m | 10m | | `/v1/rerank` | 10s | 10m | 10m | | `/workflow` | 10s | 30s | 10s | --- ## Support - **Health:** `curl https://api.riotpiao.com/healthz` - **Readiness:** `curl https://api.riotpiao.com/readyz` - **Models:** `curl https://api.riotpiao.com/v1/models` - **Logs:** `kubectl -n api logs deployment/homelab-frontend`