- Remove broken observe/dashboard Traefik routes (server accessed via /server only) - Remove unused acme volume - Add JWT audience claim validation (https://api.cameleer.local) in SecurityConfig - Secure bootstrap output file with chmod 600 - Add dev-only comments on TLS_SKIP_VERIFY and credential logging Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6.8 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Project
Cameleer SaaS — multi-tenant SaaS platform wrapping the Cameleer observability stack (Java agent + server) for Apache Camel applications. Customers get managed observability for their Camel integrations without running infrastructure.
Ecosystem
This repo is the SaaS layer on top of two proven components:
- cameleer3 (sibling repo) — Java agent using ByteBuddy for zero-code instrumentation of Camel apps. Captures route executions, processor traces, payloads, metrics, and route graph topology. Deploys as
-javaagentJAR. - cameleer3-server (sibling repo) — Spring Boot observability backend. Receives agent data via HTTP, pushes config/commands via SSE. PostgreSQL + OpenSearch storage. React SPA dashboard. JWT auth with Ed25519 config signing.
- cameleer-website — Marketing site (Astro 5)
- design-system — Shared React component library (
@cameleer/design-systemon Gitea npm registry)
Agent-server protocol is defined in cameleer3/cameleer3-common/PROTOCOL.md. The agent and server are mature, proven components — this repo wraps them with multi-tenancy, billing, and self-service onboarding.
Architecture Context
The existing cameleer3-server already has single-tenant auth (JWT, RBAC, bootstrap tokens, OIDC). The SaaS layer must:
- Add multi-tenancy (tenant isolation of agent data, diagrams, configs)
- Provide self-service signup, billing, and team management
- Generate per-tenant bootstrap tokens for agent registration
- Proxy or federate access to tenant-specific cameleer3-server instances
- Enforce usage quotas and metered billing
Routing (single-domain, path-based via Traefik)
All services on one hostname. Two env vars control everything: PUBLIC_HOST + PUBLIC_PROTOCOL.
| Path | Target | Notes |
|---|---|---|
/platform/* |
cameleer-saas:8080 | SPA + API (server.servlet.context-path: /platform) |
/server/* |
cameleer3-server-ui:80 | Server dashboard (strip-prefix + BASE_PATH=/server) |
/ |
redirect → /platform/ |
Via docker/traefik-dynamic.yml |
/* (catch-all) |
cameleer-logto:3001 (priority=1) | Custom sign-in UI, OIDC, interaction |
- SPA assets at
/_app/(ViteassetsDir: '_app') to avoid conflict with Logto's/assets/ - Logto
ENDPOINT=${PUBLIC_PROTOCOL}://${PUBLIC_HOST}(same domain, same origin) - TLS: self-signed cert init container (
traefik-certs) for dev, ACME for production - Root
/→/platform/redirect via Traefik file provider (docker/traefik-dynamic.yml) - LoginPage auto-redirects to Logto OIDC (no intermediate button)
Custom sign-in UI (ui/sign-in/)
Separate Vite+React SPA replacing Logto's default sign-in page. Visually matches cameleer3-server LoginPage.
- Built as custom Logto Docker image (
cameleer-logto):ui/sign-in/Dockerfile= node build stage +FROM ghcr.io/logto-io/logto:latest+ COPY dist over/etc/logto/packages/experience/dist/ - Uses
@cameleer/design-systemcomponents (Card, Input, Button, FormField, Alert) - Authenticates via Logto Experience API (4-step: init → verify password → identify → submit → redirect)
CUSTOM_UI_PATHenv var does NOT work for Logto OSS — must volume-mount or replace the experience dist directory- Favicon bundled in
ui/sign-in/public/favicon.svg(served by Logto, not SaaS)
Auth enforcement
- All API endpoints enforce OAuth2 scopes via
@PreAuthorize("hasAuthority('SCOPE_xxx')")annotations - Tenant isolation enforced by
TenantIsolationInterceptor(a singleHandlerInterceptoron/api/**that resolves JWT org_id to TenantContext and validates{tenantId},{environmentId},{appId}path variables; fail-closed, platform admins bypass) - 13 OAuth2 scopes on the Logto API resource (
https://api.cameleer.local): 10 platform scopes + 3 server scopes (server:admin,server:operator,server:viewer), served to the frontend fromGET /platform/api/config - Server scopes map to server RBAC roles via JWT
scopeclaim (server readsrolesClaim: "scope") - Org role
admingetsserver:admin, org rolemembergetsserver:viewer - Custom
JwtDecoderinSecurityConfig.java— ES384 algorithm,at+jwttoken type, split issuer-uri (string validation) / jwk-set-uri (Docker-internal fetch), audience validation (https://api.cameleer.local)
Server integration (cameleer3-server env vars)
| Env var | Value | Purpose |
|---|---|---|
CAMELEER_OIDC_ISSUER_URI |
${PUBLIC_PROTOCOL}://${PUBLIC_HOST}/oidc |
Token issuer claim validation |
CAMELEER_OIDC_JWK_SET_URI |
http://logto:3001/oidc/jwks |
Docker-internal JWK fetch |
CAMELEER_OIDC_TLS_SKIP_VERIFY |
true |
Skip cert verify for OIDC discovery (dev only — disable in production) |
CAMELEER_CORS_ALLOWED_ORIGINS |
${PUBLIC_PROTOCOL}://${PUBLIC_HOST} |
Allow browser requests through Traefik |
BASE_PATH (server-ui) |
/server |
React Router basename + <base> tag |
Bootstrap (docker/logto-bootstrap.sh)
Idempotent script run via logto-bootstrap init container. Phases:
- Wait for Logto + server health
- Get Management API token (reads
m-defaultsecret from DB) - Create Logto apps (SPA, Traditional with
skipConsent, M2M with Management API role) 3b. Create API resource scopes (10 platform + 3 server scopes) - Create roles (platform-admin, org admin/member with API resource scope assignments)
- Create users (SaaS admin with platform-admin role + Logto console access, tenant admin)
- Create organization, add users with org roles
- Configure cameleer3-server OIDC (
rolesClaim: "scope",audience,defaultRoles: ["VIEWER"]) - Configure Logto sign-in branding (Cameleer colors
#C6820E/#D4941E, logo from/platform/logo.svg) - Cleanup seeded Logto apps
- Write bootstrap results to
/data/logto-bootstrap.json
SaaS admin credentials (SAAS_ADMIN_USER/SAAS_ADMIN_PASS) work for both the SaaS platform and the Logto console (port 3002).
Related Conventions
- Gitea-hosted:
gitea.siegeln.net/cameleer/ - CI:
.gitea/workflows/— Gitea Actions - K8s target: k3s cluster at 192.168.50.86
- Docker images: CI builds and pushes all images — Dockerfiles use multi-stage builds, no local builds needed
cameleer-saas— SaaS app (frontend + JAR baked in)cameleer-logto— custom Logto with sign-in UI baked in
- Docker builds:
--no-cache,--provenance=falsefor Gitea compatibility docker-compose.dev.yml— exposes ports for direct access, setsSPRING_PROFILES_ACTIVE: dev. No volume mounts — all artifacts come from CI-built images- Design system: import from
@cameleer/design-system(Gitea npm registry)
Disabled Skills
- Do NOT use any
gsd:*skills in this project. This includes all/gsd:prefixed commands.