The installer (install.sh, templates/, bootstrap scripts) now lives in
cameleer/cameleer-saas-installer (public repo). Added as a git submodule
at installer/ so compose templates remain the single source of truth.
Dev compose is now a thin overlay (ports + volume mount + dev env vars).
Production templates are chained via COMPOSE_FILE in .env:
installer/templates/docker-compose.yml
installer/templates/docker-compose.saas.yml
docker-compose.yml (dev overrides)
No code duplication — fixes to compose templates go to the installer
repo and propagate to both production deployments and dev via submodule.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Logto validates M2M tokens by fetching its own JWKS from the ENDPOINT
URL (e.g. https://app.cameleer.io/oidc/jwks). Behind a Cloudflare
tunnel, that hostname resolves to Cloudflare's IP and the container
can't route back through the tunnel — the fetch times out (ETIMEDOUT),
causing all Management API calls to return 500.
Adding extra_hosts maps AUTH_HOST to host-gateway so the request goes
to the Docker host, which has Traefik on :443, which routes back to
Logto internally. This hairpin works because NODE_TLS_REJECT=0 accepts
the self-signed cert.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Traefik auto-calculates router priority from rule string length. When
deployed with a domain longer than 23 chars (e.g. app.cameleer.io),
Host(`app.cameleer.io`) (25 chars) outranks PathPrefix(`/platform`)
(23 chars), causing ALL requests — including /platform/* — to route
to Logto instead of the SaaS app. This breaks login because the sign-in
UI loads without an OIDC interaction session.
Setting priority=1 makes Logto a true catch-all, matching the intent
documented in docker/CLAUDE.md.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Support separate auth domain (e.g. auth.cameleer.io) for Logto while
keeping the SaaS app on PUBLIC_HOST (e.g. app.cameleer.io). AUTH_HOST
defaults to PUBLIC_HOST for backward-compatible single-domain setups.
- Logto routing: Host(AUTH_HOST) replaces PathPrefix('/') catch-all
- Root redirect moved from traefik-dynamic.yml to Docker labels with
Host(PUBLIC_HOST) scope so it doesn't intercept auth domain
- Self-signed cert generates SANs for both domains
- Bootstrap Host header uses AUTH_HOST for Logto endpoint validation
- Spring issuer-uri and oidcissueruri use new authhost property
- Both installers (sh + ps1) prompt for AUTH_HOST in expert mode
Local dev: AUTH_HOST=auth.localhost (resolves to 127.0.0.1, no hosts file)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The server now requires a non-empty JWT secret. The installer (bash + ps1)
generates a random value for both SaaS and standalone modes, and the compose
templates map it into the respective containers. Also fixes container names
in generated INSTALL.md docs to use the cameleer- prefix consistently.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The compose volume `jars` gets created as `<project>_jars` by Docker
Compose, but JARDOCKERVOLUME tells the server to mount `cameleer-jars`
on deployed app containers. These are different Docker volumes, so
the app JAR was never visible inside the app container — causing
ClassNotFoundException on startup.
Fix: add `name: cameleer-jars` to the volume definition so both the
server and deployed app containers share the same named volume.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
CAMELEER_SERVER_RUNTIME_BASEIMAGE was never set on provisioned
per-tenant server containers, causing them to fall back to the
server's hardcoded default. Added CAMELEER_SAAS_PROVISIONING_RUNTIMEBASEIMAGE
as a configurable property that gets forwarded during provisioning.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Reference .env file documenting all configuration variables across both
deployment modes, with section headers for compose assembly, public access,
credentials, TLS, Docker, provisioning, and monitoring.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Optional compose overlays: TLS overlay mounts user-supplied certs into
traefik, monitoring overlay replaces the noop bridge with an external
Docker network for Prometheus scraping.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Standalone mode: server + server-ui services with postgres image override
to stock postgres:16-alpine. Includes traefik-dynamic.yml for default TLS
certificate store configuration.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Logto identity provider and cameleer-saas management plane services.
Includes Traefik labels, CORS config, bootstrap healthcheck, and all
provisioning env vars parameterized from .env.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>