docs: API.md + RBAC.md with Authentik integration
Documentation: - docs/API.md: Complete API reference with examples - All endpoints with curl examples - Python SDK example - Error responses and rate limits - docs/RBAC.md: RBAC system documentation - Two-level access control explained - Built-in roles (admin, portfolio-agent, authenticated-user) - Authentik configuration guide - Scope mapping examples for roles/permissions - Troubleshooting guide JWT Integration: - Add 'roles' field to JwtClaims struct - Wire roles from Authentik JWT to RBAC Claims - API key users get 'admin' role by default Tests: - Add test_to_rbac_claims_with_roles - Verify roles extraction from JWT - 670 tests passing
This commit is contained in:
+414
@@ -0,0 +1,414 @@
|
||||
# Memory API Reference
|
||||
|
||||
## Authentication
|
||||
|
||||
All endpoints require authentication via JWT token (from Authentik) or API key fallback.
|
||||
|
||||
### JWT Authentication (Recommended)
|
||||
|
||||
```bash
|
||||
# Get token from Authentik
|
||||
TOKEN=$(curl -s -X POST https://authentik.riotpiao.com/application/o/token/ \
|
||||
-d "grant_type=client_credentials" \
|
||||
-d "client_id=YOUR_CLIENT_ID" \
|
||||
-d "client_secret=YOUR_CLIENT_SECRET" | jq -r '.access_token')
|
||||
|
||||
# Use token in requests
|
||||
curl -H "Authorization: Bearer $TOKEN" \
|
||||
http://memory.riotpiao.com/memory/query?project=homelab&query=kubernetes
|
||||
```
|
||||
|
||||
### API Key Authentication (Fallback)
|
||||
|
||||
```bash
|
||||
curl -H "apikey: YOUR_API_KEY" \
|
||||
http://memory.riotpiao.com/memory/query?project=homelab&query=kubernetes
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Endpoints
|
||||
|
||||
### Health Check
|
||||
|
||||
```http
|
||||
GET /health
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"status": "ok",
|
||||
"uptime_secs": 3600
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Query Memory
|
||||
|
||||
Search learned knowledge using semantic + hybrid search.
|
||||
|
||||
```http
|
||||
GET /memory/query?project={project}&query={query}&limit={limit}&method={method}
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
| Name | Type | Required | Description |
|
||||
|------|------|----------|-------------|
|
||||
| project | string | Yes | Project to search in |
|
||||
| query | string | Yes | Search query |
|
||||
| limit | int | No | Max results (default: 10) |
|
||||
| method | string | No | `semantic` or `hybrid` (default: hybrid) |
|
||||
|
||||
**Example:**
|
||||
```bash
|
||||
curl -H "Authorization: Bearer $TOKEN" \
|
||||
"http://memory.riotpiao.com/memory/query?project=homelab&query=fix%20kubernetes%20port%20conflict&limit=5"
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"query": "fix kubernetes port conflict",
|
||||
"project": "homelab",
|
||||
"method": "hybrid",
|
||||
"results": [
|
||||
{
|
||||
"level": "L1",
|
||||
"score": 0.92,
|
||||
"text": "To fix port conflicts in Kubernetes...",
|
||||
"source": "troubleshooting/ports.md",
|
||||
"provenance": ["session-123"]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**RBAC:** Requires `memory:read` permission. Results filtered by user's project/visibility access.
|
||||
|
||||
---
|
||||
|
||||
### Context Lookup (Three-Tier RAG)
|
||||
|
||||
Get contextual knowledge for tool/task with failure diagnosis.
|
||||
|
||||
```http
|
||||
POST /memory/context
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
**Body:**
|
||||
```json
|
||||
{
|
||||
"project": "homelab",
|
||||
"tool": "kubectl",
|
||||
"task": "debug-pod",
|
||||
"scope": "tool_context",
|
||||
"budget": 8192,
|
||||
"failure_log": "CrashLoopBackOff: container exited with code 1"
|
||||
}
|
||||
```
|
||||
|
||||
**Example:**
|
||||
```bash
|
||||
curl -X POST -H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"project":"homelab","tool":"kubectl","task":"debug-pod","budget":4096}' \
|
||||
http://memory.riotpiao.com/memory/context
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"tier": 1,
|
||||
"lessons": [
|
||||
{
|
||||
"tier": 1,
|
||||
"level": "L1",
|
||||
"score": 1.0,
|
||||
"text": "CrashLoopBackOff usually means...",
|
||||
"matched_kind": "symptom",
|
||||
"seen_count": 15
|
||||
}
|
||||
],
|
||||
"skills": [
|
||||
{
|
||||
"name": "diagnose-pod-failure",
|
||||
"score": 0.95,
|
||||
"description": "Debug Kubernetes pod crashes"
|
||||
}
|
||||
],
|
||||
"budget": {
|
||||
"limit": 4096,
|
||||
"used": 2048,
|
||||
"dropped": []
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**RBAC:** Requires `memory:read` permission + project access.
|
||||
|
||||
---
|
||||
|
||||
### Ingest Records
|
||||
|
||||
Add new knowledge to memory.
|
||||
|
||||
```http
|
||||
POST /memory/ingest
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
**Body:**
|
||||
```json
|
||||
{
|
||||
"project": "homelab",
|
||||
"ingest_id": "session-2024-01-15-001",
|
||||
"source": "conversation://claude/session-123",
|
||||
"records": [
|
||||
{"text": "Kubernetes uses port 6443 for API server..."},
|
||||
{"text": "To change the port, edit /etc/kubernetes/manifests/kube-apiserver.yaml"}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Example:**
|
||||
```bash
|
||||
curl -X POST -H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"project":"homelab","ingest_id":"test-001","source":"manual","records":[{"text":"Test fact"}]}' \
|
||||
http://memory.riotpiao.com/memory/ingest
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"status": "accepted",
|
||||
"ingest_id": "test-001",
|
||||
"records_queued": 1
|
||||
}
|
||||
```
|
||||
|
||||
**RBAC:** Requires `memory:write` permission + project write access.
|
||||
|
||||
---
|
||||
|
||||
### Learn from Text
|
||||
|
||||
Process and learn from a block of text (chunking + embedding + synthesis).
|
||||
|
||||
```http
|
||||
POST /memory/learn
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
**Body:**
|
||||
```json
|
||||
{
|
||||
"project": "homelab",
|
||||
"text": "# Kubernetes Networking\n\nKubernetes uses CNI plugins...",
|
||||
"query": "What are the key networking concepts?",
|
||||
"chunk_size": 2000,
|
||||
"memory_budget": 4096
|
||||
}
|
||||
```
|
||||
|
||||
**Example:**
|
||||
```bash
|
||||
curl -X POST -H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"project":"homelab","text":"# Guide\nSome content...","query":"Summarize this"}' \
|
||||
http://memory.riotpiao.com/memory/learn
|
||||
```
|
||||
|
||||
**RBAC:** Requires `memory:write` permission + project write access.
|
||||
|
||||
---
|
||||
|
||||
### List Projects
|
||||
|
||||
Get all accessible projects.
|
||||
|
||||
```http
|
||||
GET /memory/projects
|
||||
```
|
||||
|
||||
**Example:**
|
||||
```bash
|
||||
curl -H "Authorization: Bearer $TOKEN" \
|
||||
http://memory.riotpiao.com/memory/projects
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"projects": ["homelab", "portfolio"],
|
||||
"count": 2
|
||||
}
|
||||
```
|
||||
|
||||
**RBAC:** Returns only projects user has read access to.
|
||||
|
||||
---
|
||||
|
||||
### List Skills
|
||||
|
||||
Get extracted skills.
|
||||
|
||||
```http
|
||||
GET /memory/skills
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"skills": [
|
||||
{
|
||||
"name": "diagnose-pod-failure",
|
||||
"description": "Debug Kubernetes pod issues",
|
||||
"when_to_use": "Pod in CrashLoopBackOff or Error state"
|
||||
}
|
||||
],
|
||||
"count": 1
|
||||
}
|
||||
```
|
||||
|
||||
**RBAC:** Requires `memory:read` permission.
|
||||
|
||||
---
|
||||
|
||||
### Ingest Status
|
||||
|
||||
Check status of an ingest job.
|
||||
|
||||
```http
|
||||
GET /memory/ingest/{ingest_id}
|
||||
```
|
||||
|
||||
**Example:**
|
||||
```bash
|
||||
curl -H "Authorization: Bearer $TOKEN" \
|
||||
http://memory.riotpiao.com/memory/ingest/session-2024-01-15-001
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"ingest_id": "session-2024-01-15-001",
|
||||
"status": "completed",
|
||||
"records_processed": 5,
|
||||
"created_at": "2024-01-15T10:30:00Z",
|
||||
"completed_at": "2024-01-15T10:30:05Z"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Error Responses
|
||||
|
||||
### 401 Unauthorized
|
||||
```json
|
||||
{
|
||||
"error": "unauthorized",
|
||||
"reason": "missing Authorization header"
|
||||
}
|
||||
```
|
||||
|
||||
### 403 Forbidden
|
||||
```json
|
||||
{
|
||||
"error": "forbidden",
|
||||
"reason": "missing capability: memory:write"
|
||||
}
|
||||
```
|
||||
|
||||
Or with RBAC:
|
||||
```json
|
||||
{
|
||||
"error": "forbidden",
|
||||
"reason": "access denied to project 'secret-project'"
|
||||
}
|
||||
```
|
||||
|
||||
### 429 Too Many Requests
|
||||
```json
|
||||
{
|
||||
"error": "rate_limited",
|
||||
"reason": "exceeded 1000 requests/hour for /memory/query"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Rate Limits
|
||||
|
||||
| Endpoint | Limit |
|
||||
|----------|-------|
|
||||
| `/memory/ingest` | 100/hour |
|
||||
| `/memory/query` | 1000/hour |
|
||||
| `/memory/context` | 100/hour |
|
||||
| `/memory/learn` | 100/hour |
|
||||
|
||||
Rate limits are per-user (based on JWT `sub` claim).
|
||||
|
||||
---
|
||||
|
||||
## SDK Examples
|
||||
|
||||
### Python
|
||||
|
||||
```python
|
||||
import requests
|
||||
|
||||
class MemoryClient:
|
||||
def __init__(self, base_url, token):
|
||||
self.base_url = base_url
|
||||
self.headers = {"Authorization": f"Bearer {token}"}
|
||||
|
||||
def query(self, project, query, limit=10):
|
||||
resp = requests.get(
|
||||
f"{self.base_url}/memory/query",
|
||||
params={"project": project, "query": query, "limit": limit},
|
||||
headers=self.headers
|
||||
)
|
||||
resp.raise_for_status()
|
||||
return resp.json()
|
||||
|
||||
def ingest(self, project, records, source="api"):
|
||||
import uuid
|
||||
resp = requests.post(
|
||||
f"{self.base_url}/memory/ingest",
|
||||
json={
|
||||
"project": project,
|
||||
"ingest_id": str(uuid.uuid4()),
|
||||
"source": source,
|
||||
"records": [{"text": r} for r in records]
|
||||
},
|
||||
headers=self.headers
|
||||
)
|
||||
resp.raise_for_status()
|
||||
return resp.json()
|
||||
|
||||
# Usage
|
||||
client = MemoryClient("http://memory.riotpiao.com", TOKEN)
|
||||
results = client.query("homelab", "kubernetes networking")
|
||||
```
|
||||
|
||||
### curl One-Liners
|
||||
|
||||
```bash
|
||||
# Query
|
||||
curl -H "Authorization: Bearer $TOKEN" \
|
||||
"http://memory.riotpiao.com/memory/query?project=homelab&query=kubernetes"
|
||||
|
||||
# Ingest
|
||||
curl -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
|
||||
-d '{"project":"homelab","ingest_id":"'$(uuidgen)'","source":"cli","records":[{"text":"New fact"}]}' \
|
||||
http://memory.riotpiao.com/memory/ingest
|
||||
|
||||
# Context
|
||||
curl -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
|
||||
-d '{"project":"homelab","tool":"kubectl","task":"debug"}' \
|
||||
http://memory.riotpiao.com/memory/context
|
||||
```
|
||||
Reference in New Issue
Block a user