#!/bin/bash # BlackRoad Memory API Server # REST + GraphQL API for memory system MEMORY_DIR="$HOME/.blackroad/memory" API_DIR="$MEMORY_DIR/api" API_DB="$API_DIR/api.db" API_PORT="${API_PORT:-8888}" # Colors GREEN='\033[0;32m' YELLOW='\033[1;33m' RED='\033[0;31m' CYAN='\033[0;36m' PURPLE='\033[0;35m' BLUE='\033[0;34m' NC='\033[0m' init() { echo -e "${PURPLE}╔════════════════════════════════════════════════╗${NC}" echo -e "${PURPLE}║ 🔌 Memory API Server ║${NC}" echo -e "${PURPLE}╚════════════════════════════════════════════════╝${NC}\n" mkdir -p "$API_DIR" # Create API database sqlite3 "$API_DB" <<'SQL' -- API keys CREATE TABLE IF NOT EXISTS api_keys ( id INTEGER PRIMARY KEY AUTOINCREMENT, key TEXT UNIQUE NOT NULL, name TEXT NOT NULL, permissions TEXT NOT NULL, -- JSON permissions created_at INTEGER NOT NULL, last_used INTEGER, requests_count INTEGER DEFAULT 0, rate_limit INTEGER DEFAULT 1000, status TEXT DEFAULT 'active' ); -- API requests log CREATE TABLE IF NOT EXISTS api_requests ( id INTEGER PRIMARY KEY AUTOINCREMENT, api_key TEXT, endpoint TEXT NOT NULL, method TEXT NOT NULL, status_code INTEGER, response_time INTEGER, -- milliseconds timestamp INTEGER NOT NULL ); -- Rate limiting CREATE TABLE IF NOT EXISTS rate_limits ( api_key TEXT NOT NULL, window_start INTEGER NOT NULL, request_count INTEGER DEFAULT 0, PRIMARY KEY (api_key, window_start) ); -- Create indexes CREATE INDEX IF NOT EXISTS idx_api_requests_timestamp ON api_requests(timestamp); CREATE INDEX IF NOT EXISTS idx_api_requests_api_key ON api_requests(api_key); CREATE INDEX IF NOT EXISTS idx_rate_limits_window ON rate_limits(window_start); SQL # Create default admin API key local admin_key="blackroad_$(openssl rand -hex 16)" local timestamp=$(date +%s) sqlite3 "$API_DB" </dev/null) echo "HTTP/1.1 200 OK" echo "Content-Type: application/json" echo "X-RateLimit-Remaining: $remaining" echo "" echo "$result" local end_time=$(date +%s%3N) local duration=$((end_time - start_time)) log_request "$api_key" "$endpoint" "$method" 200 "$duration" ;; /api/memory/search) local query="$data" if [ -z "$query" ]; then echo "HTTP/1.1 400 Bad Request" echo "Content-Type: application/json" echo "" echo '{"error":"Query required"}' log_request "$api_key" "$endpoint" "$method" 400 0 return 1 fi local result=$(~/memory-query.sh search "$query" 2>/dev/null) echo "HTTP/1.1 200 OK" echo "Content-Type: application/json" echo "X-RateLimit-Remaining: $remaining" echo "" echo "$result" local end_time=$(date +%s%3N) local duration=$((end_time - start_time)) log_request "$api_key" "$endpoint" "$method" 200 "$duration" ;; /api/memory/stats) local result=$(~/memory-query.sh stats 2>/dev/null | tail -n +2) # Skip header echo "HTTP/1.1 200 OK" echo "Content-Type: application/json" echo "X-RateLimit-Remaining: $remaining" echo "" echo "{\"stats\":\"$result\"}" local end_time=$(date +%s%3N) local duration=$((end_time - start_time)) log_request "$api_key" "$endpoint" "$method" 200 "$duration" ;; /api/codex/search) local query="$data" if [ -z "$query" ]; then echo "HTTP/1.1 400 Bad Request" echo "Content-Type: application/json" echo "" echo '{"error":"Query required"}' log_request "$api_key" "$endpoint" "$method" 400 0 return 1 fi local result=$(~/memory-codex.sh search "$query" 2>/dev/null) echo "HTTP/1.1 200 OK" echo "Content-Type: application/json" echo "X-RateLimit-Remaining: $remaining" echo "" echo "$result" local end_time=$(date +%s%3N) local duration=$((end_time - start_time)) log_request "$api_key" "$endpoint" "$method" 200 "$duration" ;; /api/codex/recommend) local problem="$data" if [ -z "$problem" ]; then echo "HTTP/1.1 400 Bad Request" echo "Content-Type: application/json" echo "" echo '{"error":"Problem description required"}' log_request "$api_key" "$endpoint" "$method" 400 0 return 1 fi local result=$(~/memory-codex.sh recommend "$problem" 2>/dev/null) echo "HTTP/1.1 200 OK" echo "Content-Type: application/json" echo "X-RateLimit-Remaining: $remaining" echo "" echo "$result" local end_time=$(date +%s%3N) local duration=$((end_time - start_time)) log_request "$api_key" "$endpoint" "$method" 200 "$duration" ;; /api/predict/success) local entity="$data" if [ -z "$entity" ]; then echo "HTTP/1.1 400 Bad Request" echo "Content-Type: application/json" echo "" echo '{"error":"Entity required"}' log_request "$api_key" "$endpoint" "$method" 400 0 return 1 fi local result=$(~/memory-predictor.sh predict "$entity" 2>/dev/null) echo "HTTP/1.1 200 OK" echo "Content-Type: application/json" echo "X-RateLimit-Remaining: $remaining" echo "" echo "{\"prediction\":\"$result\"}" local end_time=$(date +%s%3N) local duration=$((end_time - start_time)) log_request "$api_key" "$endpoint" "$method" 200 "$duration" ;; /api/health) echo "HTTP/1.1 200 OK" echo "Content-Type: application/json" echo "X-RateLimit-Remaining: $remaining" echo "" echo '{"status":"healthy","timestamp":'$(date +%s)'}' local end_time=$(date +%s%3N) local duration=$((end_time - start_time)) log_request "$api_key" "$endpoint" "$method" 200 "$duration" ;; *) echo "HTTP/1.1 404 Not Found" echo "Content-Type: application/json" echo "" echo "{\"error\":\"Endpoint not found: $endpoint\"}" log_request "$api_key" "$endpoint" "$method" 404 0 ;; esac } # Start API server start_server() { echo -e "${CYAN}🔌 Starting API server on port $API_PORT...${NC}" echo -e "${YELLOW}💡 Test with: curl -H 'X-API-Key: YOUR_KEY' http://localhost:$API_PORT/api/health${NC}\n" while true; do { # Read HTTP request read -r request_line method=$(echo "$request_line" | awk '{print $1}') endpoint=$(echo "$request_line" | awk '{print $2}') # Read headers api_key="" content_length=0 while read -r header; do header=$(echo "$header" | tr -d '\r\n') [ -z "$header" ] && break # Extract API key if echo "$header" | grep -qi "^X-API-Key:"; then api_key=$(echo "$header" | cut -d: -f2- | sed 's/^ *//') fi # Extract content length if echo "$header" | grep -qi "^Content-Length:"; then content_length=$(echo "$header" | cut -d: -f2- | sed 's/^ *//') fi done # Read body if present data="" if [ "$content_length" -gt 0 ]; then data=$(head -c "$content_length") fi # Handle request handle_rest "$method" "$endpoint" "$api_key" "$data" } | nc -l "$API_PORT" 2>/dev/null sleep 0.1 done } # Show API statistics show_stats() { echo -e "${PURPLE}╔════════════════════════════════════════════════╗${NC}" echo -e "${PURPLE}║ API Server Statistics ║${NC}" echo -e "${PURPLE}╚════════════════════════════════════════════════╝${NC}\n" # Total requests local total=$(sqlite3 "$API_DB" "SELECT COUNT(*) FROM api_requests") echo -e "${CYAN}Total Requests:${NC} $total" # Requests by endpoint echo -e "\n${PURPLE}Top Endpoints:${NC}" sqlite3 -header -column "$API_DB" < "$docs_file" <<'DOCS' # BlackRoad Memory API Documentation ## Base URL ``` http://localhost:8888 ``` ## Authentication All requests require an API key in the `X-API-Key` header: ```bash curl -H "X-API-Key: YOUR_API_KEY" http://localhost:8888/api/health ``` ## Rate Limits - Default: 1,000 requests/hour - Admin keys: 10,000 requests/hour - Rate limit info in `X-RateLimit-Remaining` header ## Endpoints ### Health Check ``` GET /api/health ``` **Response:** ```json { "status": "healthy", "timestamp": 1234567890 } ``` ### Recent Memory Entries ``` GET /api/memory/recent?limit=10 ``` **Parameters:** - `limit` (optional): Number of entries (default: 10) **Response:** ```json [ { "action": "enhanced", "entity": "blackroad-os-web", "timestamp": 1234567890 } ] ``` ### Search Memory ``` POST /api/memory/search Content-Type: text/plain cloudflare deployment ``` **Response:** ```json [ { "action": "deployed", "entity": "blackroad-os-dashboard", "details": "Deployed to Cloudflare Pages" } ] ``` ### Memory Statistics ``` GET /api/memory/stats ``` **Response:** ```json { "stats": "Total Entries: 2588\nActions: 45 types\n..." } ``` ### Search Codex ``` POST /api/codex/search Content-Type: text/plain retry logic ``` **Response:** ```json { "solutions": [...], "patterns": [...], "best_practices": [...] } ``` ### Get Recommendations ``` POST /api/codex/recommend Content-Type: text/plain high failure rate ``` **Response:** ```json { "recommendations": [ { "type": "solution", "name": "Exponential Backoff", "success_rate": 95 } ] } ``` ### Predict Success ``` POST /api/predict/success Content-Type: text/plain blackroad-cloud ``` **Response:** ```json { "prediction": "MEDIUM probability - Consider pre-checks (45% historical success)" } ``` ## Error Codes - `200 OK` - Success - `400 Bad Request` - Invalid request - `401 Unauthorized` - Invalid API key - `404 Not Found` - Endpoint not found - `429 Too Many Requests` - Rate limit exceeded ## Examples ### Bash ```bash # Health check curl -H "X-API-Key: YOUR_KEY" http://localhost:8888/api/health # Recent entries curl -H "X-API-Key: YOUR_KEY" http://localhost:8888/api/memory/recent?limit=5 # Search memory curl -X POST -H "X-API-Key: YOUR_KEY" -d "cloudflare" http://localhost:8888/api/memory/search # Get recommendations curl -X POST -H "X-API-Key: YOUR_KEY" -d "deployment failing" http://localhost:8888/api/codex/recommend ``` ### JavaScript ```javascript const API_KEY = 'YOUR_KEY'; const BASE_URL = 'http://localhost:8888'; // Fetch recent entries fetch(`${BASE_URL}/api/memory/recent?limit=10`, { headers: { 'X-API-Key': API_KEY } }) .then(res => res.json()) .then(data => console.log(data)); // Search memory fetch(`${BASE_URL}/api/memory/search`, { method: 'POST', headers: { 'X-API-Key': API_KEY }, body: 'cloudflare' }) .then(res => res.json()) .then(data => console.log(data)); ``` ### Python ```python import requests API_KEY = 'YOUR_KEY' BASE_URL = 'http://localhost:8888' # Health check response = requests.get( f'{BASE_URL}/api/health', headers={'X-API-Key': API_KEY} ) print(response.json()) # Search memory response = requests.post( f'{BASE_URL}/api/memory/search', headers={'X-API-Key': API_KEY}, data='cloudflare' ) print(response.json()) ``` ## Rate Limit Headers Every response includes rate limit information: ``` X-RateLimit-Remaining: 995 ``` When rate limit is exceeded, you'll receive a `429` response: ```json { "error": "Rate limit exceeded" } ``` Wait until the next hour window to resume requests. --- **BlackRoad Memory API Server** *Real-time access to memory, codex, and predictions* DOCS echo -e "${GREEN}✓${NC} API documentation created: $docs_file" } # Main execution case "${1:-help}" in init) init create_docs ;; start) start_server ;; stats) show_stats ;; keys) list_keys ;; create-key) create_key "$2" "$3" "$4" ;; revoke-key) revoke_key "$2" ;; docs) create_docs ;; help|*) echo -e "${PURPLE}╔════════════════════════════════════════════════╗${NC}" echo -e "${PURPLE}║ 🔌 Memory API Server ║${NC}" echo -e "${PURPLE}╚════════════════════════════════════════════════╝${NC}\n" echo "REST API for BlackRoad Memory System" echo "" echo "Usage: $0 COMMAND [OPTIONS]" echo "" echo "Setup:" echo " init - Initialize API server" echo "" echo "Server:" echo " start - Start API server" echo "" echo "API Keys:" echo " keys - List all API keys" echo " create-key NAME [PERMS] [LIMIT] - Create new API key" echo " revoke-key KEY - Revoke API key" echo "" echo "Monitoring:" echo " stats - Show API statistics" echo "" echo "Documentation:" echo " docs - Create API documentation" echo "" echo "Examples:" echo " $0 init" echo " $0 start" echo " $0 create-key 'My App' '{\"read\":true}' 5000" echo " $0 stats" echo "" echo "Test API:" echo " curl -H 'X-API-Key: YOUR_KEY' http://localhost:$API_PORT/api/health" ;; esac