docs: update HOWTO.md with security auth flow, JWT headers, and config
Some checks failed
CI / build (push) Failing after 4s
Some checks failed
CI / build (push) Failing after 4s
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
87
HOWTO.md
87
HOWTO.md
@@ -35,32 +35,66 @@ ClickHouse credentials: `cameleer` / `cameleer_dev`, database `cameleer3`.
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
mvn clean package -DskipTests
|
mvn clean package -DskipTests
|
||||||
java -jar cameleer3-server-app/target/cameleer3-server-app-1.0-SNAPSHOT.jar
|
CAMELEER_AUTH_TOKEN=my-secret-token java -jar cameleer3-server-app/target/cameleer3-server-app-1.0-SNAPSHOT.jar
|
||||||
```
|
```
|
||||||
|
|
||||||
The server starts on **port 8081**.
|
The server starts on **port 8081**. The `CAMELEER_AUTH_TOKEN` environment variable is **required** — the server fails fast on startup if it is not set.
|
||||||
|
|
||||||
|
For token rotation without downtime, set `CAMELEER_AUTH_TOKEN_PREVIOUS` to the old token while rolling out the new one. The server accepts both during the overlap window.
|
||||||
|
|
||||||
## API Endpoints
|
## API Endpoints
|
||||||
|
|
||||||
|
### Authentication (Phase 4)
|
||||||
|
|
||||||
|
All endpoints except health, registration, and docs require a JWT Bearer token. The typical flow:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Register agent (requires bootstrap token)
|
||||||
|
curl -s -X POST http://localhost:8081/api/v1/agents/register \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-H "Authorization: Bearer my-secret-token" \
|
||||||
|
-d '{"agentId":"agent-1","name":"Order Service","group":"order-service-prod","version":"1.0.0","routeIds":["route-1"],"capabilities":["deep-trace","replay"]}'
|
||||||
|
# Response includes: accessToken, refreshToken, serverPublicKey (Ed25519, Base64)
|
||||||
|
|
||||||
|
# 2. Use access token for all subsequent requests
|
||||||
|
TOKEN="<accessToken from registration>"
|
||||||
|
|
||||||
|
# 3. Refresh when access token expires (1h default)
|
||||||
|
curl -s -X POST http://localhost:8081/api/v1/agents/agent-1/refresh \
|
||||||
|
-H "Authorization: Bearer <refreshToken>"
|
||||||
|
# Response: { "accessToken": "new-jwt" }
|
||||||
|
```
|
||||||
|
|
||||||
|
**Public endpoints (no JWT required):** `GET /api/v1/health`, `POST /api/v1/agents/register` (uses bootstrap token), OpenAPI/Swagger docs.
|
||||||
|
|
||||||
|
**Protected endpoints (JWT required):** All other endpoints including ingestion, search, agent management, commands.
|
||||||
|
|
||||||
|
**SSE connections:** Authenticated via query parameter: `/agents/{id}/events?token=<jwt>` (EventSource API doesn't support custom headers).
|
||||||
|
|
||||||
|
**Ed25519 signatures:** All SSE command payloads (config-update, deep-trace, replay) include a `signature` field. Agents verify payload integrity using the `serverPublicKey` received during registration. The server generates a new ephemeral keypair on each startup — agents must re-register to get the new key.
|
||||||
|
|
||||||
### Ingestion (POST, returns 202 Accepted)
|
### Ingestion (POST, returns 202 Accepted)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Post route execution data
|
# Post route execution data (JWT required)
|
||||||
curl -s -X POST http://localhost:8081/api/v1/data/executions \
|
curl -s -X POST http://localhost:8081/api/v1/data/executions \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-H "X-Protocol-Version: 1" \
|
-H "X-Protocol-Version: 1" \
|
||||||
|
-H "Authorization: Bearer $TOKEN" \
|
||||||
-d '{"agentId":"agent-1","routeId":"route-1","executionId":"exec-1","status":"COMPLETED","startTime":"2026-03-11T00:00:00Z","endTime":"2026-03-11T00:00:01Z","processorExecutions":[]}'
|
-d '{"agentId":"agent-1","routeId":"route-1","executionId":"exec-1","status":"COMPLETED","startTime":"2026-03-11T00:00:00Z","endTime":"2026-03-11T00:00:01Z","processorExecutions":[]}'
|
||||||
|
|
||||||
# Post route diagram
|
# Post route diagram
|
||||||
curl -s -X POST http://localhost:8081/api/v1/data/diagrams \
|
curl -s -X POST http://localhost:8081/api/v1/data/diagrams \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-H "X-Protocol-Version: 1" \
|
-H "X-Protocol-Version: 1" \
|
||||||
|
-H "Authorization: Bearer $TOKEN" \
|
||||||
-d '{"agentId":"agent-1","routeId":"route-1","version":1,"nodes":[],"edges":[]}'
|
-d '{"agentId":"agent-1","routeId":"route-1","version":1,"nodes":[],"edges":[]}'
|
||||||
|
|
||||||
# Post agent metrics
|
# Post agent metrics
|
||||||
curl -s -X POST http://localhost:8081/api/v1/data/metrics \
|
curl -s -X POST http://localhost:8081/api/v1/data/metrics \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-H "X-Protocol-Version: 1" \
|
-H "X-Protocol-Version: 1" \
|
||||||
|
-H "Authorization: Bearer $TOKEN" \
|
||||||
-d '[{"agentId":"agent-1","metricName":"cpu","value":42.0,"timestamp":"2026-03-11T00:00:00Z","tags":{}}]'
|
-d '[{"agentId":"agent-1","metricName":"cpu","value":42.0,"timestamp":"2026-03-11T00:00:00Z","tags":{}}]'
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -83,29 +117,36 @@ open http://localhost:8081/api/v1/swagger-ui.html
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Search by status (GET with basic filters)
|
# Search by status (GET with basic filters)
|
||||||
curl -s "http://localhost:8081/api/v1/search/executions?status=COMPLETED&limit=10"
|
curl -s -H "Authorization: Bearer $TOKEN" \
|
||||||
|
"http://localhost:8081/api/v1/search/executions?status=COMPLETED&limit=10"
|
||||||
|
|
||||||
# Search by time range
|
# Search by time range
|
||||||
curl -s "http://localhost:8081/api/v1/search/executions?timeFrom=2026-03-11T00:00:00Z&timeTo=2026-03-12T00:00:00Z"
|
curl -s -H "Authorization: Bearer $TOKEN" \
|
||||||
|
"http://localhost:8081/api/v1/search/executions?timeFrom=2026-03-11T00:00:00Z&timeTo=2026-03-12T00:00:00Z"
|
||||||
|
|
||||||
# Advanced search (POST with full-text)
|
# Advanced search (POST with full-text)
|
||||||
curl -s -X POST http://localhost:8081/api/v1/search/executions \
|
curl -s -X POST http://localhost:8081/api/v1/search/executions \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
|
-H "Authorization: Bearer $TOKEN" \
|
||||||
-d '{"status":"FAILED","text":"NullPointerException","limit":20}'
|
-d '{"status":"FAILED","text":"NullPointerException","limit":20}'
|
||||||
|
|
||||||
# Transaction detail (nested processor tree)
|
# Transaction detail (nested processor tree)
|
||||||
curl -s http://localhost:8081/api/v1/executions/{executionId}
|
curl -s -H "Authorization: Bearer $TOKEN" \
|
||||||
|
http://localhost:8081/api/v1/executions/{executionId}
|
||||||
|
|
||||||
# Processor exchange snapshot
|
# Processor exchange snapshot
|
||||||
curl -s http://localhost:8081/api/v1/executions/{executionId}/processors/{index}/snapshot
|
curl -s -H "Authorization: Bearer $TOKEN" \
|
||||||
|
http://localhost:8081/api/v1/executions/{executionId}/processors/{index}/snapshot
|
||||||
|
|
||||||
# Render diagram as SVG
|
# Render diagram as SVG
|
||||||
curl -s http://localhost:8081/api/v1/diagrams/{contentHash}/render \
|
curl -s -H "Authorization: Bearer $TOKEN" \
|
||||||
-H "Accept: image/svg+xml"
|
-H "Accept: image/svg+xml" \
|
||||||
|
http://localhost:8081/api/v1/diagrams/{contentHash}/render
|
||||||
|
|
||||||
# Render diagram as JSON layout
|
# Render diagram as JSON layout
|
||||||
curl -s http://localhost:8081/api/v1/diagrams/{contentHash}/render \
|
curl -s -H "Authorization: Bearer $TOKEN" \
|
||||||
-H "Accept: application/json"
|
-H "Accept: application/json" \
|
||||||
|
http://localhost:8081/api/v1/diagrams/{contentHash}/render
|
||||||
```
|
```
|
||||||
|
|
||||||
**Search response format:** `{ "data": [...], "total": N, "offset": 0, "limit": 50 }`
|
**Search response format:** `{ "data": [...], "total": N, "offset": 0, "limit": 50 }`
|
||||||
@@ -117,38 +158,44 @@ curl -s http://localhost:8081/api/v1/diagrams/{contentHash}/render \
|
|||||||
### Agent Registry & SSE (Phase 3)
|
### Agent Registry & SSE (Phase 3)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Register an agent
|
# Register an agent (uses bootstrap token, not JWT — see Authentication section above)
|
||||||
curl -s -X POST http://localhost:8081/api/v1/agents/register \
|
curl -s -X POST http://localhost:8081/api/v1/agents/register \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
|
-H "Authorization: Bearer my-secret-token" \
|
||||||
-d '{"agentId":"agent-1","name":"Order Service","group":"order-service-prod","version":"1.0.0","routeIds":["route-1","route-2"],"capabilities":["deep-trace","replay"]}'
|
-d '{"agentId":"agent-1","name":"Order Service","group":"order-service-prod","version":"1.0.0","routeIds":["route-1","route-2"],"capabilities":["deep-trace","replay"]}'
|
||||||
|
|
||||||
# Heartbeat (call every 30s)
|
# Heartbeat (call every 30s)
|
||||||
curl -s -X POST http://localhost:8081/api/v1/agents/agent-1/heartbeat
|
curl -s -X POST http://localhost:8081/api/v1/agents/agent-1/heartbeat \
|
||||||
|
-H "Authorization: Bearer $TOKEN"
|
||||||
|
|
||||||
# List agents (optionally filter by status)
|
# List agents (optionally filter by status)
|
||||||
curl -s "http://localhost:8081/api/v1/agents"
|
curl -s -H "Authorization: Bearer $TOKEN" "http://localhost:8081/api/v1/agents"
|
||||||
curl -s "http://localhost:8081/api/v1/agents?status=LIVE"
|
curl -s -H "Authorization: Bearer $TOKEN" "http://localhost:8081/api/v1/agents?status=LIVE"
|
||||||
|
|
||||||
# Connect to SSE event stream (agent receives commands here)
|
# Connect to SSE event stream (JWT via query parameter)
|
||||||
curl -s -N http://localhost:8081/api/v1/agents/agent-1/events
|
curl -s -N "http://localhost:8081/api/v1/agents/agent-1/events?token=$TOKEN"
|
||||||
|
|
||||||
# Send command to single agent
|
# Send command to single agent
|
||||||
curl -s -X POST http://localhost:8081/api/v1/agents/agent-1/commands \
|
curl -s -X POST http://localhost:8081/api/v1/agents/agent-1/commands \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
|
-H "Authorization: Bearer $TOKEN" \
|
||||||
-d '{"type":"config-update","payload":{"samplingRate":0.5}}'
|
-d '{"type":"config-update","payload":{"samplingRate":0.5}}'
|
||||||
|
|
||||||
# Send command to agent group
|
# Send command to agent group
|
||||||
curl -s -X POST http://localhost:8081/api/v1/agents/groups/order-service-prod/commands \
|
curl -s -X POST http://localhost:8081/api/v1/agents/groups/order-service-prod/commands \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
|
-H "Authorization: Bearer $TOKEN" \
|
||||||
-d '{"type":"deep-trace","payload":{"routeId":"route-1","durationSeconds":60}}'
|
-d '{"type":"deep-trace","payload":{"routeId":"route-1","durationSeconds":60}}'
|
||||||
|
|
||||||
# Broadcast command to all live agents
|
# Broadcast command to all live agents
|
||||||
curl -s -X POST http://localhost:8081/api/v1/agents/commands \
|
curl -s -X POST http://localhost:8081/api/v1/agents/commands \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
|
-H "Authorization: Bearer $TOKEN" \
|
||||||
-d '{"type":"config-update","payload":{"samplingRate":1.0}}'
|
-d '{"type":"config-update","payload":{"samplingRate":1.0}}'
|
||||||
|
|
||||||
# Acknowledge command delivery
|
# Acknowledge command delivery
|
||||||
curl -s -X POST http://localhost:8081/api/v1/agents/agent-1/commands/{commandId}/ack
|
curl -s -X POST http://localhost:8081/api/v1/agents/agent-1/commands/{commandId}/ack \
|
||||||
|
-H "Authorization: Bearer $TOKEN"
|
||||||
```
|
```
|
||||||
|
|
||||||
**Agent lifecycle:** LIVE (heartbeat within 90s) → STALE (missed 3 heartbeats) → DEAD (5min after STALE). DEAD agents kept indefinitely.
|
**Agent lifecycle:** LIVE (heartbeat within 90s) → STALE (missed 3 heartbeats) → DEAD (5min after STALE). DEAD agents kept indefinitely.
|
||||||
@@ -177,6 +224,10 @@ Key settings in `cameleer3-server-app/src/main/resources/application.yml`:
|
|||||||
| `agent-registry.dead-threshold-seconds` | 300 | Time after STALE before DEAD |
|
| `agent-registry.dead-threshold-seconds` | 300 | Time after STALE before DEAD |
|
||||||
| `agent-registry.command-expiry-seconds` | 60 | Pending command TTL |
|
| `agent-registry.command-expiry-seconds` | 60 | Pending command TTL |
|
||||||
| `agent-registry.keepalive-interval-seconds` | 15 | SSE ping keepalive interval |
|
| `agent-registry.keepalive-interval-seconds` | 15 | SSE ping keepalive interval |
|
||||||
|
| `security.access-token-expiry-ms` | 3600000 | JWT access token lifetime (1h) |
|
||||||
|
| `security.refresh-token-expiry-ms` | 604800000 | Refresh token lifetime (7d) |
|
||||||
|
| `security.bootstrap-token` | `${CAMELEER_AUTH_TOKEN}` | Bootstrap token for agent registration (required) |
|
||||||
|
| `security.bootstrap-token-previous` | `${CAMELEER_AUTH_TOKEN_PREVIOUS}` | Previous bootstrap token for rotation (optional) |
|
||||||
|
|
||||||
## Running Tests
|
## Running Tests
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user