docs: reflect cameleer-runtime-loader image source moved to cameleer-saas

Update CLAUDE.md and .claude/rules/cicd.md to point at the new
source-of-truth location (cameleer-saas/docker/runtime-loader/) and
flag LoaderHardeningIT as the cross-repo contract test instead of an
internal regression guard. The image's runtime contract (env vars,
mount path, exit codes) is unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-04-28 13:05:39 +02:00
parent 3334f0a1d2
commit a605d38750
2 changed files with 8 additions and 8 deletions

View File

@@ -16,7 +16,7 @@ paths:
- Docker: multi-stage build (`Dockerfile`), `$BUILDPLATFORM` for native Maven on ARM64 runner, amd64 runtime. `docker-entrypoint.sh` imports `/certs/ca.pem` into JVM truststore before starting the app (supports custom CAs for OIDC discovery without `CAMELEER_SERVER_SECURITY_OIDCTLSSKIPVERIFY`). - Docker: multi-stage build (`Dockerfile`), `$BUILDPLATFORM` for native Maven on ARM64 runner, amd64 runtime. `docker-entrypoint.sh` imports `/certs/ca.pem` into JVM truststore before starting the app (supports custom CAs for OIDC discovery without `CAMELEER_SERVER_SECURITY_OIDCTLSSKIPVERIFY`).
- `REGISTRY_TOKEN` build arg required for `cameleer-common` dependency resolution - `REGISTRY_TOKEN` build arg required for `cameleer-common` dependency resolution
- Registry: `gitea.siegeln.net/cameleer/cameleer-server` (container images) - Registry: `gitea.siegeln.net/cameleer/cameleer-server` (container images)
- `cameleer-runtime-loader` image (init container that fetches the deployable JAR before the runtime container starts) is built and pushed by the same `docker` job, but only when files under `cameleer-runtime-loader/` actually changed in the push. Detection runs in the `build` job (`Detect runtime-loader changes` step, diffs `${{ github.event.before }}..${{ github.sha }}`) and is exposed as the `loader_changed` job output. The loader build step uses `if: needs.build.outputs.loader_changed == 'true'`. Build job's checkout uses `fetch-depth: 0` so the diff has access to the prior commit. - `cameleer-runtime-loader` image (init container that fetches the deployable JAR before the runtime container starts) is built and pushed by **cameleer-saas** CI (`docker/runtime-loader/` in that repo) — it lives alongside the other sidecar/infra images (runtime-base, postgres, clickhouse, traefik, logto). cameleer-server **consumes** the image via `DockerRuntimeOrchestrator` but does not build it. Cross-repo contract is regression-tested by `LoaderHardeningIT` here, which pulls the published `:latest` and asserts exit 0 under the orchestrator's hardening contract.
- K8s manifests in `deploy/` — Kustomize base + overlays (main/feature), shared infra (PostgreSQL, ClickHouse, Logto) as top-level manifests - 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) - 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` - Feature branches: isolated namespace, PG schema; Traefik Ingress at `<slug>-api.cameleer.siegeln.net`

View File

@@ -64,7 +64,7 @@ java -jar cameleer-server-app/target/cameleer-server-app-1.0-SNAPSHOT.jar
- Login routing: `GET /api/v1/auth/capabilities` (unauthenticated) tells the SPA whether OIDC is the primary entry point. When OIDC is configured, the SSO button is the primary CTA and the local form is hidden behind `?local` (admin-recovery escape hatch). Per RFC 9700 §4.4 we do **not** use `prompt=none` for primary login — that returns `login_required` for first-time users and traps them on a local form. - Login routing: `GET /api/v1/auth/capabilities` (unauthenticated) tells the SPA whether OIDC is the primary entry point. When OIDC is configured, the SSO button is the primary CTA and the local form is hidden behind `?local` (admin-recovery escape hatch). Per RFC 9700 §4.4 we do **not** use `prompt=none` for primary login — that returns `login_required` for first-time users and traps them on a local form.
- OIDC: Optional external identity provider support (token exchange pattern). Configured via admin API/UI, stored in database (`server_config` table). Resource server mode: accepts external access tokens (Logto M2M) via JWKS validation when `CAMELEER_SERVER_SECURITY_OIDCISSUERURI` is set. Scope-based role mapping via `SystemRole.normalizeScope()`. System roles synced on every OIDC login via `applyClaimMappings()` in `OidcAuthController` (calls `clearManagedAssignments` + `assignManagedRole` on `RbacService`) — always overwrites managed role assignments; uses managed assignment origin to avoid touching group-inherited or directly-assigned roles. Supports ES384, ES256, RS256. - OIDC: Optional external identity provider support (token exchange pattern). Configured via admin API/UI, stored in database (`server_config` table). Resource server mode: accepts external access tokens (Logto M2M) via JWKS validation when `CAMELEER_SERVER_SECURITY_OIDCISSUERURI` is set. Scope-based role mapping via `SystemRole.normalizeScope()`. System roles synced on every OIDC login via `applyClaimMappings()` in `OidcAuthController` (calls `clearManagedAssignments` + `assignManagedRole` on `RbacService`) — always overwrites managed role assignments; uses managed assignment origin to avoid touching group-inherited or directly-assigned roles. Supports ES384, ES256, RS256.
- OIDC role extraction: `OidcTokenExchanger` reads roles from the **access_token** first (JWT with `at+jwt` type), then falls back to id_token. `OidcConfig` includes `audience` (RFC 8707 resource indicator) and `additionalScopes`. All provider-specific configuration is external — no provider-specific code in the server. - OIDC role extraction: `OidcTokenExchanger` reads roles from the **access_token** first (JWT with `at+jwt` type), then falls back to id_token. `OidcConfig` includes `audience` (RFC 8707 resource indicator) and `additionalScopes`. All provider-specific configuration is external — no provider-specific code in the server.
- Container orchestration: tenant containers no longer bind-mount JARs from the host. `DockerRuntimeOrchestrator.startContainer` runs a 2-phase op per replica — a `cameleer-runtime-loader` init container fetches the JAR from a signed URL into a per-replica named volume, then the main container mounts that volume RO at `/app/jars`. Env vars: `CAMELEER_SERVER_RUNTIME_LOADERIMAGE` (loader init-container image, default `gitea.siegeln.net/cameleer/cameleer-runtime-loader:latest`); `CAMELEER_SERVER_RUNTIME_ARTIFACTTOKENTTLSECONDS` (signed-URL TTL, default `600`); `CAMELEER_SERVER_RUNTIME_ARTIFACTBASEURL` (base URL the loader uses to reach the server; defaults to `cameleer.server.runtime.serverurl`, then `http://cameleer-server:8081`). See `.claude/rules/docker-orchestration.md` for the full loader pattern. - Container orchestration: tenant containers no longer bind-mount JARs from the host. `DockerRuntimeOrchestrator.startContainer` runs a 2-phase op per replica — a `cameleer-runtime-loader` init container fetches the JAR from a signed URL into a per-replica named volume, then the main container mounts that volume RO at `/app/jars`. The loader image (default `gitea.siegeln.net/cameleer/cameleer-runtime-loader:latest`) is built and published by **cameleer-saas** at `docker/runtime-loader/` — this repo only consumes it. Env vars: `CAMELEER_SERVER_RUNTIME_LOADERIMAGE` (override loader image); `CAMELEER_SERVER_RUNTIME_ARTIFACTTOKENTTLSECONDS` (signed-URL TTL, default `600`); `CAMELEER_SERVER_RUNTIME_ARTIFACTBASEURL` (base URL the loader uses to reach the server; defaults to `cameleer.server.runtime.serverurl`, then `http://cameleer-server:8081`). See `.claude/rules/docker-orchestration.md` for the full loader pattern; `LoaderHardeningIT` is the cross-repo contract test.
- Sensitive keys: Global enforced baseline for masking sensitive data in agent payloads. Merge rule: `final = global UNION per-app` (case-insensitive dedup, per-app can only add, never remove global keys). - Sensitive keys: Global enforced baseline for masking sensitive data in agent payloads. Merge rule: `final = global UNION per-app` (case-insensitive dedup, per-app can only add, never remove global keys).
- User persistence: PostgreSQL `users` table, admin CRUD at `/api/v1/admin/users`. `users.user_id` is the **bare** identifier — local users as `<username>`, OIDC users as `oidc:<sub>`. JWT `sub` carries the `user:` namespace prefix so `JwtAuthenticationFilter` can tell user tokens from agent tokens; write paths (`UiAuthController`, `OidcAuthController`, `UserAdminController`) all upsert unprefixed, and env-scoped read-path controllers strip the `user:` prefix before using the value as an FK to `users.user_id` / `user_roles.user_id`. Alerting / outbound FKs (`alert_rules.created_by`, `outbound_connections.created_by`, …) therefore all reference the bare form. - User persistence: PostgreSQL `users` table, admin CRUD at `/api/v1/admin/users`. `users.user_id` is the **bare** identifier — local users as `<username>`, OIDC users as `oidc:<sub>`. JWT `sub` carries the `user:` namespace prefix so `JwtAuthenticationFilter` can tell user tokens from agent tokens; write paths (`UiAuthController`, `OidcAuthController`, `UserAdminController`) all upsert unprefixed, and env-scoped read-path controllers strip the `user:` prefix before using the value as an FK to `users.user_id` / `user_roles.user_id`. Alerting / outbound FKs (`alert_rules.created_by`, `outbound_connections.created_by`, …) therefore all reference the bare form.
- Usage analytics: ClickHouse `usage_events` table tracks authenticated UI requests, flushed every 5s - Usage analytics: ClickHouse `usage_events` table tracks authenticated UI requests, flushed every 5s
@@ -100,7 +100,7 @@ When adding, removing, or renaming classes, controllers, endpoints, UI component
<!-- gitnexus:start --> <!-- gitnexus:start -->
# GitNexus — Code Intelligence # GitNexus — Code Intelligence
This project is indexed by GitNexus as **init-container-jar-fetch** (10716 symbols, 27745 relationships, 300 execution flows). Use the GitNexus MCP tools to understand code, assess impact, and navigate safely. This project is indexed by GitNexus as **cameleer-server** (10697 symbols, 27649 relationships, 300 execution flows). Use the GitNexus MCP tools to understand code, assess impact, and navigate safely.
> If any GitNexus tool warns the index is stale, run `npx gitnexus analyze` in terminal first. > If any GitNexus tool warns the index is stale, run `npx gitnexus analyze` in terminal first.
@@ -116,7 +116,7 @@ This project is indexed by GitNexus as **init-container-jar-fetch** (10716 symbo
1. `gitnexus_query({query: "<error or symptom>"})` — find execution flows related to the issue 1. `gitnexus_query({query: "<error or symptom>"})` — find execution flows related to the issue
2. `gitnexus_context({name: "<suspect function>"})` — see all callers, callees, and process participation 2. `gitnexus_context({name: "<suspect function>"})` — see all callers, callees, and process participation
3. `READ gitnexus://repo/init-container-jar-fetch/process/{processName}` — trace the full execution flow step by step 3. `READ gitnexus://repo/cameleer-server/process/{processName}` — trace the full execution flow step by step
4. For regressions: `gitnexus_detect_changes({scope: "compare", base_ref: "main"})` — see what your branch changed 4. For regressions: `gitnexus_detect_changes({scope: "compare", base_ref: "main"})` — see what your branch changed
## When Refactoring ## When Refactoring
@@ -155,10 +155,10 @@ This project is indexed by GitNexus as **init-container-jar-fetch** (10716 symbo
| Resource | Use for | | Resource | Use for |
|----------|---------| |----------|---------|
| `gitnexus://repo/init-container-jar-fetch/context` | Codebase overview, check index freshness | | `gitnexus://repo/cameleer-server/context` | Codebase overview, check index freshness |
| `gitnexus://repo/init-container-jar-fetch/clusters` | All functional areas | | `gitnexus://repo/cameleer-server/clusters` | All functional areas |
| `gitnexus://repo/init-container-jar-fetch/processes` | All execution flows | | `gitnexus://repo/cameleer-server/processes` | All execution flows |
| `gitnexus://repo/init-container-jar-fetch/process/{name}` | Step-by-step execution trace | | `gitnexus://repo/cameleer-server/process/{name}` | Step-by-step execution trace |
## Self-Check Before Finishing ## Self-Check Before Finishing