From 30aaacb5b59177b0641039b7f281deca9ae40403 Mon Sep 17 00:00:00 2001 From: hsiegeln <37154749+hsiegeln@users.noreply.github.com> Date: Tue, 7 Apr 2026 18:58:27 +0200 Subject: [PATCH] fix: correct protocol version header, disable SQL logging, document deployment pipeline - ServerApiClient: use X-Cameleer-Protocol-Version: 1 (server expects "1", not "2") - Disable Hibernate show-sql in dev profile (too verbose) - CLAUDE.md: document deployment pipeline architecture, M2M server role in bootstrap, runtime-base image in CI Co-Authored-By: Claude Opus 4.6 (1M context) --- CLAUDE.md | 22 +++++++++++++++++-- .../saas/identity/ServerApiClient.java | 2 +- src/main/resources/application-dev.yml | 2 +- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 61f1562..a194732 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -83,14 +83,31 @@ Separate Vite+React SPA replacing Logto's default sign-in page. Visually matches The server's OIDC config (`OidcConfig`) includes `audience` (RFC 8707 resource indicator) and `additionalScopes`. The `audience` is sent as `resource` in both the authorization request and token exchange, which makes Logto return a JWT access token instead of opaque. The Custom JWT script maps org roles to `roles: ["server:admin"]`. If OIDC returns no roles and the user already exists, `syncOidcRoles` preserves existing local roles. +### Deployment pipeline + +App deployment is fully async via `DeploymentExecutor` (separate `@Service` so Spring `@Async` proxy works): +1. Build image: `FROM cameleer-runtime-base:latest` + `COPY app.jar` (via `DockerRuntimeOrchestrator`) +2. Stop/remove old container (by deployment metadata + orphan name cleanup) +3. Start new container on the compose network with env vars (`CAMELEER_AUTH_TOKEN`, `CAMELEER_EXPORT_ENDPOINT`, etc.), Traefik labels, resource limits +4. Wait for Docker health check (`/cameleer/health` on agent port 9464) +5. Update deployment status to RUNNING or FAILED + +Key files: +- `DeploymentExecutor.java` — async deployment logic, extracted from `DeploymentService` to avoid Spring `@Async` self-invocation +- `DockerRuntimeOrchestrator.java` — Docker client (zerodep transport for Unix socket), image build, container lifecycle +- `docker/runtime-base/Dockerfile` — base image with agent JAR, maps env vars to `-D` system properties +- `ServerApiClient.java` — M2M token acquisition for SaaS→server API calls (agent status). Uses `X-Cameleer-Protocol-Version: 1` header +- Docker socket access: `group_add: ["0"]` in docker-compose.yml (not root group membership in Dockerfile) +- Network: deployed containers join `${COMPOSE_PROJECT_NAME}_cameleer` network + ### Bootstrap (`docker/logto-bootstrap.sh`) Idempotent script run via `logto-bootstrap` init container. Phases: 1. Wait for Logto + server health 2. Get Management API token (reads `m-default` secret from DB) -3. Create Logto apps (SPA, Traditional with `skipConsent`, M2M with Management API role) +3. Create Logto apps (SPA, Traditional with `skipConsent`, M2M with Management API role + server API role) 3b. Create API resource scopes (10 platform + 3 server scopes) -4. Create org roles (owner, operator, viewer with API resource scope assignments) +4. Create org roles (owner, operator, viewer with API resource scope assignments) + M2M server role (`cameleer-m2m-server` with `server:admin` scope) 5. Create users (platform owner with Logto console access, viewer for testing read-only OIDC) 6. Create organization, add users with org roles (owner + viewer) 7. Configure cameleer3-server OIDC (`rolesClaim: "roles"`, `audience`, `defaultRoles: ["VIEWER"]`) @@ -109,6 +126,7 @@ Platform owner credentials (`SAAS_ADMIN_USER`/`SAAS_ADMIN_PASS`) work for both t - 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 + - `cameleer-runtime-base` — base image for deployed apps (agent JAR + JRE). CI downloads latest agent SNAPSHOT from Gitea Maven registry - Docker builds: `--no-cache`, `--provenance=false` for Gitea compatibility - `docker-compose.dev.yml` — exposes ports for direct access, sets `SPRING_PROFILES_ACTIVE: dev`. Volume-mounts `./ui/dist` into the container so local UI builds are served without rebuilding the Docker image (`SPRING_WEB_RESOURCES_STATIC_LOCATIONS` overrides classpath) - Design system: import from `@cameleer/design-system` (Gitea npm registry) diff --git a/src/main/java/net/siegeln/cameleer/saas/identity/ServerApiClient.java b/src/main/java/net/siegeln/cameleer/saas/identity/ServerApiClient.java index 5271c44..bf27b51 100644 --- a/src/main/java/net/siegeln/cameleer/saas/identity/ServerApiClient.java +++ b/src/main/java/net/siegeln/cameleer/saas/identity/ServerApiClient.java @@ -45,7 +45,7 @@ public class ServerApiClient { .get() .uri(uri) .header("Authorization", "Bearer " + getAccessToken()) - .header("X-Cameleer-Protocol-Version", "2"); + .header("X-Cameleer-Protocol-Version", "1"); } private synchronized String getAccessToken() { diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index 45827c3..b656d18 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -1,3 +1,3 @@ spring: jpa: - show-sql: true + show-sql: false