diff --git a/CLAUDE.md b/CLAUDE.md index 25b080fc..06eb769b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -282,7 +282,7 @@ In SaaS mode, each tenant's server and its deployed apps are isolated at the Doc # GitNexus — Code Intelligence -This project is indexed by GitNexus as **cameleer3-server** (5787 symbols, 14234 relationships, 300 execution flows). Use the GitNexus MCP tools to understand code, assess impact, and navigate safely. +This project is indexed by GitNexus as **cameleer3-server** (5803 symbols, 14279 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. diff --git a/HOWTO.md b/HOWTO.md index 85d84592..f709f5eb 100644 --- a/HOWTO.md +++ b/HOWTO.md @@ -183,13 +183,7 @@ Logto is proxy-aware via `TRUST_PROXY_HEADER=1`. The `LOGTO_ENDPOINT` and `LOGTO - Name: `Cameleer SaaS` - Assign the API Resource created above with `server:admin` scope - Note the **Client ID** and **Client Secret** -5. **Configure Cameleer OIDC login**: Use the admin API (`PUT /api/v1/admin/oidc`) or set env vars for initial seeding: - ``` - CAMELEER_OIDC_ENABLED=true - CAMELEER_OIDC_ISSUER=/oidc - CAMELEER_OIDC_CLIENT_ID= - CAMELEER_OIDC_CLIENT_SECRET= - ``` +5. **Configure Cameleer OIDC login**: Use the admin API (`PUT /api/v1/admin/oidc`) or the admin UI. OIDC login configuration is stored in the database — no env vars needed for the SPA OIDC flow. 6. **Configure resource server** (for M2M token validation): ``` CAMELEER_SERVER_SECURITY_OIDCISSUERURI=/oidc @@ -385,36 +379,82 @@ When the write buffer is full (default capacity: 50,000), ingestion endpoints re ## Configuration -Key settings in `cameleer3-server-app/src/main/resources/application.yml`: +Key settings in `cameleer3-server-app/src/main/resources/application.yml`. All custom properties live under `cameleer.server.*`. Env vars are a mechanical 1:1 mapping (dots to underscores, uppercase). -| Setting | Default | Description | -|---------|---------|-------------| -| `server.port` | 8081 | Server port | -| `ingestion.buffer-capacity` | 50000 | Max items in write buffer | -| `ingestion.batch-size` | 5000 | Items per batch insert | -| `ingestion.flush-interval-ms` | 1000 | Buffer flush interval (ms) | -| `agent-registry.heartbeat-interval-seconds` | 30 | Expected heartbeat interval | -| `agent-registry.stale-threshold-seconds` | 90 | Time before agent marked STALE | -| `agent-registry.dead-threshold-seconds` | 300 | Time after STALE before DEAD | -| `agent-registry.command-expiry-seconds` | 60 | Pending command TTL | -| `agent-registry.keepalive-interval-seconds` | 15 | SSE ping keepalive interval | -| `security.access-token-expiry-ms` | 3600000 | JWT access token lifetime (1h) | -| `security.refresh-token-expiry-ms` | 604800000 | Refresh token lifetime (7d) | -| `cameleer.server.security.bootstraptoken` | `${CAMELEER_SERVER_SECURITY_BOOTSTRAPTOKEN}` | Bootstrap token for agent registration (required) | -| `cameleer.server.security.bootstraptokenprevious` | `${CAMELEER_SERVER_SECURITY_BOOTSTRAPTOKENPREVIOUS}` | Previous bootstrap token for rotation (optional) | -| `cameleer.server.security.uiuser` | `admin` | UI login username (`CAMELEER_SERVER_SECURITY_UIUSER` env var) | -| `cameleer.server.security.uipassword` | `admin` | UI login password (`CAMELEER_SERVER_SECURITY_UIPASSWORD` env var) | -| `cameleer.server.security.uiorigin` | `http://localhost:5173` | CORS allowed origin for UI (`CAMELEER_SERVER_SECURITY_UIORIGIN` env var) | -| `cameleer.server.security.corsallowedorigins` | *(empty)* | Comma-separated CORS origins (`CAMELEER_SERVER_SECURITY_CORSALLOWEDORIGINS`) — overrides `uiorigin` when set | -| `cameleer.server.security.jwtsecret` | *(random)* | HMAC secret for JWT signing (`CAMELEER_SERVER_SECURITY_JWTSECRET`). If set, tokens survive restarts | -| `security.oidc.enabled` | `false` | Enable OIDC login (`CAMELEER_OIDC_ENABLED`) | -| `security.oidc.issuer-uri` | | OIDC provider issuer URL (`CAMELEER_OIDC_ISSUER`) | -| `security.oidc.client-id` | | OAuth2 client ID (`CAMELEER_OIDC_CLIENT_ID`) | -| `security.oidc.client-secret` | | OAuth2 client secret (`CAMELEER_OIDC_CLIENT_SECRET`) | -| `security.oidc.roles-claim` | `realm_access.roles` | JSONPath to roles in OIDC id_token (`CAMELEER_OIDC_ROLES_CLAIM`) | -| `security.oidc.default-roles` | `VIEWER` | Default roles for new OIDC users (`CAMELEER_OIDC_DEFAULT_ROLES`) | -| `cameleer.server.indexer.debouncems` | `2000` | Search indexer debounce delay (`CAMELEER_SERVER_INDEXER_DEBOUNCEMS`) | -| `cameleer.indexer.queue-size` | `10000` | Search indexer queue capacity (`CAMELEER_INDEXER_QUEUE_SIZE`) | +**Security** (`cameleer.server.security.*`): + +| Setting | Default | Env var | Description | +|---------|---------|---------|-------------| +| `cameleer.server.security.bootstraptoken` | *(required)* | `CAMELEER_SERVER_SECURITY_BOOTSTRAPTOKEN` | Bootstrap token for agent registration | +| `cameleer.server.security.bootstraptokenprevious` | *(empty)* | `CAMELEER_SERVER_SECURITY_BOOTSTRAPTOKENPREVIOUS` | Previous bootstrap token for rotation | +| `cameleer.server.security.uiuser` | `admin` | `CAMELEER_SERVER_SECURITY_UIUSER` | UI login username | +| `cameleer.server.security.uipassword` | `admin` | `CAMELEER_SERVER_SECURITY_UIPASSWORD` | UI login password | +| `cameleer.server.security.uiorigin` | `http://localhost:5173` | `CAMELEER_SERVER_SECURITY_UIORIGIN` | CORS allowed origin for UI | +| `cameleer.server.security.corsallowedorigins` | *(empty)* | `CAMELEER_SERVER_SECURITY_CORSALLOWEDORIGINS` | Comma-separated CORS origins — overrides `uiorigin` when set | +| `cameleer.server.security.jwtsecret` | *(random)* | `CAMELEER_SERVER_SECURITY_JWTSECRET` | HMAC secret for JWT signing. If set, tokens survive restarts | +| `cameleer.server.security.accesstokenexpiryms` | `3600000` | `CAMELEER_SERVER_SECURITY_ACCESSTOKENEXPIRYMS` | JWT access token lifetime (1h) | +| `cameleer.server.security.refreshtokenexpiryms` | `604800000` | `CAMELEER_SERVER_SECURITY_REFRESHTOKENEXPIRYMS` | Refresh token lifetime (7d) | + +**OIDC resource server** (`cameleer.server.security.oidc.*`): + +| Setting | Default | Env var | Description | +|---------|---------|---------|-------------| +| `cameleer.server.security.oidc.issueruri` | *(empty)* | `CAMELEER_SERVER_SECURITY_OIDC_ISSUERURI` | OIDC issuer URI — enables resource server mode | +| `cameleer.server.security.oidc.jwkseturi` | *(empty)* | `CAMELEER_SERVER_SECURITY_OIDC_JWKSETURI` | Direct JWKS URL — bypasses OIDC discovery | +| `cameleer.server.security.oidc.audience` | *(empty)* | `CAMELEER_SERVER_SECURITY_OIDC_AUDIENCE` | Expected JWT audience | +| `cameleer.server.security.oidc.tlsskipverify` | `false` | `CAMELEER_SERVER_SECURITY_OIDC_TLSSKIPVERIFY` | Skip TLS cert verification for OIDC calls | + +**Note:** OIDC *login* configuration (issuer, client ID, client secret, roles claim, default roles) is stored in the database and managed via the admin API (`PUT /api/v1/admin/oidc`) or admin UI. The env vars above are for resource server mode (M2M token validation) only. + +**Ingestion** (`cameleer.server.ingestion.*`): + +| Setting | Default | Env var | Description | +|---------|---------|---------|-------------| +| `cameleer.server.ingestion.buffercapacity` | `50000` | `CAMELEER_SERVER_INGESTION_BUFFERCAPACITY` | Max items in write buffer | +| `cameleer.server.ingestion.batchsize` | `5000` | `CAMELEER_SERVER_INGESTION_BATCHSIZE` | Items per batch insert | +| `cameleer.server.ingestion.flushintervalms` | `5000` | `CAMELEER_SERVER_INGESTION_FLUSHINTERVALMS` | Buffer flush interval (ms) | +| `cameleer.server.ingestion.bodysizelimit` | `16384` | `CAMELEER_SERVER_INGESTION_BODYSIZELIMIT` | Max body size per execution (bytes) | + +**Agent registry** (`cameleer.server.agentregistry.*`): + +| Setting | Default | Env var | Description | +|---------|---------|---------|-------------| +| `cameleer.server.agentregistry.heartbeatintervalms` | `30000` | `CAMELEER_SERVER_AGENTREGISTRY_HEARTBEATINTERVALMS` | Expected heartbeat interval (ms) | +| `cameleer.server.agentregistry.stalethresholdms` | `90000` | `CAMELEER_SERVER_AGENTREGISTRY_STALETHRESHOLDMS` | Time before agent marked STALE (ms) | +| `cameleer.server.agentregistry.deadthresholdms` | `300000` | `CAMELEER_SERVER_AGENTREGISTRY_DEADTHRESHOLDMS` | Time after STALE before DEAD (ms) | +| `cameleer.server.agentregistry.pingintervalms` | `15000` | `CAMELEER_SERVER_AGENTREGISTRY_PINGINTERVALMS` | SSE ping keepalive interval (ms) | +| `cameleer.server.agentregistry.commandexpiryms` | `60000` | `CAMELEER_SERVER_AGENTREGISTRY_COMMANDEXPIRYMS` | Pending command TTL (ms) | +| `cameleer.server.agentregistry.lifecyclecheckintervalms` | `10000` | `CAMELEER_SERVER_AGENTREGISTRY_LIFECYCLECHECKINTERVALMS` | Lifecycle monitor interval (ms) | + +**Runtime** (`cameleer.server.runtime.*`): + +| Setting | Default | Env var | Description | +|---------|---------|---------|-------------| +| `cameleer.server.runtime.enabled` | `true` | `CAMELEER_SERVER_RUNTIME_ENABLED` | Enable Docker orchestration | +| `cameleer.server.runtime.baseimage` | `cameleer-runtime-base:latest` | `CAMELEER_SERVER_RUNTIME_BASEIMAGE` | Base Docker image for app containers | +| `cameleer.server.runtime.dockernetwork` | `cameleer` | `CAMELEER_SERVER_RUNTIME_DOCKERNETWORK` | Primary Docker network | +| `cameleer.server.runtime.jarstoragepath` | `/data/jars` | `CAMELEER_SERVER_RUNTIME_JARSTORAGEPATH` | JAR file storage directory | +| `cameleer.server.runtime.jardockervolume` | *(empty)* | `CAMELEER_SERVER_RUNTIME_JARDOCKERVOLUME` | Docker volume for JAR sharing | +| `cameleer.server.runtime.routingmode` | `path` | `CAMELEER_SERVER_RUNTIME_ROUTINGMODE` | `path` or `subdomain` Traefik routing | +| `cameleer.server.runtime.routingdomain` | `localhost` | `CAMELEER_SERVER_RUNTIME_ROUTINGDOMAIN` | Domain for Traefik routing labels | +| `cameleer.server.runtime.serverurl` | *(empty)* | `CAMELEER_SERVER_RUNTIME_SERVERURL` | Server URL injected into app containers | +| `cameleer.server.runtime.agenthealthport` | `9464` | `CAMELEER_SERVER_RUNTIME_AGENTHEALTHPORT` | Agent health check port | +| `cameleer.server.runtime.healthchecktimeout` | `60` | `CAMELEER_SERVER_RUNTIME_HEALTHCHECKTIMEOUT` | Health check timeout (seconds) | +| `cameleer.server.runtime.container.memorylimit` | `512m` | `CAMELEER_SERVER_RUNTIME_CONTAINER_MEMORYLIMIT` | Default memory limit for app containers | +| `cameleer.server.runtime.container.cpushares` | `512` | `CAMELEER_SERVER_RUNTIME_CONTAINER_CPUSHARES` | Default CPU shares for app containers | + +**Other** (`cameleer.server.*`): + +| Setting | Default | Env var | Description | +|---------|---------|---------|-------------| +| `cameleer.server.tenant.id` | `default` | `CAMELEER_SERVER_TENANT_ID` | Tenant identifier | +| `cameleer.server.indexer.debouncems` | `2000` | `CAMELEER_SERVER_INDEXER_DEBOUNCEMS` | Search indexer debounce delay (ms) | +| `cameleer.server.indexer.queuesize` | `10000` | `CAMELEER_SERVER_INDEXER_QUEUESIZE` | Search indexer queue capacity | +| `cameleer.server.license.token` | *(empty)* | `CAMELEER_SERVER_LICENSE_TOKEN` | License token | +| `cameleer.server.license.publickey` | *(empty)* | `CAMELEER_SERVER_LICENSE_PUBLICKEY` | License verification public key | +| `cameleer.server.clickhouse.url` | `jdbc:clickhouse://localhost:8123/cameleer` | `CAMELEER_SERVER_CLICKHOUSE_URL` | ClickHouse JDBC URL | +| `cameleer.server.clickhouse.username` | `default` | `CAMELEER_SERVER_CLICKHOUSE_USERNAME` | ClickHouse user | +| `cameleer.server.clickhouse.password` | *(empty)* | `CAMELEER_SERVER_CLICKHOUSE_PASSWORD` | ClickHouse password | ## Web UI Development diff --git a/docs/SERVER-CAPABILITIES.md b/docs/SERVER-CAPABILITIES.md index ae07d4ed..00afa294 100644 --- a/docs/SERVER-CAPABILITIES.md +++ b/docs/SERVER-CAPABILITIES.md @@ -56,7 +56,7 @@ LIVE ──(no heartbeat for 90s)──→ STALE ──(300s more)──→ DEAD └────(heartbeat arrives)──────────┘ ``` -Thresholds are configurable via `agent-registry.*` properties. +Thresholds are configurable via `cameleer.server.agentregistry.*` properties. ### Registration @@ -419,7 +419,7 @@ Registry: `gitea.siegeln.net/cameleer/cameleer3-server` | `SPRING_DATASOURCE_URL` | No | `jdbc:postgresql://localhost:5432/cameleer3` | PostgreSQL JDBC URL | | `SPRING_DATASOURCE_USERNAME` | No | `cameleer` | PostgreSQL user | | `SPRING_DATASOURCE_PASSWORD` | No | `cameleer_dev` | PostgreSQL password | -| `CAMELEER_DB_SCHEMA` | No | `tenant_{CAMELEER_SERVER_TENANT_ID}` | PostgreSQL schema (override for feature branches) | +| `CAMELEER_SERVER_INGESTION_BODYSIZELIMIT` | No | `16384` | Max body size per execution (bytes) | | `CAMELEER_SERVER_SECURITY_OIDCISSUERURI` | No | (empty) | OIDC issuer URI — enables resource server mode for M2M tokens | | `CAMELEER_SERVER_SECURITY_OIDCJWKSETURI` | No | (empty) | Direct JWKS URL — bypasses OIDC discovery for container networking | | `CAMELEER_SERVER_SECURITY_OIDCAUDIENCE` | No | (empty) | Expected JWT audience (API resource indicator) | @@ -435,18 +435,19 @@ Registry: `gitea.siegeln.net/cameleer/cameleer3-server` | Variable | Default | Purpose | |----------|---------|---------| -| `INGESTION_BUFFER_CAPACITY` | 50000 | Ring buffer size | -| `INGESTION_BATCH_SIZE` | 5000 | Flush batch size | -| `INGESTION_FLUSH_INTERVAL_MS` | 5000 | Periodic flush interval | +| `CAMELEER_SERVER_INGESTION_BUFFERCAPACITY` | 50000 | Ring buffer size | +| `CAMELEER_SERVER_INGESTION_BATCHSIZE` | 5000 | Flush batch size | +| `CAMELEER_SERVER_INGESTION_FLUSHINTERVALMS` | 5000 | Periodic flush interval | +| `CAMELEER_SERVER_INGESTION_BODYSIZELIMIT` | 16384 | Max body size per execution (bytes) | ### Agent Registry Tuning | Variable | Default | Purpose | |----------|---------|---------| -| `AGENT_REGISTRY_STALE_THRESHOLD_MS` | 90000 | Heartbeat miss → STALE | -| `AGENT_REGISTRY_DEAD_THRESHOLD_MS` | 300000 | STALE duration → DEAD | -| `AGENT_REGISTRY_PING_INTERVAL_MS` | 15000 | SSE keepalive interval | -| `AGENT_REGISTRY_COMMAND_EXPIRY_MS` | 60000 | Pending command TTL | +| `CAMELEER_SERVER_AGENTREGISTRY_STALETHRESHOLDMS` | 90000 | Heartbeat miss → STALE | +| `CAMELEER_SERVER_AGENTREGISTRY_DEADTHRESHOLDMS` | 300000 | STALE duration → DEAD | +| `CAMELEER_SERVER_AGENTREGISTRY_PINGINTERVALMS` | 15000 | SSE keepalive interval | +| `CAMELEER_SERVER_AGENTREGISTRY_COMMANDEXPIRYMS` | 60000 | Pending command TTL | ---