# PhotonCloud REST API Patterns **Status:** Draft (T050.S1) **Created:** 2025-12-12 **Author:** PeerA ## Overview This document defines consistent REST API patterns for all PhotonCloud services. Goal: curl/シェルスクリプト/組み込み環境で簡単に使える HTTP API. ## URL Structure ``` {scheme}://{host}:{port}/api/v1/{resource}[/{id}][/{action}] ``` ### Examples ``` GET /api/v1/kv/mykey # Get key PUT /api/v1/kv/mykey # Put key DELETE /api/v1/kv/mykey # Delete key GET /api/v1/vms # List VMs POST /api/v1/vms # Create VM GET /api/v1/vms/vm-123 # Get VM DELETE /api/v1/vms/vm-123 # Delete VM POST /api/v1/vms/vm-123/start # Start VM (action) POST /api/v1/vms/vm-123/stop # Stop VM (action) ``` ## HTTP Methods | Method | Usage | Idempotent | |--------|-------|------------| | GET | Read resource(s) | Yes | | POST | Create resource or execute action | No | | PUT | Create or replace resource | Yes | | DELETE | Delete resource | Yes | | PATCH | Partial update (optional) | No | ## Request Format ### Content-Type ``` Content-Type: application/json ``` ### Authentication ``` Authorization: Bearer ``` Token obtained from IAM: ```bash # Get token curl -X POST http://iam:8081/api/v1/auth/token \ -H "Content-Type: application/json" \ -d '{"username": "admin", "password": "secret"}' # Response {"token": "eyJ..."} # Use token curl http://chainfire:8082/api/v1/kv/mykey \ -H "Authorization: Bearer eyJ..." ``` ### Request Body (POST/PUT) ```json { "field1": "value1", "field2": 123 } ``` ## Response Format ### Success Response ```json { "data": { // Resource data }, "meta": { "request_id": "req-abc123", "timestamp": "2025-12-12T01:40:00Z" } } ``` ### List Response ```json { "data": [ { "id": "item-1", ... }, { "id": "item-2", ... } ], "meta": { "total": 42, "limit": 20, "offset": 0, "request_id": "req-abc123" } } ``` ### Error Response ```json { "error": { "code": "NOT_FOUND", "message": "Resource not found", "details": { "resource": "vm", "id": "vm-123" } }, "meta": { "request_id": "req-abc123", "timestamp": "2025-12-12T01:40:00Z" } } ``` ### Error Codes | HTTP Status | Error Code | Description | |-------------|------------|-------------| | 400 | BAD_REQUEST | Invalid request format | | 401 | UNAUTHORIZED | Missing or invalid token | | 403 | FORBIDDEN | Insufficient permissions | | 404 | NOT_FOUND | Resource not found | | 409 | CONFLICT | Resource already exists | | 422 | VALIDATION_ERROR | Request validation failed | | 429 | RATE_LIMITED | Too many requests | | 500 | INTERNAL_ERROR | Server error | | 503 | SERVICE_UNAVAILABLE | Service temporarily unavailable | ## Pagination ### Request ``` GET /api/v1/vms?limit=20&offset=40 ``` ### Parameters | Param | Type | Default | Max | Description | |-------|------|---------|-----|-------------| | limit | int | 20 | 100 | Items per page | | offset | int | 0 | - | Skip N items | ## Filtering ### Query Parameters ``` GET /api/v1/vms?status=running&project_id=proj-123 ``` ### Prefix Search (KV) ``` GET /api/v1/kv?prefix=config/ ``` ## Port Convention | Service | gRPC Port | HTTP Port | |---------|-----------|-----------| | ChainFire | 50051 | 8081 | | FlareDB | 50052 | 8082 | | IAM | 50053 | 8083 | | PlasmaVMC | 50054 | 8084 | | k8shost | 50055 | 8085 | | LightningSTOR | 50056 | 8086 | | CreditService | 50057 | 8087 | | PrismNET | 50058 | 8088 | | NightLight | 50059 | 8089 | | FiberLB | 50060 | 8090 | | FlashDNS | 50061 | 8091 | ## Service-Specific Endpoints ### ChainFire (KV Store) ``` GET /api/v1/kv/{key} # Get value PUT /api/v1/kv/{key} # Put value {"value": "..."} DELETE /api/v1/kv/{key} # Delete key GET /api/v1/kv?prefix={prefix} # Range scan GET /api/v1/cluster/status # Cluster health POST /api/v1/cluster/members # Add member ``` ### FlareDB (Database) ``` POST /api/v1/sql # Execute SQL {"query": "SELECT ..."} GET /api/v1/tables # List tables GET /api/v1/kv/{key} # KV get PUT /api/v1/kv/{key} # KV put GET /api/v1/scan?start={}&end={} # Range scan ``` ### IAM (Authentication) ``` POST /api/v1/auth/token # Get token POST /api/v1/auth/verify # Verify token GET /api/v1/users # List users POST /api/v1/users # Create user GET /api/v1/users/{id} # Get user DELETE /api/v1/users/{id} # Delete user GET /api/v1/projects # List projects POST /api/v1/projects # Create project ``` ### PlasmaVMC (VM Management) ``` GET /api/v1/vms # List VMs POST /api/v1/vms # Create VM GET /api/v1/vms/{id} # Get VM DELETE /api/v1/vms/{id} # Delete VM POST /api/v1/vms/{id}/start # Start VM POST /api/v1/vms/{id}/stop # Stop VM POST /api/v1/vms/{id}/reboot # Reboot VM GET /api/v1/vms/{id}/console # Get console URL ``` ### k8shost (Kubernetes) ``` GET /api/v1/pods # List pods POST /api/v1/pods # Create pod GET /api/v1/pods/{name} # Get pod DELETE /api/v1/pods/{name} # Delete pod GET /api/v1/services # List services POST /api/v1/services # Create service GET /api/v1/nodes # List nodes ``` ### CreditService (Billing) ``` GET /api/v1/wallets/{project_id} # Get wallet balance POST /api/v1/wallets/{project_id}/reserve # Reserve credits POST /api/v1/wallets/{project_id}/commit # Commit reservation POST /api/v1/wallets/{project_id}/release # Release reservation GET /api/v1/quotas/{project_id} # Get quotas PUT /api/v1/quotas/{project_id} # Set quotas ``` ### NightLight (Metrics) - Already HTTP ``` POST /api/v1/write # Push metrics (Prometheus remote write) GET /api/v1/query?query={promql} # Instant query GET /api/v1/query_range?query={}&start={}&end={}&step={} # Range query GET /api/v1/series?match[]={}&start={}&end={} # Series metadata GET /api/v1/labels # List labels ``` ### LightningSTOR (S3) - Already HTTP ``` # S3-compatible (different port/path) PUT /{bucket} # Create bucket DELETE /{bucket} # Delete bucket GET / # List buckets PUT /{bucket}/{key} # Put object GET /{bucket}/{key} # Get object DELETE /{bucket}/{key} # Delete object GET /{bucket}?list-type=2 # List objects ``` ## curl Examples ### ChainFire KV ```bash # Put curl -X PUT http://localhost:8081/api/v1/kv/mykey \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $TOKEN" \ -d '{"value": "hello world"}' # Get curl http://localhost:8081/api/v1/kv/mykey \ -H "Authorization: Bearer $TOKEN" # Delete curl -X DELETE http://localhost:8081/api/v1/kv/mykey \ -H "Authorization: Bearer $TOKEN" ``` ### PlasmaVMC ```bash # Create VM curl -X POST http://localhost:8084/api/v1/vms \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $TOKEN" \ -d '{ "name": "my-vm", "cpu_cores": 2, "memory_mb": 4096, "disk_gb": 20, "image": "ubuntu-22.04" }' # List VMs curl http://localhost:8084/api/v1/vms \ -H "Authorization: Bearer $TOKEN" # Start VM curl -X POST http://localhost:8084/api/v1/vms/vm-123/start \ -H "Authorization: Bearer $TOKEN" ``` ### FlareDB SQL ```bash # Execute query curl -X POST http://localhost:8082/api/v1/sql \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $TOKEN" \ -d '{"query": "SELECT * FROM users WHERE id = 1"}' ``` ## Implementation Notes ### Rust Framework Use `axum` (already in most services): ```rust use axum::{ routing::{get, post, put, delete}, Router, Json, extract::Path, }; let app = Router::new() .route("/api/v1/kv/:key", get(get_kv).put(put_kv).delete(delete_kv)) .route("/api/v1/cluster/status", get(cluster_status)); ``` ### Run Alongside gRPC ```rust // Start both servers tokio::select! { _ = grpc_server.serve(grpc_addr) => {}, _ = axum::Server::bind(&http_addr).serve(app.into_make_service()) => {}, } ``` ### Error Mapping ```rust impl From for HttpError { fn from(e: ServiceError) -> Self { match e { ServiceError::NotFound(_) => HttpError::not_found(e.to_string()), ServiceError::AlreadyExists(_) => HttpError::conflict(e.to_string()), ServiceError::InvalidArgument(_) => HttpError::bad_request(e.to_string()), _ => HttpError::internal(e.to_string()), } } } ``` ## References - T050 Task: docs/por/T050-rest-api/task.yaml - PROJECT.md: 統一API/仕様 - Existing HTTP: NightLight (metrics), LightningSTOR (S3)