2026-04-15 15:28:42 +02:00
# HOWTO — Cameleer Server
2026-03-11 13:51:10 +01:00
## Prerequisites
- Java 17+
- Maven 3.9+
Add React UI with Execution Explorer, auth, and standalone deployment
- Scaffold Vite + React + TypeScript frontend in ui/ with full design
system (dark/light themes) matching the HTML mockups
- Implement Execution Explorer page: search filters, results table with
expandable processor tree and exchange detail sidebar, pagination
- Add UI authentication: UiAuthController (login/refresh endpoints),
JWT filter handles ui: subject prefix, CORS configuration
- Shared components: StatusPill, DurationBar, StatCard, AppBadge,
FilterChip, Pagination — all using CSS Modules with design tokens
- API client layer: openapi-fetch with auth middleware, TanStack Query
hooks for search/detail/snapshot queries, Zustand for state
- Standalone deployment: Nginx Dockerfile, K8s Deployment + ConfigMap +
NodePort (30080), runtime config.js for API base URL
- Embedded mode: maven-resources-plugin copies ui/dist into JAR static
resources, SPA forward controller for client-side routing
- CI/CD: UI build step, Docker build/push for server-ui image, K8s
deploy step for UI, UI credential secrets
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 13:59:22 +01:00
- Node.js 22+ and npm
2026-03-11 13:51:10 +01:00
- Docker & Docker Compose
2026-04-15 15:28:42 +02:00
- Access to the Gitea Maven registry (for `cameleer-common` dependency)
2026-03-11 13:51:10 +01:00
## Build
```bash
Add React UI with Execution Explorer, auth, and standalone deployment
- Scaffold Vite + React + TypeScript frontend in ui/ with full design
system (dark/light themes) matching the HTML mockups
- Implement Execution Explorer page: search filters, results table with
expandable processor tree and exchange detail sidebar, pagination
- Add UI authentication: UiAuthController (login/refresh endpoints),
JWT filter handles ui: subject prefix, CORS configuration
- Shared components: StatusPill, DurationBar, StatCard, AppBadge,
FilterChip, Pagination — all using CSS Modules with design tokens
- API client layer: openapi-fetch with auth middleware, TanStack Query
hooks for search/detail/snapshot queries, Zustand for state
- Standalone deployment: Nginx Dockerfile, K8s Deployment + ConfigMap +
NodePort (30080), runtime config.js for API base URL
- Embedded mode: maven-resources-plugin copies ui/dist into JAR static
resources, SPA forward controller for client-side routing
- CI/CD: UI build step, Docker build/push for server-ui image, K8s
deploy step for UI, UI credential secrets
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 13:59:22 +01:00
# Build UI first (required for embedded mode)
cd ui && npm ci && npm run build && cd ..
# Backend
2026-03-11 13:51:10 +01:00
mvn clean compile # compile only
mvn clean verify # compile + run all tests (needs Docker for integration tests)
```
2026-04-21 19:41:30 +02:00
## Start a brand-new local environment (Docker)
2026-03-11 13:51:10 +01:00
2026-04-21 19:41:30 +02:00
The repo ships a `docker-compose.yml` with the full stack: PostgreSQL, ClickHouse, the Spring Boot server, and the nginx-served SPA. All dev defaults are baked into the compose file — no `.env` file or extra config needed for a first run.
2026-03-11 13:51:10 +01:00
```bash
2026-04-21 19:41:30 +02:00
# 1. Clean slate (safe if this is already a first run — noop when no volumes exist)
docker compose down -v
# 2. Build + start everything. First run rebuilds both images (~2– 4 min).
docker compose up -d --build
# 3. Watch the server come up (health check goes green in ~60– 90s after Flyway + ClickHouse init)
docker compose logs -f cameleer-server
# ready when you see "Started CameleerServerApplication in ...".
# Ctrl+C when ready — containers keep running.
# 4. Smoke test
curl -s http://localhost:8081/api/v1/health # → {"status":"UP"}
2026-03-11 13:51:10 +01:00
```
2026-04-21 19:41:30 +02:00
Open the UI at **http://localhost:8080 ** (nginx) and log in with **admin / admin ** .
| Service | Host port | URL / notes |
|------------|-----------|-------------|
| Web UI (nginx) | 8080 | http://localhost:8080 — proxies `/api` to the server |
| Server API | 8081 | http://localhost:8081/api/v1/health, http://localhost:8081/api/v1/swagger-ui.html |
| PostgreSQL | 5432 | user `cameleer` , password `cameleer_dev` , db `cameleer` |
| ClickHouse | 8123 (HTTP), 9000 (native) | user `default` , no password, db `cameleer` |
2026-03-11 13:51:10 +01:00
2026-04-21 19:41:30 +02:00
**Dev credentials baked into compose (do not use in production):**
2026-03-11 13:51:10 +01:00
2026-04-21 19:41:30 +02:00
| Purpose | Value |
|---|---|
| UI login | `admin` / `admin` |
| Bootstrap token (agent registration) | `dev-bootstrap-token-for-local-agent-registration` |
| JWT secret | `dev-jwt-secret-32-bytes-min-0123456789abcdef0123456789abcdef` |
| `CAMELEER_SERVER_RUNTIME_ENABLED` | `false` (Docker-in-Docker app orchestration off for the local stack) |
2026-03-11 13:51:10 +01:00
2026-04-21 19:41:30 +02:00
Override any of these by editing `docker-compose.yml` or passing `-e KEY=value` to `docker compose run` .
### Common lifecycle commands
2026-03-11 13:51:10 +01:00
```bash
2026-04-21 19:41:30 +02:00
# Stop everything but keep volumes (quick restart later)
docker compose stop
# Start again after a stop
docker compose start
# Apply changes to the server code / UI — rebuild just what changed
docker compose up -d --build cameleer-server
docker compose up -d --build cameleer-ui
# Wipe the environment completely (drops PG + ClickHouse volumes — all data gone)
docker compose down -v
# Fresh Flyway run by dropping just the PG volume (keeps ClickHouse data)
docker compose down
docker volume rm cameleer-server_cameleer-pgdata
docker compose up -d
```
### Infra-only mode (backend via `mvn` / UI via Vite)
If you want to iterate on backend/UI code without rebuilding the server image on every change, start just the databases and run the server + UI locally:
```bash
# 1. Only infra containers
docker compose up -d cameleer-postgres cameleer-clickhouse
# 2. Build and run the server jar against those containers
2026-03-11 13:51:10 +01:00
mvn clean package -DskipTests
2026-04-21 19:41:30 +02:00
SPRING_DATASOURCE_URL="jdbc:postgresql://localhost:5432/cameleer?currentSchema=tenant_default&ApplicationName=tenant_default" \
2026-04-06 22:08:58 +02:00
SPRING_DATASOURCE_USERNAME=cameleer \
SPRING_DATASOURCE_PASSWORD=cameleer_dev \
2026-04-21 19:41:30 +02:00
SPRING_FLYWAY_USER=cameleer \
SPRING_FLYWAY_PASSWORD=cameleer_dev \
CAMELEER_SERVER_CLICKHOUSE_URL="jdbc:clickhouse://localhost:8123/cameleer" \
CAMELEER_SERVER_CLICKHOUSE_USERNAME=default \
CAMELEER_SERVER_CLICKHOUSE_PASSWORD= \
CAMELEER_SERVER_SECURITY_BOOTSTRAPTOKEN=dev-bootstrap-token-for-local-agent-registration \
CAMELEER_SERVER_SECURITY_JWTSECRET=dev-jwt-secret-32-bytes-min-0123456789abcdef0123456789abcdef \
CAMELEER_SERVER_RUNTIME_ENABLED=false \
CAMELEER_SERVER_TENANT_ID=default \
2026-04-15 15:28:42 +02:00
java -jar cameleer-server-app/target/cameleer-server-app-1.0-SNAPSHOT.jar
2026-03-11 13:51:10 +01:00
2026-04-21 19:41:30 +02:00
# 3. In another terminal — Vite dev server on :5173 (proxies /api → :8081)
cd ui && npm install && npm run dev
```
2026-04-06 22:08:58 +02:00
2026-04-21 19:41:30 +02:00
Database schema is applied automatically: PostgreSQL via Flyway migrations on server startup, ClickHouse tables via `ClickHouseSchemaInitializer` . No manual DDL needed.
2026-03-11 21:09:59 +01:00
2026-04-21 19:41:30 +02:00
`CAMELEER_SERVER_SECURITY_BOOTSTRAPTOKEN` is **required ** for agent registration — the server fails fast on startup if it's not set. For token rotation without downtime, set `CAMELEER_SERVER_SECURITY_BOOTSTRAPTOKENPREVIOUS` to the old token while rolling out the new one — the server accepts both during the overlap window.
2026-03-11 13:51:10 +01:00
## API Endpoints
2026-03-11 21:09:59 +01:00
### 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" }
```
Add React UI with Execution Explorer, auth, and standalone deployment
- Scaffold Vite + React + TypeScript frontend in ui/ with full design
system (dark/light themes) matching the HTML mockups
- Implement Execution Explorer page: search filters, results table with
expandable processor tree and exchange detail sidebar, pagination
- Add UI authentication: UiAuthController (login/refresh endpoints),
JWT filter handles ui: subject prefix, CORS configuration
- Shared components: StatusPill, DurationBar, StatCard, AppBadge,
FilterChip, Pagination — all using CSS Modules with design tokens
- API client layer: openapi-fetch with auth middleware, TanStack Query
hooks for search/detail/snapshot queries, Zustand for state
- Standalone deployment: Nginx Dockerfile, K8s Deployment + ConfigMap +
NodePort (30080), runtime config.js for API base URL
- Embedded mode: maven-resources-plugin copies ui/dist into JAR static
resources, SPA forward controller for client-side routing
- CI/CD: UI build step, Docker build/push for server-ui image, K8s
deploy step for UI, UI credential secrets
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 13:59:22 +01:00
**UI Login (for browser access):**
```bash
# Login with UI credentials (returns JWT tokens)
curl -s -X POST http://localhost:8081/api/v1/auth/login \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"admin"}'
# Response: { "accessToken": "...", "refreshToken": "..." }
# Refresh UI token
curl -s -X POST http://localhost:8081/api/v1/auth/refresh \
-H "Content-Type: application/json" \
-d '{"refreshToken":"<refreshToken>"}'
```
Migrate config to cameleer.server.* naming convention
Move all configuration properties under the cameleer.server.* namespace
with all-lowercase dot-separated names and mechanical env var mapping
(dots→underscores, uppercase). This aligns with the agent's convention
(cameleer.agent.*) and establishes a predictable pattern across all
components.
Changes:
- Move 6 config prefixes under cameleer.server.*: agent-registry,
ingestion, security, license, clickhouse, and cameleer.tenant/runtime/indexer
- Rename all kebab-case properties to concatenated lowercase
(e.g., bootstrap-token → bootstraptoken, jar-storage-path → jarstoragepath)
- Update all env vars to CAMELEER_SERVER_* mechanical mapping
- Fix container-cpu-request/container-cpu-shares mismatch bug
- Remove displayName from AgentRegistrationRequest (redundant with instanceId)
- Update agent container env vars to CAMELEER_AGENT_* convention
- Update K8s manifests and CI workflow for new env var names
- Update CLAUDE.md, HOWTO.md, SERVER-CAPABILITIES.md documentation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 18:10:51 +02:00
UI credentials are configured via `CAMELEER_SERVER_SECURITY_UIUSER` / `CAMELEER_SERVER_SECURITY_UIPASSWORD` env vars (default: `admin` / `admin` ).
Add React UI with Execution Explorer, auth, and standalone deployment
- Scaffold Vite + React + TypeScript frontend in ui/ with full design
system (dark/light themes) matching the HTML mockups
- Implement Execution Explorer page: search filters, results table with
expandable processor tree and exchange detail sidebar, pagination
- Add UI authentication: UiAuthController (login/refresh endpoints),
JWT filter handles ui: subject prefix, CORS configuration
- Shared components: StatusPill, DurationBar, StatCard, AppBadge,
FilterChip, Pagination — all using CSS Modules with design tokens
- API client layer: openapi-fetch with auth middleware, TanStack Query
hooks for search/detail/snapshot queries, Zustand for state
- Standalone deployment: Nginx Dockerfile, K8s Deployment + ConfigMap +
NodePort (30080), runtime config.js for API base URL
- Embedded mode: maven-resources-plugin copies ui/dist into JAR static
resources, SPA forward controller for client-side routing
- CI/CD: UI build step, Docker build/push for server-ui image, K8s
deploy step for UI, UI credential secrets
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 13:59:22 +01:00
**Public endpoints (no JWT required):** `GET /api/v1/health` , `POST /api/v1/agents/register` (uses bootstrap token), `POST /api/v1/auth/**` , OpenAPI/Swagger docs.
2026-03-11 21:09:59 +01:00
**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.
2026-03-14 12:41:41 +01:00
### RBAC (Role-Based Access Control)
JWTs carry a `roles` claim. Endpoints are restricted by role:
| Role | Access |
|------|--------|
2026-03-25 11:53:27 +01:00
| `AGENT` | Data ingestion (`/data/**` — executions, diagrams, metrics, logs), heartbeat, SSE events, command ack |
2026-04-06 15:52:25 +02:00
| `VIEWER` | Search, execution detail, diagrams, agent list, app config (read-only) |
| `OPERATOR` | VIEWER + send commands to agents, route control, replay, edit app config |
| `ADMIN` | OPERATOR + user management, audit log, OIDC config, database admin (`/admin/**` ) |
2026-03-14 12:41:41 +01:00
The env-var local user gets `ADMIN` role. Agents get `AGENT` role at registration.
2026-04-06 16:29:20 +02:00
**UI role gating:** The sidebar hides the Admin section for non-ADMIN users. Admin routes (`/admin/*` ) redirect to `/` for non-admin. The diagram node toolbar and route control bar are hidden for VIEWER. Config is a main tab (`/config` shows all apps, `/config/:appId` filters to one app with detail panel; sidebar clicks stay on config tab, route clicks resolve to parent app). VIEWER sees read-only, OPERATOR+ can edit.
2026-04-06 15:52:25 +02:00
2026-03-14 12:41:41 +01:00
### OIDC Login (Optional)
2026-03-17 00:26:50 +01:00
OIDC configuration is stored in PostgreSQL and managed via the admin API or UI. The SPA checks if OIDC is available:
2026-03-14 12:41:41 +01:00
```bash
2026-03-14 13:00:13 +01:00
# 1. SPA checks if OIDC is available (returns 404 if not configured)
2026-03-14 12:41:41 +01:00
curl -s http://localhost:8081/api/v1/auth/oidc/config
# Returns: { "issuer": "...", "clientId": "...", "authorizationEndpoint": "..." }
# 2. After OIDC redirect, SPA sends the authorization code
curl -s -X POST http://localhost:8081/api/v1/auth/oidc/callback \
-H "Content-Type: application/json" \
-d '{"code":"auth-code-from-provider","redirectUri":"http://localhost:5173/callback"}'
# Returns: { "accessToken": "...", "refreshToken": "..." }
```
Local login remains available as fallback even when OIDC is enabled.
2026-03-14 13:00:13 +01:00
### OIDC Admin Configuration (ADMIN only)
OIDC settings are managed at runtime via the admin API. No server restart needed.
```bash
# Get current OIDC config
curl -s -H "Authorization: Bearer $TOKEN" http://localhost:8081/api/v1/admin/oidc
# Save OIDC config (client_secret: send "********" to keep existing, or new value to update)
curl -s -X PUT http://localhost:8081/api/v1/admin/oidc \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d '{
"enabled": true,
2026-04-13 22:51:08 +02:00
"issuerUri": "http://cameleer-logto:3001/oidc",
2026-03-14 13:00:13 +01:00
"clientId": "your-client-id",
"clientSecret": "your-client-secret",
"rolesClaim": "realm_access.roles",
"defaultRoles": ["VIEWER"]
}'
# Test OIDC provider connectivity
curl -s -X POST http://localhost:8081/api/v1/admin/oidc/test \
-H "Authorization: Bearer $TOKEN"
# Delete OIDC config (disables OIDC)
curl -s -X DELETE http://localhost:8081/api/v1/admin/oidc \
-H "Authorization: Bearer $TOKEN"
```
Migrate config to cameleer.server.* naming convention
Move all configuration properties under the cameleer.server.* namespace
with all-lowercase dot-separated names and mechanical env var mapping
(dots→underscores, uppercase). This aligns with the agent's convention
(cameleer.agent.*) and establishes a predictable pattern across all
components.
Changes:
- Move 6 config prefixes under cameleer.server.*: agent-registry,
ingestion, security, license, clickhouse, and cameleer.tenant/runtime/indexer
- Rename all kebab-case properties to concatenated lowercase
(e.g., bootstrap-token → bootstraptoken, jar-storage-path → jarstoragepath)
- Update all env vars to CAMELEER_SERVER_* mechanical mapping
- Fix container-cpu-request/container-cpu-shares mismatch bug
- Remove displayName from AgentRegistrationRequest (redundant with instanceId)
- Update agent container env vars to CAMELEER_AGENT_* convention
- Update K8s manifests and CI workflow for new env var names
- Update CLAUDE.md, HOWTO.md, SERVER-CAPABILITIES.md documentation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 18:10:51 +02:00
**Initial provisioning**: OIDC can also be seeded from `CAMELEER_SERVER_SECURITY_OIDC*` env vars on first startup (when DB is empty). After that, the admin API takes over.
2026-03-14 13:00:13 +01:00
2026-04-05 13:15:09 +02:00
### Logto Setup (OIDC Provider)
Logto is deployed alongside the Cameleer stack. After first deployment:
2026-04-05 13:31:17 +02:00
Logto is proxy-aware via `TRUST_PROXY_HEADER=1` . The `LOGTO_ENDPOINT` and `LOGTO_ADMIN_ENDPOINT` secrets define the public-facing URLs that Logto uses for OIDC discovery, issuer URI, and redirect URLs. When behind a reverse proxy (e.g., Traefik), set these to the external URLs (e.g., `https://auth.cameleer.my.domain` ). Logto needs its own subdomain — it cannot be path-prefixed under another app.
1. **Initial setup ** : Open the Logto admin console (the `LOGTO_ADMIN_ENDPOINT` URL) and create the admin account
2026-04-05 13:15:09 +02:00
2. **Create SPA application ** : Applications → Create → Single Page App
- Name: `Cameleer UI`
2026-04-05 13:31:17 +02:00
- Redirect URI: your UI URL + `/oidc/callback`
2026-04-05 13:15:09 +02:00
- Note the **Client ID **
3. **Create API Resource ** : API Resources → Create
- Name: `Cameleer Server API`
2026-04-05 13:31:17 +02:00
- Indicator: your API URL (e.g., `https://cameleer.siegeln.net/api` )
2026-04-06 10:06:11 +02:00
- Add permissions: `server:admin` , `server:operator` , `server:viewer`
2026-04-05 13:15:09 +02:00
4. **Create M2M application ** (for SaaS platform): Applications → Create → Machine-to-Machine
- Name: `Cameleer SaaS`
2026-04-06 10:06:11 +02:00
- Assign the API Resource created above with `server:admin` scope
2026-03-14 12:45:02 +01:00
- Note the **Client ID ** and **Client Secret **
2026-04-11 21:56:19 +02:00
5. **Configure Cameleer OIDC login ** : Use the admin API (`PUT /api/v1/admin/oidc` ) or the admin UI. OIDC login configuration is stored in the database — no env vars needed for the SPA OIDC flow.
2026-04-05 13:15:09 +02:00
6. **Configure resource server ** (for M2M token validation):
```
Migrate config to cameleer.server.* naming convention
Move all configuration properties under the cameleer.server.* namespace
with all-lowercase dot-separated names and mechanical env var mapping
(dots→underscores, uppercase). This aligns with the agent's convention
(cameleer.agent.*) and establishes a predictable pattern across all
components.
Changes:
- Move 6 config prefixes under cameleer.server.*: agent-registry,
ingestion, security, license, clickhouse, and cameleer.tenant/runtime/indexer
- Rename all kebab-case properties to concatenated lowercase
(e.g., bootstrap-token → bootstraptoken, jar-storage-path → jarstoragepath)
- Update all env vars to CAMELEER_SERVER_* mechanical mapping
- Fix container-cpu-request/container-cpu-shares mismatch bug
- Remove displayName from AgentRegistrationRequest (redundant with instanceId)
- Update agent container env vars to CAMELEER_AGENT_* convention
- Update K8s manifests and CI workflow for new env var names
- Update CLAUDE.md, HOWTO.md, SERVER-CAPABILITIES.md documentation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 18:10:51 +02:00
CAMELEER_SERVER_SECURITY_OIDCISSUERURI=<LOGTO_ENDPOINT>/oidc
2026-04-13 22:51:08 +02:00
CAMELEER_SERVER_SECURITY_OIDCJWKSETURI=http://cameleer-logto:3001/oidc/jwks
Migrate config to cameleer.server.* naming convention
Move all configuration properties under the cameleer.server.* namespace
with all-lowercase dot-separated names and mechanical env var mapping
(dots→underscores, uppercase). This aligns with the agent's convention
(cameleer.agent.*) and establishes a predictable pattern across all
components.
Changes:
- Move 6 config prefixes under cameleer.server.*: agent-registry,
ingestion, security, license, clickhouse, and cameleer.tenant/runtime/indexer
- Rename all kebab-case properties to concatenated lowercase
(e.g., bootstrap-token → bootstraptoken, jar-storage-path → jarstoragepath)
- Update all env vars to CAMELEER_SERVER_* mechanical mapping
- Fix container-cpu-request/container-cpu-shares mismatch bug
- Remove displayName from AgentRegistrationRequest (redundant with instanceId)
- Update agent container env vars to CAMELEER_AGENT_* convention
- Update K8s manifests and CI workflow for new env var names
- Update CLAUDE.md, HOWTO.md, SERVER-CAPABILITIES.md documentation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 18:10:51 +02:00
CAMELEER_SERVER_SECURITY_OIDCAUDIENCE=<api-resource-indicator-from-step-3>
CAMELEER_SERVER_SECURITY_OIDCTLSSKIPVERIFY=true # optional — skip cert verification for self-signed CAs
2026-03-14 12:45:02 +01:00
```
Migrate config to cameleer.server.* naming convention
Move all configuration properties under the cameleer.server.* namespace
with all-lowercase dot-separated names and mechanical env var mapping
(dots→underscores, uppercase). This aligns with the agent's convention
(cameleer.agent.*) and establishes a predictable pattern across all
components.
Changes:
- Move 6 config prefixes under cameleer.server.*: agent-registry,
ingestion, security, license, clickhouse, and cameleer.tenant/runtime/indexer
- Rename all kebab-case properties to concatenated lowercase
(e.g., bootstrap-token → bootstraptoken, jar-storage-path → jarstoragepath)
- Update all env vars to CAMELEER_SERVER_* mechanical mapping
- Fix container-cpu-request/container-cpu-shares mismatch bug
- Remove displayName from AgentRegistrationRequest (redundant with instanceId)
- Update agent container env vars to CAMELEER_AGENT_* convention
- Update K8s manifests and CI workflow for new env var names
- Update CLAUDE.md, HOWTO.md, SERVER-CAPABILITIES.md documentation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 18:10:51 +02:00
`OIDCJWKSETURI` is needed when the public issuer URL isn't reachable from inside containers — it fetches JWKS directly from the internal Logto service. `OIDCTLSSKIPVERIFY` disables certificate verification for all OIDC HTTP calls (discovery, token exchange, JWKS); use only when the provider has a self-signed CA.
2026-03-14 12:45:02 +01:00
2026-04-06 01:45:45 +02:00
### SSO Behavior
When OIDC is configured and enabled, the UI automatically redirects to the OIDC provider for silent SSO (`prompt=none` ). Users with an active provider session are signed in without seeing a login form. On first login, the provider may show a consent screen (scopes), after which subsequent logins are seamless. If auto-signup is enabled, new users are automatically provisioned with the configured default roles.
- **Bypass SSO**: Navigate to `/login?local` to see the local login form
- **Subpath deployments**: The OIDC redirect_uri respects `BASE_PATH` (e.g., `https://host/server/oidc/callback` )
2026-04-06 10:11:49 +02:00
- **Role sync**: System roles (ADMIN/OPERATOR/VIEWER) are synced from OIDC scopes on every login — revoking a scope in the provider takes effect on next login. Manually assigned group memberships are preserved.
2026-04-06 01:45:45 +02:00
2026-03-14 12:41:41 +01:00
### User Management (ADMIN only)
```bash
# List all users
curl -s -H "Authorization: Bearer $TOKEN" http://localhost:8081/api/v1/admin/users
# Update user roles
curl -s -X PUT http://localhost:8081/api/v1/admin/users/{userId}/roles \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d '{"roles":["VIEWER","OPERATOR"]}'
# Delete user
curl -s -X DELETE http://localhost:8081/api/v1/admin/users/{userId} \
-H "Authorization: Bearer $TOKEN"
```
2026-03-11 13:51:10 +01:00
### Ingestion (POST, returns 202 Accepted)
```bash
2026-03-11 21:09:59 +01:00
# Post route execution data (JWT required)
2026-03-11 13:51:10 +01:00
curl -s -X POST http://localhost:8081/api/v1/data/executions \
-H "Content-Type: application/json" \
-H "X-Protocol-Version: 1" \
2026-03-11 21:09:59 +01:00
-H "Authorization: Bearer $TOKEN" \
2026-03-11 13:51:10 +01:00
-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
curl -s -X POST http://localhost:8081/api/v1/data/diagrams \
-H "Content-Type: application/json" \
-H "X-Protocol-Version: 1" \
2026-03-11 21:09:59 +01:00
-H "Authorization: Bearer $TOKEN" \
2026-03-11 13:51:10 +01:00
-d '{"agentId":"agent-1","routeId":"route-1","version":1,"nodes":[],"edges":[]}'
# Post agent metrics
curl -s -X POST http://localhost:8081/api/v1/data/metrics \
-H "Content-Type: application/json" \
-H "X-Protocol-Version: 1" \
2026-03-11 21:09:59 +01:00
-H "Authorization: Bearer $TOKEN" \
2026-03-11 13:51:10 +01:00
-d '[{"agentId":"agent-1","metricName":"cpu","value":42.0,"timestamp":"2026-03-11T00:00:00Z","tags":{}}]'
2026-03-25 11:53:27 +01:00
2026-04-12 10:33:03 +02:00
# Post application log entries (raw JSON array — no wrapper)
2026-03-25 11:53:27 +01:00
curl -s -X POST http://localhost:8081/api/v1/data/logs \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
2026-04-12 10:33:03 +02:00
-d '[{
"timestamp": "2026-03-25T10:00:00Z",
"level": "INFO",
"loggerName": "com.acme.MyService",
"message": "Processing order #12345 ",
"threadName": "main",
"source": "app"
}]'
2026-03-11 13:51:10 +01:00
```
**Note:** The `X-Protocol-Version: 1` header is required on all `/api/v1/data/**` endpoints. Missing or wrong version returns 400.
### Health & Docs
```bash
# Health check
curl -s http://localhost:8081/api/v1/health
# OpenAPI JSON
curl -s http://localhost:8081/api/v1/api-docs
# Swagger UI
open http://localhost:8081/api/v1/swagger-ui.html
```
2026-03-11 17:44:56 +01:00
### Search (Phase 2)
```bash
# Search by status (GET with basic filters)
2026-03-11 21:09:59 +01:00
curl -s -H "Authorization: Bearer $TOKEN" \
"http://localhost:8081/api/v1/search/executions?status=COMPLETED&limit=10"
2026-03-11 17:44:56 +01:00
# Search by time range
2026-03-11 21:09:59 +01:00
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"
2026-03-11 17:44:56 +01:00
# Advanced search (POST with full-text)
curl -s -X POST http://localhost:8081/api/v1/search/executions \
-H "Content-Type: application/json" \
2026-03-11 21:09:59 +01:00
-H "Authorization: Bearer $TOKEN" \
2026-03-11 17:44:56 +01:00
-d '{"status":"FAILED","text":"NullPointerException","limit":20}'
# Transaction detail (nested processor tree)
2026-03-11 21:09:59 +01:00
curl -s -H "Authorization: Bearer $TOKEN" \
http://localhost:8081/api/v1/executions/{executionId}
2026-03-11 17:44:56 +01:00
# Processor exchange snapshot
2026-03-11 21:09:59 +01:00
curl -s -H "Authorization: Bearer $TOKEN" \
http://localhost:8081/api/v1/executions/{executionId}/processors/{index}/snapshot
2026-03-11 17:44:56 +01:00
# Render diagram as SVG
2026-03-11 21:09:59 +01:00
curl -s -H "Authorization: Bearer $TOKEN" \
-H "Accept: image/svg+xml" \
http://localhost:8081/api/v1/diagrams/{contentHash}/render
2026-03-11 17:44:56 +01:00
# Render diagram as JSON layout
2026-03-11 21:09:59 +01:00
curl -s -H "Authorization: Bearer $TOKEN" \
-H "Accept: application/json" \
http://localhost:8081/api/v1/diagrams/{contentHash}/render
2026-03-11 17:44:56 +01:00
```
**Search response format:** `{ "data": [...], "total": N, "offset": 0, "limit": 50 }`
**Supported search filters (GET):** `status` , `timeFrom` , `timeTo` , `correlationId` , `limit` , `offset`
**Additional POST filters:** `durationMin` , `durationMax` , `text` (global full-text), `textInBody` , `textInHeaders` , `textInErrors`
2026-03-11 19:22:06 +01:00
### Agent Registry & SSE (Phase 3)
```bash
2026-03-11 21:09:59 +01:00
# Register an agent (uses bootstrap token, not JWT — see Authentication section above)
2026-03-11 19:22:06 +01:00
curl -s -X POST http://localhost:8081/api/v1/agents/register \
-H "Content-Type: application/json" \
2026-03-11 21:09:59 +01:00
-H "Authorization: Bearer my-secret-token" \
2026-03-11 19:22:06 +01:00
-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)
2026-03-11 21:09:59 +01:00
curl -s -X POST http://localhost:8081/api/v1/agents/agent-1/heartbeat \
-H "Authorization: Bearer $TOKEN"
2026-03-11 19:22:06 +01:00
# List agents (optionally filter by status)
2026-03-11 21:09:59 +01:00
curl -s -H "Authorization: Bearer $TOKEN" "http://localhost:8081/api/v1/agents"
curl -s -H "Authorization: Bearer $TOKEN" "http://localhost:8081/api/v1/agents?status=LIVE"
2026-03-11 19:22:06 +01:00
2026-03-11 21:09:59 +01:00
# Connect to SSE event stream (JWT via query parameter)
curl -s -N "http://localhost:8081/api/v1/agents/agent-1/events?token=$TOKEN"
2026-03-11 19:22:06 +01:00
# Send command to single agent
curl -s -X POST http://localhost:8081/api/v1/agents/agent-1/commands \
-H "Content-Type: application/json" \
2026-03-11 21:09:59 +01:00
-H "Authorization: Bearer $TOKEN" \
2026-03-11 19:22:06 +01:00
-d '{"type":"config-update","payload":{"samplingRate":0.5}}'
# Send command to agent group
curl -s -X POST http://localhost:8081/api/v1/agents/groups/order-service-prod/commands \
-H "Content-Type: application/json" \
2026-03-11 21:09:59 +01:00
-H "Authorization: Bearer $TOKEN" \
2026-03-11 19:22:06 +01:00
-d '{"type":"deep-trace","payload":{"routeId":"route-1","durationSeconds":60}}'
2026-03-30 21:44:12 +02:00
# Send route control command to agent group (start/stop/suspend/resume)
curl -s -X POST http://localhost:8081/api/v1/agents/groups/order-service-prod/commands \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d '{"type":"route-control","payload":{"routeId":"route-1","action":"stop","nonce":"unique-uuid"}}'
2026-03-11 19:22:06 +01:00
# Broadcast command to all live agents
curl -s -X POST http://localhost:8081/api/v1/agents/commands \
-H "Content-Type: application/json" \
2026-03-11 21:09:59 +01:00
-H "Authorization: Bearer $TOKEN" \
2026-03-11 19:22:06 +01:00
-d '{"type":"config-update","payload":{"samplingRate":1.0}}'
# Acknowledge command delivery
2026-03-11 21:09:59 +01:00
curl -s -X POST http://localhost:8081/api/v1/agents/agent-1/commands/{commandId}/ack \
-H "Authorization: Bearer $TOKEN"
2026-03-11 19:22:06 +01:00
```
**Agent lifecycle:** LIVE (heartbeat within 90s) → STALE (missed 3 heartbeats) → DEAD (5min after STALE). DEAD agents kept indefinitely.
2026-04-03 11:22:44 +02:00
**Server restart resilience:** The agent registry is in-memory and lost on server restart. Agents auto-re-register on their next heartbeat or SSE connection — the server reconstructs registry entries from JWT claims (subject, application). Route catalog uses ClickHouse execution data as fallback until agents re-register with full route IDs. Agents should also handle 404 on heartbeat by triggering a full re-registration.
2026-03-30 21:44:12 +02:00
**SSE events:** `config-update` , `deep-trace` , `replay` , `route-control` commands pushed in real time. Server sends ping keepalive every 15s.
2026-03-11 19:22:06 +01:00
**Command expiry:** Unacknowledged commands expire after 60 seconds.
2026-04-03 11:22:44 +02:00
**Route control responses:** Route control commands return `CommandGroupResponse` with per-agent status, response count, and timed-out agent IDs.
2026-03-11 13:51:10 +01:00
### Backpressure
When the write buffer is full (default capacity: 50,000), ingestion endpoints return **503 Service Unavailable ** . Already-buffered data is not lost.
## Configuration
2026-04-15 15:28:42 +02:00
Key settings in `cameleer-server-app/src/main/resources/application.yml` . All custom properties live under `cameleer.server.*` . Env vars are a mechanical 1:1 mapping (dots to underscores, uppercase).
2026-04-11 21:56:19 +02:00
**Security** (`cameleer.server.security.*` ):
| Setting | Default | Env var | Description |
|---------|---------|---------|-------------|
| `cameleer.server.security.bootstraptoken` | * (required) * | `CAMELEER_SERVER_SECURITY_BOOTSTRAPTOKEN` | Bootstrap token for agent registration |
| `cameleer.server.security.bootstraptokenprevious` | * (empty) * | `CAMELEER_SERVER_SECURITY_BOOTSTRAPTOKENPREVIOUS` | Previous bootstrap token for rotation |
| `cameleer.server.security.uiuser` | `admin` | `CAMELEER_SERVER_SECURITY_UIUSER` | UI login username |
| `cameleer.server.security.uipassword` | `admin` | `CAMELEER_SERVER_SECURITY_UIPASSWORD` | UI login password |
| `cameleer.server.security.uiorigin` | `http://localhost:5173` | `CAMELEER_SERVER_SECURITY_UIORIGIN` | CORS allowed origin for UI |
| `cameleer.server.security.corsallowedorigins` | * (empty) * | `CAMELEER_SERVER_SECURITY_CORSALLOWEDORIGINS` | Comma-separated CORS origins — overrides `uiorigin` when set |
| `cameleer.server.security.jwtsecret` | * (random) * | `CAMELEER_SERVER_SECURITY_JWTSECRET` | HMAC secret for JWT signing. If set, tokens survive restarts |
| `cameleer.server.security.accesstokenexpiryms` | `3600000` | `CAMELEER_SERVER_SECURITY_ACCESSTOKENEXPIRYMS` | JWT access token lifetime (1h) |
| `cameleer.server.security.refreshtokenexpiryms` | `604800000` | `CAMELEER_SERVER_SECURITY_REFRESHTOKENEXPIRYMS` | Refresh token lifetime (7d) |
2026-04-11 23:19:58 +02:00
| `cameleer.server.security.infrastructureendpoints` | `true` | `CAMELEER_SERVER_SECURITY_INFRASTRUCTUREENDPOINTS` | Show DB/ClickHouse admin endpoints. Set `false` in SaaS-managed mode |
2026-04-11 21:56:19 +02:00
**OIDC resource server** (`cameleer.server.security.oidc.*` ):
| Setting | Default | Env var | Description |
|---------|---------|---------|-------------|
| `cameleer.server.security.oidc.issueruri` | * (empty) * | `CAMELEER_SERVER_SECURITY_OIDC_ISSUERURI` | OIDC issuer URI — enables resource server mode |
| `cameleer.server.security.oidc.jwkseturi` | * (empty) * | `CAMELEER_SERVER_SECURITY_OIDC_JWKSETURI` | Direct JWKS URL — bypasses OIDC discovery |
| `cameleer.server.security.oidc.audience` | * (empty) * | `CAMELEER_SERVER_SECURITY_OIDC_AUDIENCE` | Expected JWT audience |
| `cameleer.server.security.oidc.tlsskipverify` | `false` | `CAMELEER_SERVER_SECURITY_OIDC_TLSSKIPVERIFY` | Skip TLS cert verification for OIDC calls |
**Note:** OIDC * login * configuration (issuer, client ID, client secret, roles claim, default roles) is stored in the database and managed via the admin API (`PUT /api/v1/admin/oidc` ) or admin UI. The env vars above are for resource server mode (M2M token validation) only.
**Ingestion** (`cameleer.server.ingestion.*` ):
| Setting | Default | Env var | Description |
|---------|---------|---------|-------------|
| `cameleer.server.ingestion.buffercapacity` | `50000` | `CAMELEER_SERVER_INGESTION_BUFFERCAPACITY` | Max items in write buffer |
| `cameleer.server.ingestion.batchsize` | `5000` | `CAMELEER_SERVER_INGESTION_BATCHSIZE` | Items per batch insert |
| `cameleer.server.ingestion.flushintervalms` | `5000` | `CAMELEER_SERVER_INGESTION_FLUSHINTERVALMS` | Buffer flush interval (ms) |
| `cameleer.server.ingestion.bodysizelimit` | `16384` | `CAMELEER_SERVER_INGESTION_BODYSIZELIMIT` | Max body size per execution (bytes) |
**Agent registry** (`cameleer.server.agentregistry.*` ):
| Setting | Default | Env var | Description |
|---------|---------|---------|-------------|
| `cameleer.server.agentregistry.heartbeatintervalms` | `30000` | `CAMELEER_SERVER_AGENTREGISTRY_HEARTBEATINTERVALMS` | Expected heartbeat interval (ms) |
| `cameleer.server.agentregistry.stalethresholdms` | `90000` | `CAMELEER_SERVER_AGENTREGISTRY_STALETHRESHOLDMS` | Time before agent marked STALE (ms) |
| `cameleer.server.agentregistry.deadthresholdms` | `300000` | `CAMELEER_SERVER_AGENTREGISTRY_DEADTHRESHOLDMS` | Time after STALE before DEAD (ms) |
| `cameleer.server.agentregistry.pingintervalms` | `15000` | `CAMELEER_SERVER_AGENTREGISTRY_PINGINTERVALMS` | SSE ping keepalive interval (ms) |
| `cameleer.server.agentregistry.commandexpiryms` | `60000` | `CAMELEER_SERVER_AGENTREGISTRY_COMMANDEXPIRYMS` | Pending command TTL (ms) |
| `cameleer.server.agentregistry.lifecyclecheckintervalms` | `10000` | `CAMELEER_SERVER_AGENTREGISTRY_LIFECYCLECHECKINTERVALMS` | Lifecycle monitor interval (ms) |
**Runtime** (`cameleer.server.runtime.*` ):
| Setting | Default | Env var | Description |
|---------|---------|---------|-------------|
| `cameleer.server.runtime.enabled` | `true` | `CAMELEER_SERVER_RUNTIME_ENABLED` | Enable Docker orchestration |
| `cameleer.server.runtime.baseimage` | `cameleer-runtime-base:latest` | `CAMELEER_SERVER_RUNTIME_BASEIMAGE` | Base Docker image for app containers |
| `cameleer.server.runtime.dockernetwork` | `cameleer` | `CAMELEER_SERVER_RUNTIME_DOCKERNETWORK` | Primary Docker network |
| `cameleer.server.runtime.jarstoragepath` | `/data/jars` | `CAMELEER_SERVER_RUNTIME_JARSTORAGEPATH` | JAR file storage directory |
| `cameleer.server.runtime.jardockervolume` | * (empty) * | `CAMELEER_SERVER_RUNTIME_JARDOCKERVOLUME` | Docker volume for JAR sharing |
| `cameleer.server.runtime.routingmode` | `path` | `CAMELEER_SERVER_RUNTIME_ROUTINGMODE` | `path` or `subdomain` Traefik routing |
| `cameleer.server.runtime.routingdomain` | `localhost` | `CAMELEER_SERVER_RUNTIME_ROUTINGDOMAIN` | Domain for Traefik routing labels |
| `cameleer.server.runtime.serverurl` | * (empty) * | `CAMELEER_SERVER_RUNTIME_SERVERURL` | Server URL injected into app containers |
2026-04-23 18:22:27 +02:00
| `cameleer.server.runtime.certresolver` | * (empty) * | `CAMELEER_SERVER_RUNTIME_CERTRESOLVER` | Traefik TLS cert resolver name (e.g. `letsencrypt` ). Blank = omit the `tls.certresolver` label and let Traefik serve the default TLS-store cert |
2026-04-11 21:56:19 +02:00
| `cameleer.server.runtime.agenthealthport` | `9464` | `CAMELEER_SERVER_RUNTIME_AGENTHEALTHPORT` | Agent health check port |
| `cameleer.server.runtime.healthchecktimeout` | `60` | `CAMELEER_SERVER_RUNTIME_HEALTHCHECKTIMEOUT` | Health check timeout (seconds) |
| `cameleer.server.runtime.container.memorylimit` | `512m` | `CAMELEER_SERVER_RUNTIME_CONTAINER_MEMORYLIMIT` | Default memory limit for app containers |
| `cameleer.server.runtime.container.cpushares` | `512` | `CAMELEER_SERVER_RUNTIME_CONTAINER_CPUSHARES` | Default CPU shares for app containers |
**Other** (`cameleer.server.*` ):
| Setting | Default | Env var | Description |
|---------|---------|---------|-------------|
2026-04-12 16:37:46 +02:00
| `cameleer.server.catalog.discoveryttldays` | `7` | `CAMELEER_SERVER_CATALOG_DISCOVERYTTLDAYS` | Days before stale discovered apps auto-hide from sidebar |
2026-04-11 21:56:19 +02:00
| `cameleer.server.tenant.id` | `default` | `CAMELEER_SERVER_TENANT_ID` | Tenant identifier |
| `cameleer.server.indexer.debouncems` | `2000` | `CAMELEER_SERVER_INDEXER_DEBOUNCEMS` | Search indexer debounce delay (ms) |
| `cameleer.server.indexer.queuesize` | `10000` | `CAMELEER_SERVER_INDEXER_QUEUESIZE` | Search indexer queue capacity |
| `cameleer.server.license.token` | * (empty) * | `CAMELEER_SERVER_LICENSE_TOKEN` | License token |
| `cameleer.server.license.publickey` | * (empty) * | `CAMELEER_SERVER_LICENSE_PUBLICKEY` | License verification public key |
| `cameleer.server.clickhouse.url` | `jdbc:clickhouse://localhost:8123/cameleer` | `CAMELEER_SERVER_CLICKHOUSE_URL` | ClickHouse JDBC URL |
| `cameleer.server.clickhouse.username` | `default` | `CAMELEER_SERVER_CLICKHOUSE_USERNAME` | ClickHouse user |
| `cameleer.server.clickhouse.password` | * (empty) * | `CAMELEER_SERVER_CLICKHOUSE_PASSWORD` | ClickHouse password |
Add React UI with Execution Explorer, auth, and standalone deployment
- Scaffold Vite + React + TypeScript frontend in ui/ with full design
system (dark/light themes) matching the HTML mockups
- Implement Execution Explorer page: search filters, results table with
expandable processor tree and exchange detail sidebar, pagination
- Add UI authentication: UiAuthController (login/refresh endpoints),
JWT filter handles ui: subject prefix, CORS configuration
- Shared components: StatusPill, DurationBar, StatCard, AppBadge,
FilterChip, Pagination — all using CSS Modules with design tokens
- API client layer: openapi-fetch with auth middleware, TanStack Query
hooks for search/detail/snapshot queries, Zustand for state
- Standalone deployment: Nginx Dockerfile, K8s Deployment + ConfigMap +
NodePort (30080), runtime config.js for API base URL
- Embedded mode: maven-resources-plugin copies ui/dist into JAR static
resources, SPA forward controller for client-side routing
- CI/CD: UI build step, Docker build/push for server-ui image, K8s
deploy step for UI, UI credential secrets
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 13:59:22 +01:00
## Web UI Development
```bash
cd ui
npm install
npm run dev # Vite dev server on http://localhost:5173 (proxies /api to :8081)
npm run build # Production build to ui/dist/
```
Migrate config to cameleer.server.* naming convention
Move all configuration properties under the cameleer.server.* namespace
with all-lowercase dot-separated names and mechanical env var mapping
(dots→underscores, uppercase). This aligns with the agent's convention
(cameleer.agent.*) and establishes a predictable pattern across all
components.
Changes:
- Move 6 config prefixes under cameleer.server.*: agent-registry,
ingestion, security, license, clickhouse, and cameleer.tenant/runtime/indexer
- Rename all kebab-case properties to concatenated lowercase
(e.g., bootstrap-token → bootstraptoken, jar-storage-path → jarstoragepath)
- Update all env vars to CAMELEER_SERVER_* mechanical mapping
- Fix container-cpu-request/container-cpu-shares mismatch bug
- Remove displayName from AgentRegistrationRequest (redundant with instanceId)
- Update agent container env vars to CAMELEER_AGENT_* convention
- Update K8s manifests and CI workflow for new env var names
- Update CLAUDE.md, HOWTO.md, SERVER-CAPABILITIES.md documentation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 18:10:51 +02:00
Login with `admin` / `admin` (or whatever `CAMELEER_SERVER_SECURITY_UIUSER` / `CAMELEER_SERVER_SECURITY_UIPASSWORD` are set to).
Add React UI with Execution Explorer, auth, and standalone deployment
- Scaffold Vite + React + TypeScript frontend in ui/ with full design
system (dark/light themes) matching the HTML mockups
- Implement Execution Explorer page: search filters, results table with
expandable processor tree and exchange detail sidebar, pagination
- Add UI authentication: UiAuthController (login/refresh endpoints),
JWT filter handles ui: subject prefix, CORS configuration
- Shared components: StatusPill, DurationBar, StatCard, AppBadge,
FilterChip, Pagination — all using CSS Modules with design tokens
- API client layer: openapi-fetch with auth middleware, TanStack Query
hooks for search/detail/snapshot queries, Zustand for state
- Standalone deployment: Nginx Dockerfile, K8s Deployment + ConfigMap +
NodePort (30080), runtime config.js for API base URL
- Embedded mode: maven-resources-plugin copies ui/dist into JAR static
resources, SPA forward controller for client-side routing
- CI/CD: UI build step, Docker build/push for server-ui image, K8s
deploy step for UI, UI credential secrets
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 13:59:22 +01:00
The UI uses runtime configuration via `public/config.js` . In Kubernetes, a ConfigMap overrides this file to set the correct API base URL.
### Regenerate API Types
When the backend OpenAPI spec changes:
```bash
cd ui
npm run generate-api # Requires backend running on :8081
```
2026-03-11 13:51:10 +01:00
## Running Tests
feat: remove OpenSearch, add ClickHouse admin page
Remove all OpenSearch code, dependencies, configuration, deployment
manifests, and CI/CD references. Replace the OpenSearch admin page
with a ClickHouse admin page showing cluster status, table sizes,
performance metrics, and indexer pipeline stats.
- Delete 11 OpenSearch Java files (config, search impl, admin controller, DTOs, tests)
- Delete 3 OpenSearch frontend files (admin page, CSS, query hooks)
- Delete deploy/opensearch.yaml K8s manifest
- Remove opensearch Maven dependencies from pom.xml
- Remove opensearch config from application.yml, Dockerfile, docker-compose
- Remove opensearch from CI workflow (secrets, deploy, cleanup steps)
- Simplify ThresholdConfig (remove OpenSearch thresholds, database-only)
- Change default search backend from opensearch to clickhouse
- Add ClickHouseAdminController with /status, /tables, /performance, /pipeline
- Add ClickHouseAdminPage with StatCards, pipeline ProgressBar, tables DataTable
- Update CLAUDE.md, HOWTO.md, and source comments
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 18:56:06 +02:00
Integration tests use Testcontainers (starts PostgreSQL automatically — requires Docker):
2026-03-11 13:51:10 +01:00
```bash
# All tests
mvn verify
# Unit tests only (no Docker needed)
2026-04-15 15:28:42 +02:00
mvn test -pl cameleer-server-core
2026-03-11 13:51:10 +01:00
# Specific integration test
2026-04-15 15:28:42 +02:00
mvn test -pl cameleer-server-app -Dtest=ExecutionControllerIT
2026-03-11 13:51:10 +01:00
```
2026-03-17 00:26:50 +01:00
## Verify Database Data
2026-03-11 13:51:10 +01:00
After posting data and waiting for the flush interval (1s default):
```bash
2026-04-15 15:28:42 +02:00
docker exec -it cameleer-server-postgres-1 psql -U cameleer -d cameleer \
2026-03-17 00:26:50 +01:00
-c "SELECT count(*) FROM route_executions"
2026-03-11 13:51:10 +01:00
```
2026-03-12 19:14:08 +01:00
## Kubernetes Deployment
The full stack is deployed to k3s via CI/CD on push to `main` . K8s manifests are in `deploy/` .
### Architecture
```
cameleer namespace:
2026-04-13 22:51:08 +02:00
PostgreSQL (StatefulSet, 10Gi PVC) ← cameleer-postgres:5432 (ClusterIP)
ClickHouse (StatefulSet, 10Gi PVC) ← cameleer-clickhouse:8123 (ClusterIP)
2026-04-15 15:28:42 +02:00
cameleer-server (Deployment) ← NodePort 30081
cameleer-ui (Deployment, Nginx) ← NodePort 30090
2026-04-03 11:22:44 +02:00
cameleer-deploy-demo (Deployment) ← NodePort 30092
2026-04-05 13:15:09 +02:00
Logto Server (Deployment) ← NodePort 30951/30952
Logto PostgreSQL (StatefulSet, 1Gi) ← ClusterIP
2026-04-03 11:22:44 +02:00
cameleer-demo namespace:
(deployed Camel applications — managed by cameleer-deploy-demo)
2026-03-12 19:14:08 +01:00
```
### Access (from your network)
| Service | URL |
|---------|-----|
2026-03-13 14:11:50 +01:00
| Web UI | `http://192.168.50.86:30090` |
2026-03-12 19:14:08 +01:00
| Server API | `http://192.168.50.86:30081/api/v1/health` |
| Swagger UI | `http://192.168.50.86:30081/api/v1/swagger-ui.html` |
2026-04-03 11:22:44 +02:00
| Deploy Demo | `http://192.168.50.86:30092` |
2026-04-05 13:31:17 +02:00
| Logto API | `LOGTO_ENDPOINT` secret (NodePort 30951 direct, or behind reverse proxy) |
| Logto Admin | `LOGTO_ADMIN_ENDPOINT` secret (NodePort 30952 direct, or behind reverse proxy) |
2026-03-12 19:14:08 +01:00
### CI/CD Pipeline
Add React UI with Execution Explorer, auth, and standalone deployment
- Scaffold Vite + React + TypeScript frontend in ui/ with full design
system (dark/light themes) matching the HTML mockups
- Implement Execution Explorer page: search filters, results table with
expandable processor tree and exchange detail sidebar, pagination
- Add UI authentication: UiAuthController (login/refresh endpoints),
JWT filter handles ui: subject prefix, CORS configuration
- Shared components: StatusPill, DurationBar, StatCard, AppBadge,
FilterChip, Pagination — all using CSS Modules with design tokens
- API client layer: openapi-fetch with auth middleware, TanStack Query
hooks for search/detail/snapshot queries, Zustand for state
- Standalone deployment: Nginx Dockerfile, K8s Deployment + ConfigMap +
NodePort (30080), runtime config.js for API base URL
- Embedded mode: maven-resources-plugin copies ui/dist into JAR static
resources, SPA forward controller for client-side routing
- CI/CD: UI build step, Docker build/push for server-ui image, K8s
deploy step for UI, UI credential secrets
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 13:59:22 +01:00
Push to `main` triggers: **build ** (UI npm + Maven, unit tests) → **docker ** (buildx amd64 for server + UI, push to Gitea registry) → **deploy ** (kubectl apply + rolling update).
2026-03-12 19:14:08 +01:00
Migrate config to cameleer.server.* naming convention
Move all configuration properties under the cameleer.server.* namespace
with all-lowercase dot-separated names and mechanical env var mapping
(dots→underscores, uppercase). This aligns with the agent's convention
(cameleer.agent.*) and establishes a predictable pattern across all
components.
Changes:
- Move 6 config prefixes under cameleer.server.*: agent-registry,
ingestion, security, license, clickhouse, and cameleer.tenant/runtime/indexer
- Rename all kebab-case properties to concatenated lowercase
(e.g., bootstrap-token → bootstraptoken, jar-storage-path → jarstoragepath)
- Update all env vars to CAMELEER_SERVER_* mechanical mapping
- Fix container-cpu-request/container-cpu-shares mismatch bug
- Remove displayName from AgentRegistrationRequest (redundant with instanceId)
- Update agent container env vars to CAMELEER_AGENT_* convention
- Update K8s manifests and CI workflow for new env var names
- Update CLAUDE.md, HOWTO.md, SERVER-CAPABILITIES.md documentation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 18:10:51 +02:00
Required Gitea org secrets: `REGISTRY_TOKEN` , `KUBECONFIG_BASE64` , `CAMELEER_SERVER_SECURITY_BOOTSTRAPTOKEN` , `CAMELEER_SERVER_SECURITY_JWTSECRET` , `POSTGRES_USER` , `POSTGRES_PASSWORD` , `POSTGRES_DB` , `CLICKHOUSE_USER` , `CLICKHOUSE_PASSWORD` , `CAMELEER_SERVER_SECURITY_UIUSER` (optional), `CAMELEER_SERVER_SECURITY_UIPASSWORD` (optional), `LOGTO_PG_USER` , `LOGTO_PG_PASSWORD` , `LOGTO_ENDPOINT` (public-facing Logto URL, e.g., `https://auth.cameleer.my.domain` ), `LOGTO_ADMIN_ENDPOINT` (admin console URL), `CAMELEER_SERVER_SECURITY_OIDCISSUERURI` (optional, for resource server M2M token validation), `CAMELEER_SERVER_SECURITY_OIDCAUDIENCE` (optional, API resource indicator), `CAMELEER_SERVER_SECURITY_OIDCTLSSKIPVERIFY` (optional, skip TLS cert verification for self-signed CAs).
2026-03-12 19:14:08 +01:00
### Manual K8s Commands
```bash
# Check pod status
kubectl -n cameleer get pods
# View server logs
2026-04-15 15:28:42 +02:00
kubectl -n cameleer logs -f deploy/cameleer-server
2026-03-12 19:14:08 +01:00
2026-03-17 00:26:50 +01:00
# View PostgreSQL logs
2026-04-13 22:51:08 +02:00
kubectl -n cameleer logs -f statefulset/cameleer-postgres
2026-03-17 00:26:50 +01:00
feat: remove OpenSearch, add ClickHouse admin page
Remove all OpenSearch code, dependencies, configuration, deployment
manifests, and CI/CD references. Replace the OpenSearch admin page
with a ClickHouse admin page showing cluster status, table sizes,
performance metrics, and indexer pipeline stats.
- Delete 11 OpenSearch Java files (config, search impl, admin controller, DTOs, tests)
- Delete 3 OpenSearch frontend files (admin page, CSS, query hooks)
- Delete deploy/opensearch.yaml K8s manifest
- Remove opensearch Maven dependencies from pom.xml
- Remove opensearch config from application.yml, Dockerfile, docker-compose
- Remove opensearch from CI workflow (secrets, deploy, cleanup steps)
- Simplify ThresholdConfig (remove OpenSearch thresholds, database-only)
- Change default search backend from opensearch to clickhouse
- Add ClickHouseAdminController with /status, /tables, /performance, /pipeline
- Add ClickHouseAdminPage with StatCards, pipeline ProgressBar, tables DataTable
- Update CLAUDE.md, HOWTO.md, and source comments
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 18:56:06 +02:00
# View ClickHouse logs
2026-04-13 22:51:08 +02:00
kubectl -n cameleer logs -f statefulset/cameleer-clickhouse
2026-03-12 19:14:08 +01:00
# Restart server
2026-04-15 15:28:42 +02:00
kubectl -n cameleer rollout restart deployment/cameleer-server
2026-03-12 19:14:08 +01:00
```