Rename Java packages from net.siegeln.cameleer3 to net.siegeln.cameleer,
update all references in workflows, Docker configs, docs, and bootstrap.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The log appender JAR was missing from the cameleer-runtime-base Docker
image, causing agent log forwarding to silently fail with "No supported
logging framework found, log forwarding disabled". This meant only
container stdout logs (source=container) were captured — no application
or agent logs reached ClickHouse.
CI now downloads the appender JAR from the Maven registry alongside the
agent JAR, and the Dockerfile COPYs it to /app/cameleer3-log-appender.jar
where the server's Docker entrypoint expects it (-Dloader.path for
Spring Boot, -cp for plain Java).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Installers now use `--pull always --force-recreate` on `docker compose up`
to ensure fresh images are used on every install/reinstall, preventing
stale containers from missing schema changes like db_password.
Fix VendorTenantServiceTest to expect two repository saves in provisioning
tests (one for dbPassword, one for final status).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Constructor gained an 11th parameter (TenantDatabaseService) but the
test was not updated, breaking CI compilation.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- TenantDatabaseService added to key classes
- TenantDataCleanupService now ClickHouse-only
- Per-tenant JDBC URL with currentSchema/ApplicationName in env vars table
- Provisioning flow updated with DB creation step
- Delete flow updated with schema+user drop
- Database migrations section reflects consolidated V001 baseline
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace 14 incremental migrations (V001-V015) with a single V001__init.sql
representing the final schema. Tables that were created and later dropped
(environments, api_keys, apps, deployments) are excluded.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
TenantDataCleanupService now handles only ClickHouse GDPR erasure;
the dropPostgresSchema private method is removed and the public method
renamed cleanupClickHouse(). VendorTenantService updated accordingly
with the TODO comment removed.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Inject TenantDatabaseService; call createTenantDatabase() at the start
of provisionAsync() (stores generated password on TenantEntity), and
dropTenantDatabase() in delete() before GDPR data erasure.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Per-tenant PG users and schemas for DB-level data isolation.
Each tenant server gets its own credentials and currentSchema/ApplicationName
JDBC parameters, aligned with server team's commit 7a63135.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Use Card and KpiStrip design system components, add database icons to
section headers, right-align numeric columns, replace text toggles with
chevron icons, and constrain max width to prevent ultra-wide stretching.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The server dashboard link in the sidebar footer is premature — tenant
servers may not be provisioned yet and the link target depends on org
context that isn't always available.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Same hardcoded dev credentials bug as InfrastructureService —
TenantDataCleanupService.dropPostgresSchema() used "cameleer"/"cameleer_dev"
instead of the provisioning properties, causing schema DROP to fail on
production installs during tenant deletion.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
pgConnection() had hardcoded dev credentials ("cameleer"/"cameleer_dev")
instead of using the provisioning properties, causing "password
authentication failed" on production installs where the password is
generated.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Three issues fixed:
1. Docker socket: use /var/run/docker.sock instead of Windows named pipe
(//./pipe/docker_engine) — Linux containers can't use named pipes.
2. FQDN detection: reverse-DNS lookup on host IPs to find the FQDN
instead of relying on GetHostEntry which returns bare hostname on
Windows machines with DNS-registered domain suffixes.
3. Reinstall path duplication: Push-Location/Pop-Location in the
reinstall handler used try/catch without finally, so Pop-Location
was skipped when docker compose wrote to stderr under
ErrorActionPreference=Stop. CWD stayed in the install dir, causing
the relative ./cameleer default to resolve to cameleer/cameleer.
4. Logto bootstrap: register admin-console redirect URIs and add the
admin user to Logto's internal organizations (t-default, t-admin)
with the admin role — both required for console login to work.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Use /dev/urandom instead of openssl rand for generating random
passwords. Available on all Linux/macOS systems without requiring
openssl to be installed.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The verify_health functions passed short service names (postgres,
clickhouse, server, logto) but the actual compose services are
prefixed with cameleer-. This caused docker compose ps -q to return
empty, so health was never read and checks always timed out.
Also renamed server/server-ui service definitions to
cameleer-server/cameleer-server-ui for consistency.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The admin user IS the platform admin — no separate vendor user needed.
The saas-vendor role is now always assigned to the admin user during
bootstrap. Removes VENDOR_ENABLED, VENDOR_USER, VENDOR_PASS from all
config, prompts, compose templates, and bootstrap script.
In multi-tenant mode: admin logs in with saas-admin credentials, gets
platform:admin scope via saas-vendor role, manages tenants directly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The cameleer3-server requires CAMELEER_SERVER_SECURITY_BOOTSTRAPTOKEN
at startup. In standalone mode nothing uses it externally, but the
server's SecurityBeanConfig validates it exists. Generate a random
token in the .env and pass it through.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Single-tenant installations now run the server directly without Logto
or the SaaS management plane. The installer generates a simpler compose
with 5 services: traefik, postgres, clickhouse, cameleer3-server, and
cameleer3-server-ui. Uses local auth (built-in admin), no OIDC.
Multi-tenant (vendor) mode is unchanged — full SaaS stack with Logto.
Changes:
- New DEPLOYMENT_MODE variable (standalone/saas) replaces TENANT_ORG_NAME
- generate_compose_file_standalone() for the 5-service compose
- Standalone traefik-dynamic.yml (no /platform/ redirect)
- Stock postgres:16-alpine (server creates schema via Flyway)
- Standalone health checks (server + UI instead of Logto + SaaS)
- Standalone credentials/docs generation
- Remove Phase 12b from bootstrap (no longer needed)
- Remove setup_single_tenant_record (no longer needed)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The bootstrap script runs before the SaaS app starts, but the tenants
table only exists after Flyway migrations run in the SaaS app. This
circular dependency caused Phase 12b's psql commands to fail under
set -e, crashing the Logto container on first install in single-tenant
mode.
Now the bootstrap only handles Logto-side setup (org, user roles, OIDC
redirect URIs), and the installer creates the tenant DB record after
verify_health confirms the SaaS app is up. Also makes docker_compose_up
tolerant of transient startup errors since verify_health is the real
health gate.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When the background Logto process exits during bootstrap, `kill $LOGTO_PID`
returns non-zero. Under `set -e`, this terminates the entrypoint before
reaching the production-mode restart, causing the container to error on
first startup and only recover via restart policy.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
TenantProvisionerAutoConfig already hardcodes the socket path via
.withDockerHost("unix:///var/run/docker.sock"). The env var was
redundant and not read by the Java Docker client.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Revert env_file approach — only pass the specific env vars the SaaS
app needs for its own database, identity, and tenant provisioning.
Organized into clear groups: Docker, SaaS database, Identity, and
Provisioning (passed to per-tenant servers).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Instead of explicitly listing every env var the SaaS container needs,
use env_file to pass the entire .env. This ensures all installer-
configured values (passwords, hosts, ports, etc.) are available for
current and future use by the SaaS app and its provisioning config.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The CLICKHOUSE_PASSWORD env var was set on the clickhouse container
but not passed to cameleer-saas. The provisioning properties defaulted
to 'cameleer_ch' instead of the installer-generated password, causing
tenant servers to fail ClickHouse authentication.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add datasourceUsername and datasourcePassword to test constructors
to match the updated record definition.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The new ProvisioningProperties record fields need defaults in
application.yml or Spring Boot fails to bind the configuration.
Defaults to POSTGRES_USER/POSTGRES_PASSWORD env vars with
fallback to cameleer/cameleer_dev.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The DockerTenantProvisioner hardcoded SPRING_DATASOURCE_USERNAME
and SPRING_DATASOURCE_PASSWORD as "cameleer" / "cameleer_dev".
With the installer generating random passwords, tenant servers
failed to connect to PostgreSQL.
Add datasourceUsername and datasourcePassword to ProvisioningProperties,
pass them from the compose env vars, and use them in the provisioner.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The Docker socket group varies by host (e.g., GID 1001 on WSL2).
Hardcoding group_add: ["0"] doesn't work when the socket is owned
by a different group. The installer now detects the socket GID at
install time via stat. The main docker-compose.yml uses a
configurable DOCKER_GID env var (defaults to 0).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The cameleer-saas service needs Docker socket access for tenant
provisioning. Add the socket bind mount, group_add for permissions,
and explicit DOCKER_HOST=unix:///var/run/docker.sock to prevent
the Java Docker client from falling back to TCP (which happens on
WSL2 + Docker Desktop when DOCKER_HOST leaks from the host env).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace docker compose exec commands with Docker's built-in health
status checks. The exec-based ClickHouse check hung because
clickhouse-client waits for the server during initialization.
Docker's healthcheck status is already configured in compose and
is more reliable. Logto + Bootstrap merged into one check since
the healthcheck includes the bootstrap.json file test.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Installer now asks deployment mode in simple mode:
- Multi-tenant vendor: creates saas-vendor role + assigns to admin
- Single tenant: asks for org name, creates Logto org + tenant record,
assigns admin as org owner
Reverts always-create-vendor-role — role is only created when vendor
mode is selected. TENANT_ORG_NAME env var passed to bootstrap for
single-tenant org creation.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The admin user needs platform:admin to create tenants via the vendor
console. Previously the saas-vendor role was only created when
VENDOR_SEED_ENABLED=true (for a separate vendor user). Now the role
is always created and assigned to the admin user. VENDOR_SEED_ENABLED
only controls creating the separate vendor user.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Moves hostname normalization into merge_config() so it applies
regardless of source (CLI flag, env var, config file, prompt,
auto-detect). Logto normalizes hostnames internally — case mismatch
causes JWT issuer validation failure (401).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- ClickHouse health check: use $CLICKHOUSE_PASSWORD directly instead
of extracting from .env via grep (nested quoting broke in eval)
- Normalize auto-detected hostname to lowercase (Windows returns
uppercase which causes OIDC issuer case mismatches)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When running inside the Logto container (BOOTSTRAP_LOCAL=true), the
bootstrap script skips Host and X-Forwarded-Proto headers on all curl
calls. This avoids issuer mismatches when Logto runs with localhost
endpoints during bootstrap mode. PUBLIC_HOST/PUBLIC_PROTOCOL remain
unchanged so redirect URIs are generated with the correct public values.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Start Logto with localhost endpoints so bootstrap can reach the
Management API without going through Traefik. After bootstrap
completes, restart Logto with the real public endpoints for
production use. This eliminates the Traefik race condition entirely.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The Management API requires the admin OIDC endpoint (ADMIN_ENDPOINT)
to be reachable. Since bootstrap now runs inside the Logto container
(not a separate container), Traefik may not have discovered the labels
yet. Wait for the admin endpoint to be routable before running bootstrap.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Newer Logto versions require `npm run cli db alteration deploy` after
seeding to apply schema migrations. Without this, Logto fails with
"relation systems does not exist".
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The Logto base image (ghcr.io/logto-io/logto:latest) is Alpine-based,
not Debian. Switch from apt-get to apk for installing bootstrap deps.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>