# API Documentation Charon REST API documentation. All endpoints return JSON and use standard HTTP status codes. ## Base URL ``` http://localhost:8080/api/v1 ``` ## Authentication 🚧 Authentication is not yet implemented. All endpoints are currently public. Future authentication will use JWT tokens: ```http Authorization: Bearer ``` ## Response Format ### Success Response ```json { "uuid": "550e8400-e29b-41d4-a716-446655440000", "name": "Example", "created_at": "2025-01-18T10:00:00Z" } ``` ### Error Response ```json { "error": "Resource not found", "code": 404 } ``` ## Status Codes | Code | Description | |------|-------------| | 200 | Success | | 201 | Created | | 204 | No Content (successful deletion) | | 400 | Bad Request (validation error) | | 404 | Not Found | | 500 | Internal Server Error | ## Endpoints ### Metrics (Prometheus) Expose internal counters for scraping. ```http GET /metrics ``` No authentication required. Primary WAF metrics: ```text charon_waf_requests_total charon_waf_blocked_total charon_waf_monitored_total ``` --- ### Health Check Check API health status. ```http GET /health ``` **Response 200:** ```json { "status": "ok" } ``` --- ### Security Suite (Cerberus) #### Status ```http GET /security/status ``` Returns enabled flag plus modes for each module. #### Get Global Security Config ```http GET /security/config ``` Response 200 (no config yet): `{ "config": null }` #### Upsert Global Security Config ```http POST /security/config Content-Type: application/json ``` Request Body (example): ```json { "name": "default", "enabled": true, "admin_whitelist": "198.51.100.10,203.0.113.0/24", "crowdsec_mode": "local", "waf_mode": "monitor", "waf_rules_source": "owasp-crs-local" } ``` Response 200: `{ "config": { ... } }` #### Enable Cerberus ```http POST /security/enable ``` Payload (optional break-glass token): ```json { "break_glass_token": "abcd1234" } ``` #### Disable Cerberus ```http POST /security/disable ``` Payload (required if not localhost): ```json { "break_glass_token": "abcd1234" } ``` #### Generate Break-Glass Token ```http POST /security/breakglass/generate ``` Response 200: `{ "token": "plaintext-token-once" }` #### List Security Decisions ```http GET /security/decisions?limit=50 ``` Response 200: `{ "decisions": [ ... ] }` #### Create Manual Decision ```http POST /security/decisions Content-Type: application/json ``` Payload: ```json { "ip": "203.0.113.5", "action": "block", "details": "manual temporary block" } ``` #### List Rulesets ```http GET /security/rulesets ``` Response 200: `{ "rulesets": [ ... ] }` #### Upsert Ruleset ```http POST /security/rulesets Content-Type: application/json ``` Payload: ```json { "name": "owasp-crs-quick", "source_url": "https://example.com/owasp-crs.txt", "mode": "owasp", "content": "# raw rules" } ``` Response 200: `{ "ruleset": { ... } }` #### Delete Ruleset ```http DELETE /security/rulesets/:id ``` Response 200: `{ "deleted": true }` --- ### Proxy Hosts #### List All Proxy Hosts ```http GET /proxy-hosts ``` **Response 200:** ```json [ { "uuid": "550e8400-e29b-41d4-a716-446655440000", "domain": "example.com, www.example.com", "forward_scheme": "http", "forward_host": "localhost", "forward_port": 8080, "ssl_forced": false, "http2_support": true, "hsts_enabled": false, "hsts_subdomains": false, "block_exploits": true, "websocket_support": false, "enabled": true, "remote_server_id": null, "created_at": "2025-01-18T10:00:00Z", "updated_at": "2025-01-18T10:00:00Z" } ] ``` #### Get Proxy Host ```http GET /proxy-hosts/:uuid ``` **Parameters:** - `uuid` (path) - Proxy host UUID **Response 200:** ```json { "uuid": "550e8400-e29b-41d4-a716-446655440000", "domain": "example.com", "forward_scheme": "https", "forward_host": "backend.internal", "forward_port": 9000, "ssl_forced": true, "websocket_support": false, "enabled": true, "created_at": "2025-01-18T10:00:00Z", "updated_at": "2025-01-18T10:00:00Z" } ``` **Response 404:** ```json { "error": "Proxy host not found" } ``` #### Create Proxy Host ```http POST /proxy-hosts Content-Type: application/json ``` **Request Body:** ```json { "domain": "new.example.com", "forward_scheme": "http", "forward_host": "localhost", "forward_port": 3000, "ssl_forced": false, "http2_support": true, "hsts_enabled": false, "hsts_subdomains": false, "block_exploits": true, "websocket_support": false, "enabled": true, "remote_server_id": null } ``` **Required Fields:** - `domain` - Domain name(s), comma-separated - `forward_host` - Target hostname or IP - `forward_port` - Target port number **Optional Fields:** - `forward_scheme` - Default: `"http"` - `ssl_forced` - Default: `false` - `http2_support` - Default: `true` - `hsts_enabled` - Default: `false` - `hsts_subdomains` - Default: `false` - `block_exploits` - Default: `true` - `websocket_support` - Default: `false` - `enabled` - Default: `true` - `remote_server_id` - Default: `null` **Response 201:** ```json { "uuid": "550e8400-e29b-41d4-a716-446655440001", "domain": "new.example.com", "forward_scheme": "http", "forward_host": "localhost", "forward_port": 3000, "created_at": "2025-01-18T10:05:00Z", "updated_at": "2025-01-18T10:05:00Z" } ``` **Response 400:** ```json { "error": "domain is required" } ``` #### Update Proxy Host ```http PUT /proxy-hosts/:uuid Content-Type: application/json ``` **Parameters:** - `uuid` (path) - Proxy host UUID **Request Body:** (all fields optional) ```json { "domain": "updated.example.com", "forward_port": 8081, "ssl_forced": true } ``` **Response 200:** ```json { "uuid": "550e8400-e29b-41d4-a716-446655440000", "domain": "updated.example.com", "forward_port": 8081, "ssl_forced": true, "updated_at": "2025-01-18T10:10:00Z" } ``` #### Delete Proxy Host ```http DELETE /proxy-hosts/:uuid ``` **Parameters:** - `uuid` (path) - Proxy host UUID **Response 204:** No content **Response 404:** ```json { "error": "Proxy host not found" } ``` --- ### Remote Servers #### List All Remote Servers ```http GET /remote-servers ``` **Query Parameters:** - `enabled` (optional) - Filter by enabled status (`true` or `false`) **Response 200:** ```json [ { "uuid": "660e8400-e29b-41d4-a716-446655440000", "name": "Docker Registry", "provider": "docker", "host": "registry.local", "port": 5000, "reachable": true, "last_checked": "2025-01-18T09:55:00Z", "enabled": true, "created_at": "2025-01-18T09:00:00Z", "updated_at": "2025-01-18T09:55:00Z" } ] ``` #### Get Remote Server ```http GET /remote-servers/:uuid ``` **Parameters:** - `uuid` (path) - Remote server UUID **Response 200:** ```json { "uuid": "660e8400-e29b-41d4-a716-446655440000", "name": "Docker Registry", "provider": "docker", "host": "registry.local", "port": 5000, "reachable": true, "enabled": true } ``` #### Create Remote Server ```http POST /remote-servers Content-Type: application/json ``` **Request Body:** ```json { "name": "Production API", "provider": "generic", "host": "api.prod.internal", "port": 8080, "enabled": true } ``` **Required Fields:** - `name` - Server name - `host` - Hostname or IP - `port` - Port number **Optional Fields:** - `provider` - One of: `generic`, `docker`, `kubernetes`, `aws`, `gcp`, `azure` (default: `generic`) - `enabled` - Default: `true` **Response 201:** ```json { "uuid": "660e8400-e29b-41d4-a716-446655440001", "name": "Production API", "provider": "generic", "host": "api.prod.internal", "port": 8080, "reachable": false, "enabled": true, "created_at": "2025-01-18T10:15:00Z" } ``` #### Update Remote Server ```http PUT /remote-servers/:uuid Content-Type: application/json ``` **Request Body:** (all fields optional) ```json { "name": "Updated Name", "port": 8081, "enabled": false } ``` **Response 200:** ```json { "uuid": "660e8400-e29b-41d4-a716-446655440000", "name": "Updated Name", "port": 8081, "enabled": false, "updated_at": "2025-01-18T10:20:00Z" } ``` #### Delete Remote Server ```http DELETE /remote-servers/:uuid ``` **Response 204:** No content #### Test Remote Server Connection Test connectivity to a remote server. ```http POST /remote-servers/:uuid/test ``` **Parameters:** - `uuid` (path) - Remote server UUID **Response 200:** ```json { "reachable": true, "address": "registry.local:5000", "timestamp": "2025-01-18T10:25:00Z" } ``` **Response 200 (unreachable):** ```json { "reachable": false, "address": "offline.server:8080", "error": "connection timeout", "timestamp": "2025-01-18T10:25:00Z" } ``` **Note:** This endpoint updates the `reachable` and `last_checked` fields on the remote server. --- ### Import Workflow #### Check Import Status Check if there's an active import session. ```http GET /import/status ``` **Response 200 (no session):** ```json { "has_pending": false } ``` **Response 200 (active session):** ```json { "has_pending": true, "session": { "uuid": "770e8400-e29b-41d4-a716-446655440000", "filename": "Caddyfile", "state": "reviewing", "created_at": "2025-01-18T10:30:00Z", "updated_at": "2025-01-18T10:30:00Z" } } ``` #### Get Import Preview Get preview of hosts to be imported (only available when session state is `reviewing`). ```http GET /import/preview ``` **Response 200:** ```json { "hosts": [ { "domain": "example.com", "forward_host": "localhost", "forward_port": 8080, "forward_scheme": "http" }, { "domain": "api.example.com", "forward_host": "backend", "forward_port": 9000, "forward_scheme": "https" } ], "conflicts": [ "example.com already exists" ], "errors": [] } ``` **Response 404:** ```json { "error": "No active import session" } ``` #### Upload Caddyfile Upload a Caddyfile for import. ```http POST /import/upload Content-Type: application/json ``` **Request Body:** ```json { "content": "example.com {\n reverse_proxy localhost:8080\n}", "filename": "Caddyfile" } ``` **Required Fields:** - `content` - Caddyfile content **Optional Fields:** - `filename` - Original filename (default: `"Caddyfile"`) **Response 201:** ```json { "session": { "uuid": "770e8400-e29b-41d4-a716-446655440000", "filename": "Caddyfile", "state": "parsing", "created_at": "2025-01-18T10:35:00Z" } } ``` **Response 400:** ```json { "error": "content is required" } ``` #### Commit Import Commit the import after resolving conflicts. ```http POST /import/commit Content-Type: application/json ``` **Request Body:** ```json { "session_uuid": "770e8400-e29b-41d4-a716-446655440000", "resolutions": { "example.com": "overwrite", "api.example.com": "keep" } } ``` **Required Fields:** - `session_uuid` - Active import session UUID - `resolutions` - Map of domain to resolution strategy **Resolution Strategies:** - `"keep"` - Keep existing configuration, skip import - `"overwrite"` - Replace existing with imported configuration - `"skip"` - Same as keep **Response 200:** ```json { "imported": 2, "skipped": 1, "failed": 0 } ``` **Response 400:** ```json { "error": "Invalid session or unresolved conflicts" } ``` #### Cancel Import Cancel an active import session. ```http DELETE /import/cancel?session_uuid=770e8400-e29b-41d4-a716-446655440000 ``` **Query Parameters:** - `session_uuid` - Active import session UUID **Response 204:** No content --- ## Rate Limiting 🚧 Rate limiting is not yet implemented. Future rate limits: - 100 requests per minute per IP - 1000 requests per hour per IP ## Pagination 🚧 Pagination is not yet implemented. Future pagination: ```http GET /proxy-hosts?page=1&per_page=20 ``` ## Filtering and Sorting 🚧 Advanced filtering is not yet implemented. Future filtering: ```http GET /proxy-hosts?enabled=true&sort=created_at&order=desc ``` ## Webhooks 🚧 Webhooks are not yet implemented. Future webhook events: - `proxy_host.created` - `proxy_host.updated` - `proxy_host.deleted` - `remote_server.unreachable` - `import.completed` ## SDKs No official SDKs yet. The API follows REST conventions and can be used with any HTTP client. ### JavaScript/TypeScript Example ```typescript const API_BASE = 'http://localhost:8080/api/v1'; // List proxy hosts const hosts = await fetch(`${API_BASE}/proxy-hosts`).then(r => r.json()); // Create proxy host const newHost = await fetch(`${API_BASE}/proxy-hosts`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ domain: 'example.com', forward_host: 'localhost', forward_port: 8080 }) }).then(r => r.json()); // Test remote server const testResult = await fetch(`${API_BASE}/remote-servers/${uuid}/test`, { method: 'POST' }).then(r => r.json()); ``` ### Python Example ```python import requests API_BASE = 'http://localhost:8080/api/v1' # List proxy hosts hosts = requests.get(f'{API_BASE}/proxy-hosts').json() # Create proxy host new_host = requests.post(f'{API_BASE}/proxy-hosts', json={ 'domain': 'example.com', 'forward_host': 'localhost', 'forward_port': 8080 }).json() # Test remote server test_result = requests.post(f'{API_BASE}/remote-servers/{uuid}/test').json() ``` ## Support For API issues or questions: - GitHub Issues: https://github.com/Wikid82/charon/issues - Discussions: https://github.com/Wikid82/charon/discussions