Files
cameleer-server/CLAUDE.md
hsiegeln 6288084daf
All checks were successful
CI / build (push) Successful in 2m9s
CI / cleanup-branch (push) Has been skipped
CI / docker (push) Successful in 1m41s
CI / deploy (push) Successful in 56s
CI / deploy-feature (push) Has been skipped
docs: update documentation for Docker orchestration and env var rename
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-08 22:09:18 +02:00

12 KiB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Project

Cameleer3 Server — observability server that receives, stores, and serves Camel route execution data and route diagrams from Cameleer3 agents. Pushes config and commands to agents via SSE.

  • cameleer3 (https://gitea.siegeln.net/cameleer/cameleer3) — the Java agent that instruments Camel applications
  • Protocol defined in cameleer3-common/PROTOCOL.md in the agent repo
  • This server depends on com.cameleer3:cameleer3-common (shared models and graph API)

Modules

  • cameleer3-server-core — domain logic, storage, agent registry
  • cameleer3-server-app — Spring Boot web app, REST controllers, SSE, static resources

Build Commands

mvn clean compile          # Compile all modules
mvn clean verify           # Full build with tests

Run

java -jar cameleer3-server-app/target/cameleer3-server-app-1.0-SNAPSHOT.jar

Key Conventions

  • Java 17+ required
  • Spring Boot 3.4.3 parent POM
  • Depends on com.cameleer3:cameleer3-common from Gitea Maven registry
  • Jackson JavaTimeModule for Instant deserialization
  • Communication: receives HTTP POST data from agents (executions, diagrams, metrics, logs), serves SSE event streams for config push/commands (config-update, deep-trace, replay, route-control)
  • Maintains agent instance registry (in-memory) with states: LIVE → STALE → DEAD. Auto-heals from JWT env claim + heartbeat body on heartbeat/SSE after server restart (priority: heartbeat environmentId > JWT env claim > "default"). Capabilities and route states updated on every heartbeat (protocol v2). Route catalog falls back to ClickHouse stats for route discovery when registry has incomplete data.
  • Multi-tenancy: each server instance serves one tenant (configured via CAMELEER_TENANT_ID, default: "default"). Environments (dev/staging/prod) are first-class — agents send environmentId at registration and in heartbeats. JWT carries env claim for environment persistence across token refresh. PostgreSQL isolated via schema-per-tenant (?currentSchema=tenant_{id}). ClickHouse shared DB with tenant_id + environment columns, partitioned by (tenant_id, toYYYYMM(timestamp)).
  • Storage: PostgreSQL for RBAC, config, and audit; ClickHouse for all observability data (executions, search, logs, metrics, stats, diagrams). ClickHouse schema migrations in clickhouse/*.sql, run idempotently on startup by ClickHouseSchemaInitializer. Use IF NOT EXISTS for CREATE and ADD PROJECTION.
  • Logging: ClickHouse JDBC set to INFO (com.clickhouse), HTTP client to WARN (org.apache.hc.client5) in application.yml
  • Security: JWT auth with RBAC (AGENT/VIEWER/OPERATOR/ADMIN roles), Ed25519 config signing (key derived deterministically from JWT secret via HMAC-SHA256), bootstrap token for registration. CORS: CAMELEER_CORS_ALLOWED_ORIGINS (comma-separated) overrides CAMELEER_UI_ORIGIN for multi-origin setups (e.g., reverse proxy). UI role gating: Admin sidebar/routes hidden for non-ADMIN; diagram toolbar and route control hidden for VIEWER; Config is a main tab (/config all apps, /config/:appId single app with detail; sidebar clicks stay on config, route clicks resolve to parent app). Read-only for VIEWER, editable for OPERATOR+. Role helpers: useIsAdmin(), useCanControl() in auth-store.ts. Route guard: RequireAdmin in auth/RequireAdmin.tsx.
  • OIDC: Optional external identity provider support (token exchange pattern). Configured via admin API/UI, stored in database (server_config table). Configurable userIdClaim (default sub) determines which id_token claim is used as the user identifier. Resource server mode: accepts external access tokens (Logto M2M) via JWKS validation when CAMELEER_OIDC_ISSUER_URI is set. CAMELEER_OIDC_JWK_SET_URI overrides JWKS discovery for container networking. CAMELEER_OIDC_TLS_SKIP_VERIFY=true disables TLS cert verification for OIDC calls (self-signed CAs). Scope-based role mapping via SystemRole.normalizeScope() (case-insensitive, strips server: prefix): admin/server:admin → ADMIN, operator/server:operator → OPERATOR, viewer/server:viewer → VIEWER. SSO: when OIDC enabled, UI auto-redirects to provider with prompt=none for silent sign-in; falls back to /login?local on login_required, retries without prompt=none on consent_required. Logout always redirects to /login?local (via OIDC end_session or direct fallback) to prevent SSO re-login loops. Auto-signup provisions new OIDC users with default roles. System roles synced on every OIDC login via syncOidcRoles — always overwrites directly-assigned roles (falls back to defaultRoles when OIDC returns none); uses getDirectRolesForUser to avoid touching group-inherited roles. Group memberships are never touched. Supports ES384, ES256, RS256. Shared OIDC logic in OidcProviderHelper (discovery, JWK source, algorithm set).
  • OIDC role extraction: OidcTokenExchanger reads roles from the access_token first (JWT with at+jwt type, decoded by a separate processor), then falls back to id_token. OidcConfig includes audience (RFC 8707 resource indicator — included in both authorization request and token exchange POST body to trigger JWT access tokens) and additionalScopes (extra scopes for the SPA to request). The rolesClaim config points to the claim name in the token (e.g., "roles" for Custom JWT claims, "realm_access.roles" for Keycloak). All provider-specific configuration is external — no provider-specific code in the server.
  • User persistence: PostgreSQL users table, admin CRUD at /api/v1/admin/users
  • Usage analytics: ClickHouse usage_events table tracks authenticated UI requests, flushed every 5s

CI/CD & Deployment

  • CI workflow: .gitea/workflows/ci.yml — build → docker → deploy on push to main or feature branches
  • Build step skips integration tests (-DskipITs) — Testcontainers needs Docker daemon
  • Docker: multi-stage build (Dockerfile), $BUILDPLATFORM for native Maven on ARM64 runner, amd64 runtime
  • REGISTRY_TOKEN build arg required for cameleer3-common dependency resolution
  • Registry: gitea.siegeln.net/cameleer/cameleer3-server (container images)
  • K8s manifests in deploy/ — Kustomize base + overlays (main/feature), shared infra (PostgreSQL, ClickHouse, Logto) as top-level manifests
  • Deployment target: k3s at 192.168.50.86, namespace cameleer (main), cam-<slug> (feature branches)
  • Feature branches: isolated namespace, PG schema; Traefik Ingress at <slug>-api.cameleer.siegeln.net
  • Secrets managed in CI deploy step (idempotent --dry-run=client | kubectl apply): cameleer-auth, postgres-credentials, clickhouse-credentials
  • K8s probes: server uses /api/v1/health, PostgreSQL uses pg_isready -U "$POSTGRES_USER" (env var, not hardcoded)
  • K8s security: server and database pods run with securityContext.runAsNonRoot. UI (nginx) runs without securityContext (needs root for entrypoint setup).
  • Docker: server Dockerfile has no default credentials — all DB config comes from env vars at runtime
  • Docker build uses buildx registry cache + --provenance=false for Gitea compatibility
  • CI: branch slug sanitization extracted to .gitea/sanitize-branch.sh, sourced by docker and deploy-feature jobs

UI Structure

The UI has 4 main tabs: Exchanges, Dashboard, Runtime, Deployments.

  • Exchanges — route execution search and detail
  • Dashboard — metrics and stats
  • Runtime — live agent status, logs (previously separate Logs tab merged here)
  • Deployments — deployment lifecycle. Config sub-tabs: Variables | Monitoring | Traces & Taps | Route Recording | Resources (previously a separate Config tab, now merged into Deployments)
  • Create app — full page at /apps/new (not a modal)

UI Styling

  • Always use @cameleer/design-system CSS variables for colors (var(--amber), var(--error), var(--success), etc.) — never hardcode hex values. This applies to CSS modules, inline styles, and SVG fill/stroke attributes. SVG presentation attributes resolve var() correctly.
  • Brand assets: @cameleer/design-system/assets/ provides camel-logo.svg (currentColor), cameleer3-{16,32,48,192,512}.png, and cameleer3-logo.png. Copied to ui/public/ for use as favicon (favicon-16.png, favicon-32.png) and logo (camel-logo.svg — login dialog 36px, sidebar 28x24px).
  • Sidebar generates /exchanges/ paths directly (no legacy /apps/ redirects). basePath is centralized in ui/src/config.ts; router.tsx imports it instead of re-reading <base> tag.
  • Global user preferences (environment selection) use Zustand stores with localStorage persistence — never URL search params. URL params are for page-specific state only (e.g. ?text= search query). Switching environment resets all filters and remounts pages.

Docker Orchestration

When deployed via the cameleer-saas platform, this server orchestrates customer app containers using Docker. Key components:

  • ConfigMerger — merges config at three levels: global → environment → app. Lower levels override higher. Used to produce the final env var set for a deployment.
  • TraefikLabelBuilder — generates Traefik Docker labels for path-based or subdomain-based routing to each deployed container. Two routing strategies: path prefix (/app/{slug}) and subdomain ({slug}.domain).
  • DockerNetworkManager — manages two Docker network tiers:
    • cameleer-traefik — shared network that Traefik joins; all routable containers attach here
    • cameleer-env-{slug} — per-environment isolated network; containers in the same environment can reach each other and the cameleer3-server
  • DockerEventMonitor — listens to the Docker event stream to detect container exits, OOM kills, and health state changes. Updates deployment status in real time.
  • DeploymentProgress — tracks deploy stages and broadcasts progress events via SSE for the UI progress indicator.

Deployment Status Model

Deployments move through these statuses:

Status Meaning
PENDING Queued, not started
RUNNING Container healthy and serving
DEGRADED Container up but health check failing or some replicas down
STOPPING Graceful shutdown in progress
STOPPED Intentionally stopped
FAILED Terminal failure

Replica support: deployments can specify a replica count. DEGRADED is used when at least one but not all replicas are healthy.

Deploy stages (DeployStage): BUILD → PUSH → PULL → STOP_OLD → START → HEALTH_CHECK → DONE (or FAILED at any stage).

Blue/green strategy: when re-deploying, the new container is started and health-checked before the old one is stopped, minimising downtime.

JAR Management

  • Retention policy per environment: configurable maximum number of JAR versions to keep. Older JARs are deleted automatically.
  • Nightly cleanup job (Spring @Scheduled): purges JARs exceeding the retention limit and removes orphaned files not referenced by any app version.
  • Volume-based JAR mounting for Docker-in-Docker setups: set CAMELEER_JAR_DOCKER_VOLUME to the Docker volume name that contains the JAR storage directory. When set, the orchestrator mounts this volume into the container instead of bind-mounting the host path (required when the SaaS container itself runs inside Docker and the host path is not accessible from sibling containers).

nginx / Reverse Proxy

  • client_max_body_size 200m is required in the nginx config to allow JAR uploads up to 200 MB. Without this, large JAR uploads return 413.

Disabled Skills

  • Do NOT use any gsd:* skills in this project. This includes all /gsd: prefixed commands.