Backend holds client_secret and does the token exchange server-side, making PKCE redundant. Removes code_verifier/code_challenge from all frontend auth paths and backend exchange method. Eliminates the source of "grant request is invalid" errors from verifier mismatches. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
7.6 KiB
7.6 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.
Related Project
- cameleer3 (
https://gitea.siegeln.net/cameleer/cameleer3) — the Java agent that instruments Camel applications - Protocol defined in
cameleer3-common/PROTOCOL.mdin the agent repo - This server depends on
com.cameleer3:cameleer3-common(shared models and graph API)
Modules
cameleer3-server-core— domain logic, storage, agent registrycameleer3-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-commonfrom Gitea Maven registry - Jackson
JavaTimeModuleforInstantdeserialization - 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
envclaim + heartbeat body on heartbeat/SSE after server restart (priority: heartbeatenvironmentId> JWTenvclaim >"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 sendenvironmentIdat registration and in heartbeats. JWT carriesenvclaim for environment persistence across token refresh. PostgreSQL isolated via schema-per-tenant (?currentSchema=tenant_{id}). ClickHouse shared DB withtenant_id+environmentcolumns, 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 byClickHouseSchemaInitializer. UseIF NOT EXISTSfor 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) overridesCAMELEER_UI_ORIGINfor 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 (/configall apps,/config/:appIdsingle 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()inauth-store.ts. Route guard:RequireAdmininauth/RequireAdmin.tsx. - OIDC: Optional external identity provider support (token exchange pattern). Configured via admin API/UI, stored in database (
server_configtable). ConfigurableuserIdClaim(defaultsub) determines which id_token claim is used as the user identifier. Resource server mode: accepts external access tokens (Logto M2M) via JWKS validation whenCAMELEER_OIDC_ISSUER_URIis set.CAMELEER_OIDC_JWK_SET_URIoverrides JWKS discovery for container networking.CAMELEER_OIDC_TLS_SKIP_VERIFY=truedisables TLS cert verification for OIDC calls (self-signed CAs). Scope-based role mapping viaSystemRole.normalizeScope()(case-insensitive, stripsserver:prefix):admin/server:admin→ ADMIN,operator/server:operator→ OPERATOR,viewer/server:viewer→ VIEWER. SSO: when OIDC enabled, UI auto-redirects to provider withprompt=nonefor silent sign-in; falls back to/login?localonlogin_required, retries withoutprompt=noneonconsent_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 (revocations propagate on next login); group memberships are never touched. Supports ES384, ES256, RS256. Shared OIDC logic inOidcProviderHelper(discovery, JWK source, algorithm set). - User persistence: PostgreSQL
userstable, admin CRUD at/api/v1/admin/users - Usage analytics: ClickHouse
usage_eventstable 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),$BUILDPLATFORMfor native Maven on ARM64 runner, amd64 runtime REGISTRY_TOKENbuild arg required forcameleer3-commondependency 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 usespg_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=falsefor Gitea compatibility - CI: branch slug sanitization extracted to
.gitea/sanitize-branch.sh, sourced by docker and deploy-feature jobs
UI Styling
- Always use
@cameleer/design-systemCSS variables for colors (var(--amber),var(--error),var(--success), etc.) — never hardcode hex values. This applies to CSS modules, inline styles, and SVGfill/strokeattributes. SVG presentation attributes resolvevar()correctly. - Brand assets:
@cameleer/design-system/assets/providescamel-logo.svg(currentColor),cameleer3-{16,32,48,192,512}.png, andcameleer3-logo.png. Copied toui/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 inui/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.
Disabled Skills
- Do NOT use any
gsd:*skills in this project. This includes all/gsd:prefixed commands.