diff --git a/AGENTS.md b/AGENTS.md index c4ffbe1..760847f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,7 +1,7 @@ # GitNexus — Code Intelligence -This project is indexed by GitNexus as **cameleer-saas** (2676 symbols, 5768 relationships, 224 execution flows). Use the GitNexus MCP tools to understand code, assess impact, and navigate safely. +This project is indexed by GitNexus as **cameleer-saas** (2816 symbols, 5989 relationships, 238 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/docker-compose.yml b/docker-compose.yml index 38a83e8..f3c868a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -126,6 +126,7 @@ services: CAMELEER_SAAS_IDENTITY_LOGTOPUBLICENDPOINT: ${PUBLIC_PROTOCOL:-https}://${PUBLIC_HOST:-localhost} CAMELEER_SAAS_IDENTITY_M2MCLIENTID: ${LOGTO_M2M_CLIENT_ID:-} CAMELEER_SAAS_IDENTITY_M2MCLIENTSECRET: ${LOGTO_M2M_CLIENT_SECRET:-} + CAMELEER_SERVER_SECURITY_JWTSECRET: ${CAMELEER_SERVER_SECURITY_JWTSECRET:-cameleer-dev-jwt-secret} # Provisioning — passed to per-tenant server containers CAMELEER_SAAS_PROVISIONING_PUBLICHOST: ${PUBLIC_HOST:-localhost} CAMELEER_SAAS_PROVISIONING_PUBLICPROTOCOL: ${PUBLIC_PROTOCOL:-https} diff --git a/docs/architecture.md b/docs/architecture.md index b76b452..dc9c886 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -897,7 +897,7 @@ Env vars injected into provisioned per-tenant server containers by `DockerTenant | `CAMELEER_SERVER_CLICKHOUSE_URL` | `jdbc:clickhouse://cameleer-clickhouse:8123/cameleer` | ClickHouse JDBC URL | | `CAMELEER_SERVER_TENANT_ID` | *(tenant slug)* | Tenant identifier for data isolation | | `CAMELEER_SERVER_SECURITY_BOOTSTRAPTOKEN` | *(generated)* | Agent bootstrap token | -| `CAMELEER_SERVER_SECURITY_JWTSECRET` | *(generated)* | JWT signing secret | +| `CAMELEER_SERVER_SECURITY_JWTSECRET` | *(generated, must be non-empty)* | JWT signing secret | | `CAMELEER_SERVER_SECURITY_OIDC_ISSUERURI` | `${PUBLIC_PROTOCOL}://${PUBLIC_HOST}/oidc` | OIDC issuer for M2M tokens | | `CAMELEER_SERVER_SECURITY_OIDC_JWKSETURI` | `http://cameleer-logto:3001/oidc/jwks` | Docker-internal JWK fetch | | `CAMELEER_SERVER_SECURITY_OIDC_AUDIENCE` | `https://api.cameleer.local` | JWT audience validation | diff --git a/installer/install.ps1 b/installer/install.ps1 index 70d96ca..a14ef88 100644 --- a/installer/install.ps1 +++ b/installer/install.ps1 @@ -578,32 +578,37 @@ function Generate-EnvFile { $ts = (Get-Date -Format 'yyyy-MM-dd HH:mm:ss') + ' UTC' $bt = Generate-Password + $jwtSecret = Generate-Password + if ($c.DeploymentMode -eq 'standalone') { $content = @" # Cameleer Server Configuration (standalone) # Generated by installer v${CAMELEER_INSTALLER_VERSION} on $ts - + VERSION=$($c.Version) PUBLIC_HOST=$($c.PublicHost) PUBLIC_PROTOCOL=$($c.PublicProtocol) HTTP_PORT=$($c.HttpPort) HTTPS_PORT=$($c.HttpsPort) - + # PostgreSQL POSTGRES_USER=cameleer POSTGRES_PASSWORD=$($c.PostgresPassword) POSTGRES_DB=cameleer - + # ClickHouse CLICKHOUSE_PASSWORD=$($c.ClickhousePassword) - + # Server admin SERVER_ADMIN_USER=$($c.AdminUser) SERVER_ADMIN_PASS=$($c.AdminPass) - + # Bootstrap token BOOTSTRAP_TOKEN=$bt - + +# JWT signing secret (required by server, must be non-empty) +CAMELEER_SERVER_SECURITY_JWTSECRET=$jwtSecret + # Docker DOCKER_SOCKET=$($c.DockerSocket) DOCKER_GID=$gid @@ -668,6 +673,9 @@ DOCKER_GID=$gid CAMELEER_SAAS_PROVISIONING_SERVERIMAGE=${REGISTRY}/cameleer-server:$($c.Version) CAMELEER_SAAS_PROVISIONING_SERVERUIIMAGE=${REGISTRY}/cameleer-server-ui:$($c.Version) CAMELEER_SAAS_PROVISIONING_RUNTIMEBASEIMAGE=${REGISTRY}/cameleer-runtime-base:$($c.Version) + +# JWT signing secret (forwarded to provisioned tenant servers, must be non-empty) +CAMELEER_SERVER_SECURITY_JWTSECRET=$jwtSecret "@ $content += $provisioningBlock $composeFile = 'docker-compose.yml;docker-compose.saas.yml' @@ -1033,10 +1041,10 @@ $logtoConsoleRow | Container | Purpose | |---|---| -| ``traefik`` | Reverse proxy, TLS termination, routing | -| ``postgres`` | PostgreSQL database (SaaS + Logto + tenant schemas) | -| ``clickhouse`` | Time-series storage (traces, metrics, logs) | -| ``logto`` | OIDC identity provider + bootstrap | +| ``cameleer-traefik`` | Reverse proxy, TLS termination, routing | +| ``cameleer-postgres`` | PostgreSQL database (SaaS + Logto + tenant schemas) | +| ``cameleer-clickhouse`` | Time-series storage (traces, metrics, logs) | +| ``cameleer-logto`` | OIDC identity provider + bootstrap | | ``cameleer-saas`` | SaaS platform (Spring Boot + React) | Per-tenant ``cameleer-server`` and ``cameleer-server-ui`` containers are provisioned dynamically. @@ -1157,11 +1165,11 @@ placing your certificate and key files in the ``certs/`` directory and restartin | Container | Purpose | |---|---| -| ``traefik`` | Reverse proxy, TLS termination, routing | -| ``postgres`` | PostgreSQL database (server data) | -| ``clickhouse`` | Time-series storage (traces, metrics, logs) | -| ``server`` | Cameleer Server (Spring Boot backend) | -| ``server-ui`` | Cameleer Dashboard (React frontend) | +| ``cameleer-traefik`` | Reverse proxy, TLS termination, routing | +| ``cameleer-postgres`` | PostgreSQL database (server data) | +| ``cameleer-clickhouse`` | Time-series storage (traces, metrics, logs) | +| ``cameleer-server`` | Cameleer Server (Spring Boot backend) | +| ``cameleer-server-ui`` | Cameleer Dashboard (React frontend) | ## Networking @@ -1203,7 +1211,7 @@ docker compose -p $($c.ComposeProject) exec cameleer-clickhouse clickhouse-clien | Issue | Command | |---|---| | Service not starting | ``docker compose -p $($c.ComposeProject) logs SERVICE_NAME`` | -| Server issues | ``docker compose -p $($c.ComposeProject) logs server`` | +| Server issues | ``docker compose -p $($c.ComposeProject) logs cameleer-server`` | | Routing issues | ``docker compose -p $($c.ComposeProject) logs cameleer-traefik`` | | Database issues | ``docker compose -p $($c.ComposeProject) exec cameleer-postgres psql -U cameleer -d cameleer`` | diff --git a/installer/install.sh b/installer/install.sh index 7027cfa..34ce17c 100644 --- a/installer/install.sh +++ b/installer/install.sh @@ -600,6 +600,9 @@ SERVER_ADMIN_PASS=${ADMIN_PASS} # Bootstrap token (required by server, not used externally in standalone mode) BOOTSTRAP_TOKEN=$(generate_password) +# JWT signing secret (required by server, must be non-empty) +CAMELEER_SERVER_SECURITY_JWTSECRET=$(generate_password) + # Docker DOCKER_SOCKET=${DOCKER_SOCKET} DOCKER_GID=$(stat -c '%g' "${DOCKER_SOCKET}" 2>/dev/null || echo "0") @@ -678,6 +681,9 @@ CAMELEER_SAAS_PROVISIONING_SERVERIMAGE=${REGISTRY}/cameleer-server:${VERSION} CAMELEER_SAAS_PROVISIONING_SERVERUIIMAGE=${REGISTRY}/cameleer-server-ui:${VERSION} CAMELEER_SAAS_PROVISIONING_RUNTIMEBASEIMAGE=${REGISTRY}/cameleer-runtime-base:${VERSION} +# JWT signing secret (forwarded to provisioned tenant servers, must be non-empty) +CAMELEER_SERVER_SECURITY_JWTSECRET=$(generate_password) + # Compose file assembly COMPOSE_FILE=docker-compose.yml:docker-compose.saas.yml$([ "$TLS_MODE" = "custom" ] && echo ":docker-compose.tls.yml")$([ -n "$MONITORING_NETWORK" ] && echo ":docker-compose.monitoring.yml") EOF @@ -951,10 +957,10 @@ EOF | Container | Purpose | |---|---| -| `traefik` | Reverse proxy, TLS termination, routing | -| `postgres` | PostgreSQL database (SaaS + Logto + tenant schemas) | -| `clickhouse` | Time-series storage (traces, metrics, logs) | -| `logto` | OIDC identity provider + bootstrap | +| `cameleer-traefik` | Reverse proxy, TLS termination, routing | +| `cameleer-postgres` | PostgreSQL database (SaaS + Logto + tenant schemas) | +| `cameleer-clickhouse` | Time-series storage (traces, metrics, logs) | +| `cameleer-logto` | OIDC identity provider + bootstrap | | `cameleer-saas` | SaaS platform (Spring Boot + React) | Per-tenant `cameleer-server` and `cameleer-server-ui` containers are provisioned dynamically when tenants are created. @@ -1093,11 +1099,11 @@ generate_install_doc_standalone() { | Container | Purpose | |---|---| -| \`traefik\` | Reverse proxy, TLS termination, routing | -| \`postgres\` | PostgreSQL database (server data) | -| \`clickhouse\` | Time-series storage (traces, metrics, logs) | -| \`server\` | Cameleer Server (Spring Boot backend) | -| \`server-ui\` | Cameleer Dashboard (React frontend) | +| \`cameleer-traefik\` | Reverse proxy, TLS termination, routing | +| \`cameleer-postgres\` | PostgreSQL database (server data) | +| \`cameleer-clickhouse\` | Time-series storage (traces, metrics, logs) | +| \`cameleer-server\` | Cameleer Server (Spring Boot backend) | +| \`cameleer-server-ui\` | Cameleer Dashboard (React frontend) | ## Networking @@ -1167,7 +1173,7 @@ The installer preserves your \`.env\`, credentials, and data volumes. Only the c | Issue | Command | |---|---| | Service not starting | \`docker compose -p ${COMPOSE_PROJECT} logs SERVICE_NAME\` | -| Server issues | \`docker compose -p ${COMPOSE_PROJECT} logs server\` | +| Server issues | \`docker compose -p ${COMPOSE_PROJECT} logs cameleer-server\` | | Routing issues | \`docker compose -p ${COMPOSE_PROJECT} logs cameleer-traefik\` | | Database issues | \`docker compose -p ${COMPOSE_PROJECT} exec cameleer-postgres psql -U cameleer -d cameleer\` | diff --git a/installer/templates/docker-compose.saas.yml b/installer/templates/docker-compose.saas.yml index af5d06e..e22a14f 100644 --- a/installer/templates/docker-compose.saas.yml +++ b/installer/templates/docker-compose.saas.yml @@ -77,6 +77,7 @@ services: CAMELEER_SAAS_PROVISIONING_DATASOURCEUSERNAME: ${POSTGRES_USER:-cameleer} CAMELEER_SAAS_PROVISIONING_DATASOURCEPASSWORD: ${POSTGRES_PASSWORD} CAMELEER_SAAS_PROVISIONING_CLICKHOUSEPASSWORD: ${CLICKHOUSE_PASSWORD} + CAMELEER_SERVER_SECURITY_JWTSECRET: ${CAMELEER_SERVER_SECURITY_JWTSECRET:?CAMELEER_SERVER_SECURITY_JWTSECRET must be set in .env} CAMELEER_SAAS_PROVISIONING_SERVERIMAGE: ${CAMELEER_SAAS_PROVISIONING_SERVERIMAGE:-gitea.siegeln.net/cameleer/cameleer-server:latest} CAMELEER_SAAS_PROVISIONING_SERVERUIIMAGE: ${CAMELEER_SAAS_PROVISIONING_SERVERUIIMAGE:-gitea.siegeln.net/cameleer/cameleer-server-ui:latest} CAMELEER_SAAS_PROVISIONING_RUNTIMEBASEIMAGE: ${CAMELEER_SAAS_PROVISIONING_RUNTIMEBASEIMAGE:-gitea.siegeln.net/cameleer/cameleer-runtime-base:latest} diff --git a/installer/templates/docker-compose.server.yml b/installer/templates/docker-compose.server.yml index 3e07b37..ff8f78b 100644 --- a/installer/templates/docker-compose.server.yml +++ b/installer/templates/docker-compose.server.yml @@ -29,6 +29,7 @@ services: CAMELEER_SERVER_CLICKHOUSE_USERNAME: default CAMELEER_SERVER_CLICKHOUSE_PASSWORD: ${CLICKHOUSE_PASSWORD} CAMELEER_SERVER_SECURITY_BOOTSTRAPTOKEN: ${BOOTSTRAP_TOKEN:?BOOTSTRAP_TOKEN must be set in .env} + CAMELEER_SERVER_SECURITY_JWTSECRET: ${CAMELEER_SERVER_SECURITY_JWTSECRET:?CAMELEER_SERVER_SECURITY_JWTSECRET must be set in .env} CAMELEER_SERVER_SECURITY_UIUSER: ${SERVER_ADMIN_USER:-admin} CAMELEER_SERVER_SECURITY_UIPASSWORD: ${SERVER_ADMIN_PASS:?SERVER_ADMIN_PASS must be set in .env} CAMELEER_SERVER_SECURITY_CORSALLOWEDORIGINS: ${PUBLIC_PROTOCOL:-https}://${PUBLIC_HOST:-localhost} diff --git a/src/main/java/net/siegeln/cameleer/saas/provisioning/CLAUDE.md b/src/main/java/net/siegeln/cameleer/saas/provisioning/CLAUDE.md index 7fa6e86..c7573ca 100644 --- a/src/main/java/net/siegeln/cameleer/saas/provisioning/CLAUDE.md +++ b/src/main/java/net/siegeln/cameleer/saas/provisioning/CLAUDE.md @@ -55,7 +55,7 @@ These env vars are injected into provisioned per-tenant server containers: | `CAMELEER_SERVER_CLICKHOUSE_PASSWORD` | (from provisioning config) | ClickHouse password | | `CAMELEER_SERVER_TENANT_ID` | `{slug}` | Tenant slug for data isolation | | `CAMELEER_SERVER_SECURITY_BOOTSTRAPTOKEN` | (license token) | Bootstrap auth token for M2M communication | -| `CAMELEER_SERVER_SECURITY_JWTSECRET` | (hardcoded dev value) | JWT signing secret (TODO: per-tenant generation) | +| `CAMELEER_SERVER_SECURITY_JWTSECRET` | (from env, installer-generated) | JWT signing secret (must be non-empty) | | `CAMELEER_SERVER_SECURITY_OIDC_ISSUERURI` | `${PUBLIC_PROTOCOL}://${PUBLIC_HOST}/oidc` | Token issuer claim validation | | `CAMELEER_SERVER_SECURITY_OIDC_JWKSETURI` | `http://cameleer-logto:3001/oidc/jwks` | Docker-internal JWK fetch | | `CAMELEER_SERVER_SECURITY_OIDC_TLSSKIPVERIFY` | `true` (conditional) | Skip cert verify for OIDC discovery; only set when no `/certs/ca.pem` exists. When ca.pem exists, the server's `docker-entrypoint.sh` imports it into the JVM truststore instead. |