docs(rules): document the buildtime/public registry split
Future-proofs against well-meaning "fixes" that would re-align inconsistent hostnames. CI keeps pushing to gitea.siegeln.net; runtime defaults speak registry.cameleer.io (the public alias of the same registry). Both forms of the same image coexist intentionally during the institutionalization period. - CLAUDE.md: new "Registry naming (buildtime vs public)" section between Related Project and Modules; loader-image default mention now says registry.cameleer.io with an inline cross-reference; license-API note flags that com.cameleer:cameleer-common stays on the agent repo's groupId until that project follows the same flip - .claude/rules/cicd.md: registry line now names both hostnames and points at the new CLAUDE.md section Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -15,7 +15,7 @@ paths:
|
||||
- UI build script (`ui/package.json`): `build` is `vite build` only — the type-check pass was split out into `npm run typecheck` (run separately when you want a full `tsc --noEmit` sweep).
|
||||
- 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: `gitea.siegeln.net/cameleer/cameleer-server` (container images)
|
||||
- Registry: `gitea.siegeln.net/cameleer/cameleer-server` (container images — CI push target, internal hostname). The same registry is reachable as `registry.cameleer.io/cameleer/cameleer-server` for customer pulls; the server's compiled-in image defaults target the public alias. See `CLAUDE.md` § Registry naming.
|
||||
- `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
|
||||
- Deployment target: k3s at 192.168.50.86, namespace `cameleer` (main), `cam-<slug>` (feature branches)
|
||||
|
||||
13
CLAUDE.md
13
CLAUDE.md
@@ -10,7 +10,16 @@ Cameleer Server — observability server that receives, stores, and serves Camel
|
||||
|
||||
- **cameleer** (`https://gitea.siegeln.net/cameleer/cameleer`) — the Java agent that instruments Camel applications
|
||||
- Protocol defined in `cameleer-common/PROTOCOL.md` in the agent repo
|
||||
- This server depends on `com.cameleer:cameleer-common` (shared models and graph API)
|
||||
- This server depends on `com.cameleer:cameleer-common` (shared models and graph API). The agent repo's groupId stays on `com.cameleer` until that project is institutionalized — this server's own modules are `io.cameleer.*`.
|
||||
|
||||
## Registry naming (buildtime vs public)
|
||||
|
||||
The Gitea container / Maven / npm registry has **two DNS names that resolve to the same instance**:
|
||||
|
||||
- `gitea.siegeln.net` — internal hostname. CI pushes images here; `pom.xml`, `ui/.npmrc`, `ui/Dockerfile`, `ui/package-lock.json`, `deploy/`, and `.gitea/workflows/*.yml` all reference it. **Buildtime infrastructure.**
|
||||
- `registry.cameleer.io` — public alias customers pull from. Compiled-in defaults (`application.yml`, `DeploymentExecutor.java` `@Value`), customer-facing docs (`HOWTO.md`, `ui/README.md`), and runtime image refs use this. **Customer-visible.**
|
||||
|
||||
The asymmetry is **intentional** during the institutionalization period — CI keeps publishing to the internal name while everything customer-shipped speaks the public name. Don't "fix" mismatches between, e.g., `pom.xml`'s registry URL and `application.yml`'s loader-image default; they speak to different audiences.
|
||||
|
||||
## Modules
|
||||
|
||||
@@ -64,7 +73,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.
|
||||
- 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.
|
||||
- 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.
|
||||
- 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 `registry.cameleer.io/cameleer/cameleer-runtime-loader:latest`) is built and published by **cameleer-saas** at `docker/runtime-loader/` — this repo only consumes it. (Same image, internal push target is `gitea.siegeln.net/cameleer/cameleer-runtime-loader:latest`; see "Registry naming" above.) 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).
|
||||
- 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
|
||||
|
||||
Reference in New Issue
Block a user