Files
cameleer-server/.claude/rules/app-classes.md
hsiegeln 51d7bda5b8 docs: document P3 URL taxonomy, slug immutability, tenant invariant
Locks the new conventions into rule files so future agents and humans
don't drift back into old patterns.

- .claude/rules/app-classes.md: replaces the flat endpoint catalog
  with a taxonomy-aware reorganization (env-scoped / env-admin /
  agent-only / ingestion / cross-env discovery / admin / other).
  Adds the flat-endpoint allow-list with rationale per prefix and
  documents the tenant-filter invariant for ClickHouse queries.
- CLAUDE.md: adds four convention bullets in Key Conventions —
  URL taxonomy with allow-list pointer, slug immutability rule,
  app uniqueness as (env, app_slug), env-required on env-scoped
  endpoints.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-16 23:50:38 +02:00

13 KiB

paths
paths
cameleer-server-app/**

App Module Key Classes

cameleer-server-app/src/main/java/com/cameleer/server/app/

URL taxonomy

User-facing data and config endpoints live under /api/v1/environments/{envSlug}/.... Env is a path segment, never a query param. The envSlug is resolved to an Environment bean via the @EnvPath argument resolver (web/EnvironmentPathResolver.java) — 404 on unknown slug.

Slugs are immutable after creation for both environments and apps. Slug regex: ^[a-z0-9][a-z0-9-]{0,63}$. Validated in EnvironmentService.create and AppService.createApp. Update endpoints (PUT) do not accept a slug field; Jackson drops it as an unknown property.

Flat-endpoint allow-list

These paths intentionally stay flat (no /environments/{envSlug} prefix). Every new endpoint should be env-scoped unless it appears here and the reason is documented.

Path prefix Why flat
/api/v1/data/** Agent ingestion. JWT env claim is authoritative; URL-embedded env would invite spoofing.
/api/v1/agents/register, /refresh, /{id}/heartbeat, /{id}/events (SSE), /{id}/deregister, /{id}/commands, /{id}/commands/{id}/ack, /{id}/replay Agent self-service; JWT-bound.
/api/v1/agents/commands, /api/v1/agents/groups/{group}/commands Operator fan-out; target scope is explicit in query params.
/api/v1/agents/config Agent-authoritative config read; JWT → registry → (app, env).
/api/v1/admin/{users,roles,groups,oidc,license,audit,rbac/stats,claim-mappings,thresholds,sensitive-keys,usage,clickhouse,database,environments} Truly cross-env admin. Env CRUD URLs use {envSlug}, not UUID.
/api/v1/catalog, /api/v1/catalog/{applicationId} Cross-env discovery is the purpose. Env is an optional filter via ?environment=.
/api/v1/executions/{execId}, /processors/** Exchange IDs are globally unique; permalinks.
/api/v1/diagrams/{contentHash}/render, POST /api/v1/diagrams/render Content-addressed or stateless.
/api/v1/auth/** Pre-auth; no env context exists.
/api/v1/health, /prometheus, /api-docs/**, /swagger-ui/** Server metadata.

Tenant isolation invariant

ClickHouse is shared across tenants. Every ClickHouse query must filter by tenant_id (from CAMELEER_SERVER_TENANT_ID env var, resolved via TenantContext/config) in addition to environment. New controllers added under /environments/{envSlug}/... must preserve this — the env filter from the path does not replace the tenant filter.

controller/ — REST endpoints

Env-scoped (user-facing data & config)

  • AppController/api/v1/environments/{envSlug}/apps. GET list / POST create / GET {appSlug} / DELETE {appSlug} / GET {appSlug}/versions / POST {appSlug}/versions (JAR upload) / PUT {appSlug}/container-config. App slug uniqueness is per-env ((env, app_slug) is the natural key). CreateAppRequest body has no env (path), validates slug regex.
  • DeploymentController/api/v1/environments/{envSlug}/apps/{appSlug}/deployments. GET list / POST create (body { appVersionId }) / POST {id}/stop / POST {id}/promote (body { targetEnvironment: slug } — target app slug must exist in target env) / GET {id}/logs.
  • ApplicationConfigController/api/v1/environments/{envSlug}. GET /config (list), GET/PUT /apps/{appSlug}/config, GET /apps/{appSlug}/processor-routes, POST /apps/{appSlug}/config/test-expression. PUT also pushes CONFIG_UPDATE to LIVE agents in this env.
  • AppSettingsController/api/v1/environments/{envSlug}. GET /app-settings (list), GET/PUT/DELETE /apps/{appSlug}/settings. ADMIN/OPERATOR only.
  • SearchController/api/v1/environments/{envSlug}. GET /executions, POST /executions/search, GET /stats, /stats/timeseries, /stats/timeseries/by-app, /stats/timeseries/by-route, /stats/punchcard, /attributes/keys, /errors/top.
  • LogQueryController — GET /api/v1/environments/{envSlug}/logs (filters: source, application, agentId, exchangeId, level, logger, q, time range).
  • RouteCatalogController — GET /api/v1/environments/{envSlug}/routes (merged route catalog from registry + ClickHouse; env filter unconditional).
  • RouteMetricsController — GET /api/v1/environments/{envSlug}/routes/metrics, GET /api/v1/environments/{envSlug}/routes/metrics/processors.
  • AgentListController — GET /api/v1/environments/{envSlug}/agents (registered agents with runtime metrics, filtered to env).
  • AgentEventsController — GET /api/v1/environments/{envSlug}/agents/events (lifecycle events).
  • AgentMetricsController — GET /api/v1/environments/{envSlug}/agents/{agentId}/metrics (JVM/Camel metrics). Rejects cross-env agents (404) as defence-in-depth.
  • DiagramRenderController — GET /api/v1/environments/{envSlug}/apps/{appSlug}/routes/{routeId}/diagram (env-scoped lookup). Also GET /api/v1/diagrams/{contentHash}/render (flat — content hashes are globally unique).

Env admin (env-slug-parameterized, not env-scoped data)

  • EnvironmentAdminController/api/v1/admin/environments. GET list / POST create / GET {envSlug} / PUT {envSlug} / DELETE {envSlug} / PUT {envSlug}/default-container-config / PUT {envSlug}/jar-retention. Slug immutable — PUT body has no slug field; any slug supplied is dropped by Jackson. Slug validated on POST.

Agent-only (JWT-authoritative, intentionally flat)

  • AgentRegistrationController — POST /register (requires environmentId in body; 400 if missing), POST /{id}/refresh (rejects tokens with no env claim), POST /{id}/heartbeat (env from body preferred, JWT fallback; 400 if neither), POST /{id}/deregister.
  • AgentSseController — GET /{id}/events (SSE connection).
  • AgentCommandController — POST /{agentId}/commands, POST /groups/{group}/commands, POST /commands (broadcast), POST /{agentId}/commands/{commandId}/ack, POST /{agentId}/replay.
  • AgentConfigController — GET /api/v1/agents/config. Agent-authoritative config read: resolves (app, env) from JWT subject → registry (registry miss falls back to JWT env claim; no registry entry → 404 since application can't be derived).

Ingestion (agent-only, JWT-authoritative)

  • LogIngestionController — POST /api/v1/data/logs (accepts List<LogEntry>; WARNs on missing identity, unregistered agents, empty payloads, buffer-full drops).
  • EventIngestionController — POST /api/v1/data/events.
  • ChunkIngestionController — POST /api/v1/ingestion/chunk/{executions|metrics|diagrams}.
  • ExecutionController — POST /api/v1/data/executions (legacy ingestion path when ClickHouse disabled).
  • MetricsController — POST /api/v1/data/metrics.
  • DiagramController — POST /api/v1/data/diagrams (resolves applicationId + environment from the agent registry keyed on JWT subject; stamps both on the stored TaggedDiagram).

Cross-env discovery (flat)

  • CatalogController — GET /api/v1/catalog (merges managed apps + in-memory agents + CH stats; optional ?environment= filter). DELETE /api/v1/catalog/{applicationId} (ADMIN: dismiss app, purge all CH data + PG record).

Admin (cross-env, flat)

  • UserAdminController — CRUD /api/v1/admin/users, POST /{id}/roles, POST /{id}/set-password.
  • RoleAdminController — CRUD /api/v1/admin/roles.
  • GroupAdminController — CRUD /api/v1/admin/groups.
  • OidcConfigAdminController — GET/POST /api/v1/admin/oidc, POST /test.
  • SensitiveKeysAdminController — GET/PUT /api/v1/admin/sensitive-keys. GET returns 200 or 204 if not configured. PUT accepts { keys: [...] } with optional ?pushToAgents=true. Fan-out iterates every distinct (application, environment) slice — intentional global baseline + per-env overrides.
  • ClaimMappingAdminController — CRUD /api/v1/admin/claim-mappings, POST /test.
  • LicenseAdminController — GET/POST /api/v1/admin/license.
  • ThresholdAdminController — CRUD /api/v1/admin/thresholds.
  • AuditLogController — GET /api/v1/admin/audit.
  • RbacStatsController — GET /api/v1/admin/rbac/stats.
  • UsageAnalyticsController — GET /api/v1/admin/usage (ClickHouse usage_events).
  • ClickHouseAdminController — GET /api/v1/admin/clickhouse/** (conditional on infrastructureendpoints flag).
  • DatabaseAdminController — GET /api/v1/admin/database/** (conditional on infrastructureendpoints flag).

Other (flat)

  • DetailController — GET /api/v1/executions/{executionId} + processor snapshot endpoints.
  • MetricsController — exposes /api/v1/metrics and /api/v1/prometheus (server-side Prometheus scrape endpoint).

runtime/ — Docker orchestration

  • DockerRuntimeOrchestrator — implements RuntimeOrchestrator; Docker Java client (zerodep transport), container lifecycle
  • DeploymentExecutor — @Async staged deploy: PRE_FLIGHT -> PULL_IMAGE -> CREATE_NETWORK -> START_REPLICAS -> HEALTH_CHECK -> SWAP_TRAFFIC -> COMPLETE. Container names are {tenantId}-{envSlug}-{appSlug}-{replicaIndex} (globally unique on Docker daemon). Sets per-replica CAMELEER_AGENT_INSTANCEID env var to {envSlug}-{appSlug}-{replicaIndex}.
  • DockerNetworkManager — ensures bridge networks (cameleer-traefik, cameleer-env-{slug}), connects containers
  • DockerEventMonitor — persistent Docker event stream listener (die, oom, start, stop), updates deployment status
  • TraefikLabelBuilder — generates Traefik Docker labels for path-based or subdomain routing. Also emits cameleer.replica and cameleer.instance-id labels per container for labels-first identity.
  • PrometheusLabelBuilder — generates Prometheus Docker labels (prometheus.scrape/path/port) per runtime type for docker_sd_configs auto-discovery
  • ContainerLogForwarder — streams Docker container stdout/stderr to ClickHouse with source='container'. One follow-stream thread per container, batches lines every 2s/50 lines via ClickHouseLogStore.insertBufferedBatch(). 60-second max capture timeout.
  • DisabledRuntimeOrchestrator — no-op when runtime not enabled

metrics/ — Prometheus observability

  • ServerMetrics — centralized business metrics: gauges (agents by state, SSE connections, buffer depths), counters (ingestion drops, agent transitions, deployment outcomes, auth failures), timers (flush duration, deployment duration). Exposed via /api/v1/prometheus.

storage/ — PostgreSQL repositories (JdbcTemplate)

  • PostgresAppRepository, PostgresAppVersionRepository, PostgresEnvironmentRepository
  • PostgresDeploymentRepository — includes JSONB replica_states, deploy_stage, findByContainerId
  • PostgresUserRepository, PostgresRoleRepository, PostgresGroupRepository
  • PostgresAuditRepository, PostgresOidcConfigRepository, PostgresClaimMappingRepository, PostgresSensitiveKeysRepository
  • PostgresAppSettingsRepository, PostgresApplicationConfigRepository, PostgresThresholdRepository. Both app_settings and application_config are env-scoped (PK (app_id, environment) / (application, environment)); finders take (app, env) — no env-agnostic variants.

storage/ — ClickHouse stores

  • ClickHouseExecutionStore, ClickHouseMetricsStore, ClickHouseMetricsQueryStore
  • ClickHouseStatsStore — pre-aggregated stats, punchcard
  • ClickHouseDiagramStore, ClickHouseAgentEventRepository
  • ClickHouseUsageTracker — usage_events for billing
  • ClickHouseRouteCatalogStore — persistent route catalog with first_seen cache, warm-loaded on startup

search/ — ClickHouse search and log stores

  • ClickHouseLogStore — log storage and query, MDC-based exchange/processor correlation
  • ClickHouseSearchIndex — full-text search

security/ — Spring Security

  • SecurityConfig — WebSecurityFilterChain, JWT filter, CORS, OIDC conditional
  • JwtAuthenticationFilter — OncePerRequestFilter, validates Bearer tokens
  • JwtServiceImpl — HMAC-SHA256 JWT (Nimbus JOSE)
  • OidcAuthController — /api/v1/auth/oidc (login-uri, token-exchange, logout)
  • OidcTokenExchanger — code -> tokens, role extraction from access_token then id_token
  • OidcProviderHelper — OIDC discovery, JWK source cache

agent/ — Agent lifecycle

  • SseConnectionManager — manages per-agent SSE connections, delivers commands
  • AgentLifecycleMonitor — @Scheduled 10s, LIVE->STALE->DEAD transitions
  • SsePayloadSigner — Ed25519 signs SSE payloads for agent verification

retention/ — JAR cleanup

  • JarRetentionJob — @Scheduled 03:00 daily, per-environment retention, skips deployed versions

config/ — Spring beans

  • RuntimeOrchestratorAutoConfig — conditional Docker/Disabled orchestrator + NetworkManager + EventMonitor
  • RuntimeBeanConfig — DeploymentExecutor, AppService, EnvironmentService
  • SecurityBeanConfig — JwtService, Ed25519, BootstrapTokenValidator
  • StorageBeanConfig — all repositories
  • ClickHouseConfig — ClickHouse JdbcTemplate, schema initializer