Files
cameleer-saas/docs/superpowers/specs/2026-04-13-install-script-design.md
hsiegeln 16a2ff3174 Add install script design spec
Defines a professional installer for the Cameleer SaaS platform with
dual native scripts (bash + PowerShell), three installation modes
(simple/expert/silent), and a platform simplification that consolidates
7 services into 5 by baking all init logic into Docker images.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:37:23 +02:00

16 KiB

Cameleer SaaS Install Script Design

Overview

A professional installer for the Cameleer SaaS platform, distributed as two native scripts (install.sh for Linux, install.ps1 for Windows). The installer downloads nothing — it embeds compose templates and generates all configuration from user input. All service initialization logic is baked into Docker images, configured via environment variables.

Distribution model: curl -sfL https://install.cameleer.io | bash (Linux), irm https://install.cameleer.io/windows | iex (Windows).

Platform Simplification (Prerequisites)

The current architecture uses 7 services with 10+ bind-mounted config files. This design consolidates everything into 5 services with zero bind mounts (except Docker socket and optional user-supplied TLS certs).

Image Consolidation

Image Base Bakes in
cameleer-traefik traefik:v3 Static/dynamic Traefik config, cert generation entrypoint (openssl), self-signed cert logic
cameleer-postgres postgres:16-alpine init-databases.sh (creates cameleer_saas, logto databases)
cameleer-clickhouse clickhouse/clickhouse-server Init SQL (CREATE DATABASE cameleer), clickhouse-users.xml, clickhouse-config.xml (Prometheus metrics)
cameleer-logto ghcr.io/logto-io/logto Custom sign-in UI, bootstrap logic (app/user/role/scope creation), vendor seed (env-var gated). Replaces the separate logto-bootstrap init container.
cameleer-saas eclipse-temurin:21-jre-alpine Spring Boot app + React SPA (already exists, no changes)

All images published to gitea.siegeln.net/cameleer/.

Service Reduction

Before After
traefik-certs (init container) Merged into cameleer-traefik entrypoint
traefik cameleer-traefik
postgres + bind-mounted init script cameleer-postgres
clickhouse + 3 bind-mounted config files cameleer-clickhouse
logto cameleer-logto (with bootstrap)
logto-bootstrap (init container) Merged into cameleer-logto entrypoint
cameleer-saas + bind-mounted UI cameleer-saas

Result: 7 services → 5 services. 10+ bind-mounted files → 0.

Bootstrap Merge

The logto-bootstrap init container logic moves into cameleer-logto's entrypoint as an idempotent startup step:

  1. Logto starts and seeds its own database (npm run cli db seed -- --swe)
  2. Entrypoint runs bootstrap logic (create apps, users, roles, scopes, branding)
  3. Bootstrap checks for cached results in a Docker volume — skips if already done
  4. Writes logto-bootstrap.json to shared volume
  5. If VENDOR_SEED_ENABLED=true, creates vendor user and global role
  6. Logto server starts normally

The cameleer-saas service uses depends_on: logto (healthy) and reads bootstrap results from the shared volume on startup — same as today.

Installer Architecture

Distribution

  • Linux: curl -sfL https://install.cameleer.io | bash
  • Windows: irm https://install.cameleer.io/windows | iex

The scripts are self-contained. They embed docker-compose templates and generate all files locally. No secondary downloads.

Scripts

  • install.sh — Bash, targets Linux with Docker Engine
  • install.ps1 — PowerShell, targets Windows with Docker Desktop (WSL2 backend)

Both implement identical logic and produce identical output. They share a config file format (cameleer.conf) so configurations are portable between platforms.

Prerequisites

The installer checks (does not install) these prerequisites:

  • Docker Engine (Linux) or Docker Desktop (Windows) — minimum version TBD
  • Docker Compose v2 (docker compose subcommand)
  • openssl (Linux, for password generation) — PowerShell uses [System.Security.Cryptography.RandomNumberGenerator]
  • Ports 80, 443, 3002 are free (or custom ports if specified)
  • Docker socket accessible

If any prerequisite is missing, the script prints a clear error message with a link to installation instructions and exits.

Installation Modes

Simple Mode (default)

Asks 6 essential questions:

  1. Install directory (default: ./cameleer)
  2. Public hostname (auto-detected, default: localhost)
  3. Admin username (default: admin)
  4. Admin password (default: auto-generated)
  5. Use custom TLS certificates? (default: no → self-signed)
    • If yes: paths to cert.pem, key.pem, optional ca.pem
  6. Connect to a monitoring network? (default: none)

Everything else uses secure defaults. All passwords auto-generated.

Expert Mode (--expert or chosen at interactive prompt)

Adds these options, grouped by category:

Credentials:

  • PostgreSQL password (default: generated)
  • ClickHouse password (default: generated)
  • Vendor account enable + username + password

Networking:

  • HTTP port (default: 80)
  • HTTPS port (default: 443)
  • Logto admin console port (default: 3002)

Docker:

  • Image version/tag (default: latest)
  • Compose project name (default: cameleer-saas)
  • Docker socket path (auto-detected)

TLS:

  • CA bundle path
  • NODE_TLS_REJECT_UNAUTHORIZED setting

Logto:

  • Admin console external exposure (default: yes)

Silent Mode (--silent)

No interactive prompts. Uses defaults plus overrides.

Config precedence: CLI flags > environment variables > config file (--config) > defaults.

Configuration Reference

Config key CLI flag Env var Default Simple Expert
install_dir --install-dir CAMELEER_INSTALL_DIR ./cameleer yes yes
public_host --public-host PUBLIC_HOST auto-detect yes yes
public_protocol --public-protocol PUBLIC_PROTOCOL https no yes
admin_user --admin-user SAAS_ADMIN_USER admin yes yes
admin_password --admin-password SAAS_ADMIN_PASS generated yes yes
tls_mode --tls-mode TLS_MODE self-signed yes yes
cert_file --cert-file CERT_FILE none yes* yes
key_file --key-file KEY_FILE none yes* yes
ca_file --ca-file CA_FILE none no yes
monitoring_network --monitoring-network MONITORING_NETWORK none yes yes
postgres_password --postgres-password POSTGRES_PASSWORD generated no yes
clickhouse_password --clickhouse-password CLICKHOUSE_PASSWORD generated no yes
http_port --http-port HTTP_PORT 80 no yes
https_port --https-port HTTPS_PORT 443 no yes
logto_console_port --logto-console-port LOGTO_CONSOLE_PORT 3002 no yes
logto_console_exposed --logto-console-exposed LOGTO_CONSOLE_EXPOSED true no yes
vendor_enabled --vendor-enabled VENDOR_ENABLED false no yes
vendor_user --vendor-user VENDOR_USER vendor no yes
vendor_password --vendor-password VENDOR_PASS generated no yes
version --version CAMELEER_VERSION latest no yes
compose_project --compose-project COMPOSE_PROJECT cameleer-saas no yes
docker_socket --docker-socket DOCKER_SOCKET auto-detect no yes
node_tls_reject --node-tls-reject NODE_TLS_REJECT 0 (self-signed) / 1 (custom) no yes

* Only asked in simple mode if the user chooses custom TLS.

Config File Format (cameleer.conf)

# Cameleer installation config
# Generated by installer v1.0.0 on 2026-04-13

install_dir=./cameleer
public_host=cameleer.example.com
public_protocol=https
admin_user=my-admin
version=1.0.0
tls_mode=custom
https_port=443
monitoring_network=prometheus

Plain key=value, # comments. Portable between Linux and Windows.

Auto-Detection

The installer auto-detects sensible defaults:

Value Linux Windows
Public hostname hostname -f, reverse DNS of primary IP, fallback localhost [System.Net.Dns]::GetHostEntry, fallback localhost
Docker socket /var/run/docker.sock //./pipe/docker_engine
Port availability ss -tlnp or netstat check on 80, 443, 3002 Test-NetConnection on 80, 443, 3002
Existing install Check for cameleer.conf in install directory Same

Output Files

The installer generates the following in the install directory:

./cameleer/
  docker-compose.yml       # Generated from embedded template
  .env                     # All service configuration
  .env.bak                 # Snapshot of .env at install time
  cameleer.conf            # Installer config (for re-runs, cloning)
  credentials.txt          # All generated passwords in plain text
  INSTALL.md               # Tailored documentation
  certs/                   # Only if user supplies custom TLS certs
    cert.pem
    key.pem
    ca.pem

docker-compose.yml (generated)

The compose file is generated from a template embedded in the script, with values substituted from the user's configuration. Key characteristics:

  • All services use ${VARIABLE} references to .env
  • No bind mounts except Docker socket and optional certs/ directory
  • Shared volumes: pgdata, chdata, bootstrapdata, certs
  • Networks: cameleer (internal), cameleer-traefik (for dynamic tenant routing)
  • Optional external monitoring_network with Prometheus labels on services
  • Health checks on all services
  • depends_on with health conditions for startup ordering

credentials.txt

===========================================
 CAMELEER PLATFORM CREDENTIALS
 Generated: 2026-04-13 14:32:00 UTC

 SECURE THIS FILE AND DELETE AFTER NOTING
 THESE CREDENTIALS CANNOT BE RECOVERED
===========================================

Admin Console:  https://cameleer.example.com/platform/
Admin User:     my-admin
Admin Password: aB3x...generated...9Zq

PostgreSQL:     cameleer / Kx8m...generated...Wp2
ClickHouse:     default / Rm4n...generated...Ht7

Vendor User:    acme-admin  (not enabled)

Logto Console:  https://cameleer.example.com:3002

Printed to terminal once at the end of installation. Never displayed again on re-runs.

INSTALL.md (generated)

Tailored to the actual installation values. Sections:

  1. Installation Summary — version, date, mode, install directory
  2. Service URLs — platform UI, Logto admin console, API endpoint
  3. First Steps — log in as admin, create first tenant
  4. Architecture Overview — containers running, purpose of each
  5. Networking — ports, monitoring network, Docker networks
  6. TLS — self-signed or custom, cert location, how to replace via vendor UI
  7. Data & Backups — Docker volume names, backup commands (pg_dump, clickhouse-backup)
  8. Upgrading — re-run installer with --version, what gets preserved
  9. Troubleshooting — common issues with docker compose logs commands
  10. Uninstalling — clean removal steps

Password Generation

When no password is provided, the script generates cryptographically secure random passwords:

  • Linux: openssl rand -base64 24 (32 characters)
  • Windows: [System.Security.Cryptography.RandomNumberGenerator] → Base64

Passwords Generated

Credential Config key Consumers
PostgreSQL password postgres_password postgres, logto, cameleer-saas
ClickHouse password clickhouse_password clickhouse, cameleer-saas (tenant provisioning)
Admin password admin_password Logto admin user
Vendor password vendor_password Logto vendor user (only if enabled)

Credential Lifecycle

  1. Generated (or user-provided) during install
  2. Written to .env (consumed by Docker Compose)
  3. Written to credentials.txt in plain text
  4. Printed to terminal once at end of installation
  5. Never shown again — re-runs preserve existing credentials without displaying them

Monitoring Network Integration

When a monitoring network is configured (simple or expert mode):

  1. The script verifies the network exists via docker network inspect
    • If missing in interactive mode: asks whether to create it or skip
    • If missing in silent mode: creates it automatically
  2. The network is added as an external network in the generated docker-compose.yml
  3. Services are attached to it and labeled for Prometheus Docker SD:
cameleer-saas:
  labels:
    prometheus.io/scrape: "true"
    prometheus.io/port: "8080"
    prometheus.io/path: "/platform/actuator/prometheus"

cameleer-traefik:
  labels:
    prometheus.io/scrape: "true"
    prometheus.io/port: "8082"
    prometheus.io/path: "/metrics"

cameleer-clickhouse:
  labels:
    prometheus.io/scrape: "true"
    prometheus.io/port: "9363"
    prometheus.io/path: "/metrics"

No Prometheus configuration needed on the customer's side — Docker service discovery picks up the labels automatically.

Idempotent Re-run & Upgrade

Detection

The script checks for cameleer.conf in the install directory. If found, it's a re-run.

Interactive Re-run Menu

Existing Cameleer installation detected (v1.0.0)
  Install directory: ./cameleer
  Public host: cameleer.example.com

  [1] Upgrade to v1.1.0 (pull new images, update compose)
  [2] Reconfigure (re-run interactive setup, preserve data)
  [3] Reinstall (fresh install, WARNING: destroys data volumes)
  [4] Cancel

Re-run Behavior

Action Preserve Regenerate Pull images
Upgrade .env, cameleer.conf, credentials.txt, certs/, volumes docker-compose.yml, INSTALL.md yes (new version)
Reconfigure Data volumes, credentials.txt (unless passwords changed) .env, docker-compose.yml, cameleer.conf, INSTALL.md optional
Reinstall Nothing Everything yes

Silent Re-run

Defaults to upgrade. Override with --reconfigure or --reinstall.

Safety

  • Data volumes (pgdata, chdata, bootstrapdata) are never removed unless --reinstall is explicitly chosen
  • --reinstall requires double opt-in: --reinstall --confirm-destroy
  • The script never runs docker volume rm without this confirmation

Health Verification

After docker compose up -d, the script polls services in dependency order:

Step Service Check Timeout
1 PostgreSQL pg_isready via docker compose exec 120s
2 ClickHouse clickhouse-client query via docker compose exec 120s
3 Logto GET /oidc/.well-known/openid-configuration 120s
4 Bootstrap Check logto-bootstrap.json exists in volume 120s
5 Cameleer SaaS GET /platform/api/config 120s
6 Traefik GET https://{PUBLIC_HOST}/ (expect redirect) 120s

Polling interval: 5 seconds. Total timeout: 5 minutes.

Output

Verifying installation...
  [ok] PostgreSQL          ready (3s)
  [ok] ClickHouse          ready (5s)
  [ok] Logto               ready (18s)
  [ok] Bootstrap           complete (0s)
  [ok] Cameleer SaaS       ready (8s)
  [ok] Traefik routing     ready (1s)

Installation complete!

Failure

  • Failing service marked with [FAIL] and a hint (e.g., "check docker compose logs logto")
  • Remaining checks skipped
  • Stack left running for inspection
  • Script exits with code 1

Script Structure (both platforms)

main()
  parse_args()
  detect_existing_install()
  if existing → show_rerun_menu()
  check_prerequisites()
  auto_detect_defaults()
  select_mode()              # simple / expert / silent
  if interactive → run_prompts()
  merge_config()             # CLI > env > config file > defaults
  validate_config()
  generate_passwords()       # for any not provided
  if custom_certs → copy_certs()
  generate_env_file()
  generate_compose_file()
  write_config_file()        # cameleer.conf
  docker_compose_pull()
  docker_compose_up()
  verify_health()
  generate_credentials_file()
  generate_install_doc()
  print_credentials()
  print_summary()

Each function has a direct equivalent in both bash and PowerShell. The logic, prompts, and output are identical across platforms.