11 KiB
Cameleer SaaS -- How to Install, Start & Bootstrap
Quick Start (Development)
# 1. Clone
git clone https://gitea.siegeln.net/cameleer/cameleer-saas.git
cd cameleer-saas
# 2. Create environment file
cp .env.example .env
# 3. Generate Ed25519 key pair
mkdir -p keys
ssh-keygen -t ed25519 -f keys/ed25519 -N ""
mv keys/ed25519 keys/ed25519.key
# 4. Start the stack
docker compose -f docker-compose.yml -f docker-compose.dev.yml up -d
# 5. Wait for services to be ready (~30s)
docker compose logs -f cameleer-saas --since 10s
# Look for: "Started CameleerSaasApplication"
# 6. Verify
curl http://localhost:8080/actuator/health
# {"status":"UP"}
Prerequisites
- Docker Desktop (Windows/Mac) or Docker Engine 24+ (Linux)
- Git
curlor any HTTP client (for testing)
Architecture
The platform runs as a Docker Compose stack with 6 services:
| Service | Image | Port | Purpose |
|---|---|---|---|
| traefik | traefik:v3 | 80, 443 | Reverse proxy, TLS, routing |
| postgres | postgres:16-alpine | 5432* | Platform database + Logto database |
| logto | ghcr.io/logto-io/logto | 3001*, 3002* | Identity provider (OIDC) |
| cameleer-saas | cameleer-saas:latest | 8080* | SaaS API server |
| cameleer3-server | cameleer3-server:latest | 8081 | Observability backend |
| clickhouse | clickhouse-server:latest | 8123* | Trace/metrics/log storage |
*Ports exposed to host only with docker-compose.dev.yml overlay.
Installation
1. Environment Configuration
cp .env.example .env
Edit .env and set at minimum:
# Change in production
POSTGRES_PASSWORD=<strong-password>
CAMELEER_AUTH_TOKEN=<random-string-for-agent-bootstrap>
CAMELEER_TENANT_SLUG=<your-tenant-slug> # e.g., "acme" — tags all observability data
# Logto M2M credentials (get from Logto admin console after first boot)
LOGTO_M2M_CLIENT_ID=
LOGTO_M2M_CLIENT_SECRET=
2. Ed25519 Keys
The platform uses Ed25519 keys for license signing and machine token verification.
mkdir -p keys
ssh-keygen -t ed25519 -f keys/ed25519 -N ""
mv keys/ed25519 keys/ed25519.key
This creates keys/ed25519.key (private) and keys/ed25519.pub (public). The keys directory is mounted read-only into the cameleer-saas container.
If no key files are configured, the platform generates ephemeral keys on startup (suitable for development only -- keys change on every restart).
3. Start the Stack
Development (ports exposed for direct access):
docker compose -f docker-compose.yml -f docker-compose.dev.yml up -d
Production (traffic routed through Traefik only):
docker compose up -d
4. Verify Services
# Health check
curl http://localhost:8080/actuator/health
# Check all containers are running
docker compose ps
Bootstrapping
First-Time Logto Setup
On first boot, Logto seeds its database automatically. Access the admin console to configure it:
- Open http://localhost:3002 (Logto admin console)
- Complete the initial setup wizard
- Create a Machine-to-Machine application:
- Go to Applications > Create Application > Machine-to-Machine
- Note the App ID and App Secret
- Assign the Logto Management API resource with all scopes
- Update
.env:LOGTO_M2M_CLIENT_ID=<app-id> LOGTO_M2M_CLIENT_SECRET=<app-secret> - Restart cameleer-saas:
docker compose restart cameleer-saas
Create Your First Tenant
With a Logto user token (obtained via OIDC login flow):
TOKEN="<your-logto-jwt>"
# Create tenant
curl -X POST http://localhost:8080/api/tenants \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "My Company", "slug": "my-company", "tier": "MID"}'
# A "default" environment is auto-created with the tenant
Generate a License
TENANT_ID="<uuid-from-above>"
curl -X POST "http://localhost:8080/api/tenants/$TENANT_ID/license" \
-H "Authorization: Bearer $TOKEN"
Deploy a Camel Application
# List environments
curl "http://localhost:8080/api/tenants/$TENANT_ID/environments" \
-H "Authorization: Bearer $TOKEN"
ENV_ID="<default-environment-uuid>"
# Upload JAR and create app
curl -X POST "http://localhost:8080/api/environments/$ENV_ID/apps" \
-H "Authorization: Bearer $TOKEN" \
-F 'metadata={"slug":"order-service","displayName":"Order Service"};type=application/json' \
-F "file=@/path/to/your-camel-app.jar"
APP_ID="<app-uuid-from-response>"
# Deploy (async -- returns 202 with deployment ID)
curl -X POST "http://localhost:8080/api/apps/$APP_ID/deploy" \
-H "Authorization: Bearer $TOKEN"
DEPLOYMENT_ID="<deployment-uuid>"
# Poll deployment status
curl "http://localhost:8080/api/apps/$APP_ID/deployments/$DEPLOYMENT_ID" \
-H "Authorization: Bearer $TOKEN"
# Status transitions: BUILDING -> STARTING -> RUNNING (or FAILED)
# View container logs
curl "http://localhost:8080/api/apps/$APP_ID/logs?limit=50" \
-H "Authorization: Bearer $TOKEN"
# Stop the app
curl -X POST "http://localhost:8080/api/apps/$APP_ID/stop" \
-H "Authorization: Bearer $TOKEN"
Enable Inbound HTTP Routing
If your Camel app exposes a REST endpoint, you can make it reachable from outside the stack:
# Set the port your app listens on (e.g., 8080 for Spring Boot)
curl -X PATCH "http://localhost:8080/api/environments/$ENV_ID/apps/$APP_ID/routing" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"exposedPort": 8080}'
Your app is now reachable at http://{app-slug}.{env-slug}.{tenant-slug}.{domain} (e.g., http://order-service.default.my-company.localhost). Traefik routes traffic automatically.
To disable routing, set exposedPort to null.
View the Observability Dashboard
The cameleer3-server React SPA dashboard is available at:
http://localhost/dashboard
This shows execution traces, route topology graphs, metrics, and logs for all deployed apps. Authentication is required (Logto OIDC token via forward-auth).
Check Agent & Observability Status
# Is the agent registered with cameleer3-server?
curl "http://localhost:8080/api/apps/$APP_ID/agent-status" \
-H "Authorization: Bearer $TOKEN"
# Returns: registered, state (ACTIVE/STALE/DEAD/UNKNOWN), routeIds
# Is the app producing observability data?
curl "http://localhost:8080/api/apps/$APP_ID/observability-status" \
-H "Authorization: Bearer $TOKEN"
# Returns: hasTraces, lastTraceAt, traceCount24h
API Reference
Tenants
| Method | Path | Description |
|---|---|---|
| POST | /api/tenants |
Create tenant |
| GET | /api/tenants/{id} |
Get tenant |
| GET | /api/tenants/by-slug/{slug} |
Get tenant by slug |
Licensing
| Method | Path | Description |
|---|---|---|
| POST | /api/tenants/{tid}/license |
Generate license |
| GET | /api/tenants/{tid}/license |
Get active license |
Environments
| Method | Path | Description |
|---|---|---|
| POST | /api/tenants/{tid}/environments |
Create environment |
| GET | /api/tenants/{tid}/environments |
List environments |
| GET | /api/tenants/{tid}/environments/{eid} |
Get environment |
| PATCH | /api/tenants/{tid}/environments/{eid} |
Rename environment |
| DELETE | /api/tenants/{tid}/environments/{eid} |
Delete environment |
Apps
| Method | Path | Description |
|---|---|---|
| POST | /api/environments/{eid}/apps |
Create app + upload JAR |
| GET | /api/environments/{eid}/apps |
List apps |
| GET | /api/environments/{eid}/apps/{aid} |
Get app |
| PUT | /api/environments/{eid}/apps/{aid}/jar |
Re-upload JAR |
| PATCH | /api/environments/{eid}/apps/{aid}/routing |
Set/clear exposed port |
| DELETE | /api/environments/{eid}/apps/{aid} |
Delete app |
Deployments
| Method | Path | Description |
|---|---|---|
| POST | /api/apps/{aid}/deploy |
Deploy app (async, 202) |
| GET | /api/apps/{aid}/deployments |
Deployment history |
| GET | /api/apps/{aid}/deployments/{did} |
Get deployment status |
| POST | /api/apps/{aid}/stop |
Stop current deployment |
| POST | /api/apps/{aid}/restart |
Restart app |
Logs
| Method | Path | Description |
|---|---|---|
| GET | /api/apps/{aid}/logs |
Query container logs |
Query params: since, until (ISO timestamps), limit (default 500), stream (stdout/stderr/both)
Observability
| Method | Path | Description |
|---|---|---|
| GET | /api/apps/{aid}/agent-status |
Agent registration status |
| GET | /api/apps/{aid}/observability-status |
Trace/metrics data health |
Dashboard
| Path | Description |
|---|---|
/dashboard |
cameleer3-server observability dashboard (forward-auth protected) |
Health
| Method | Path | Description |
|---|---|---|
| GET | /actuator/health |
Health check (public) |
| GET | /api/health/secured |
Authenticated health check |
Tier Limits
| Tier | Environments | Apps | Retention | Features |
|---|---|---|---|---|
| LOW | 1 | 3 | 7 days | Topology |
| MID | 2 | 10 | 30 days | + Lineage, Correlation |
| HIGH | Unlimited | 50 | 90 days | + Debugger, Replay |
| BUSINESS | Unlimited | Unlimited | 365 days | All features |
Development
Running Tests
# Unit tests only (no Docker required)
mvn test -B -Dsurefire.excludes="**/*ControllerTest.java,**/AuditRepositoryTest.java,**/CameleerSaasApplicationTest.java"
# Integration tests (requires Docker Desktop)
mvn test -B -Dtest="EnvironmentControllerTest,AppControllerTest,DeploymentControllerTest"
# All tests
mvn verify -B
Building Locally
# Build JAR
mvn clean package -DskipTests -B
# Build Docker image
docker build -t cameleer-saas:local .
# Use local image
VERSION=local docker compose -f docker-compose.yml -f docker-compose.dev.yml up -d
Troubleshooting
Logto fails to start: Check that PostgreSQL is healthy first. Logto needs the logto database created by docker/init-databases.sh. Run docker compose logs logto for details.
cameleer-saas won't start: Check docker compose logs cameleer-saas. Common issues:
- PostgreSQL not ready (wait for healthcheck)
- Flyway migration conflict (check for manual schema changes)
Ephemeral key warnings: No Ed25519 key files configured -- generating ephemeral keys (dev mode) is normal in development. For production, generate keys as described above.
Container deployment fails: Check that Docker socket is mounted (/var/run/docker.sock) and the cameleer-runtime-base image is available. Pull it with: docker pull gitea.siegeln.net/cameleer/cameleer-runtime-base:latest