diff --git a/.env.example b/.env.example index c2f89be..319293e 100644 --- a/.env.example +++ b/.env.example @@ -25,11 +25,10 @@ POSTGRES_DB=cameleer_saas CLICKHOUSE_PASSWORD=change_me_in_production # Admin user (created by bootstrap) -# Email is the primary user identity in SaaS mode. The admin email defaults -# to @ if not set explicitly. -SAAS_ADMIN_USER=admin +# In SaaS mode, this must be an email address (primary user identity). +# In standalone mode, any username is accepted. +SAAS_ADMIN_USER=admin@example.com SAAS_ADMIN_PASS=change_me_in_production -# SAAS_ADMIN_EMAIL=admin@example.com # SMTP / email connector configuration is managed at runtime via the vendor # admin UI (Email Connector page at /vendor/email). No SMTP env vars needed. diff --git a/CLAUDE.md b/CLAUDE.md index 4d00cf6..6d3f6c7 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -6,7 +6,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co Cameleer SaaS — **vendor management plane** for the Cameleer observability stack. Three personas: **vendor** (platform:admin) manages the platform and provisions tenants; **tenant admin** (tenant:manage) manages their observability instance; **new user** (authenticated, no scopes) goes through self-service onboarding. Tenants can be created by the vendor OR via self-service sign-up (email registration + onboarding wizard). Each tenant gets per-tenant cameleer-server + UI instances via Docker API. -**Email is the primary user identity** in SaaS mode. All users — including the admin — must have an email address. The admin email defaults to `@` if not set explicitly via `SAAS_ADMIN_EMAIL`. Self-service registration (email + password + verification code) is disabled by default and enabled via the vendor UI after configuring an email connector. +**Email is the primary user identity** in SaaS mode. `SAAS_ADMIN_USER` IS the email address — there is no separate `SAAS_ADMIN_EMAIL`. The installer enforces email format in SaaS mode (must contain `@`; auto-appends `@` if missing). The bootstrap uses `SAAS_ADMIN_USER` as both the Logto username and primaryEmail. In standalone mode, any username is accepted. Self-service registration (email + password + verification code) is disabled by default and enabled via the vendor UI after configuring an email connector. ## Ecosystem diff --git a/docker/CLAUDE.md b/docker/CLAUDE.md index 418d81e..e09483e 100644 --- a/docker/CLAUDE.md +++ b/docker/CLAUDE.md @@ -80,7 +80,7 @@ Idempotent script run inside the Logto container entrypoint. **Clean slate** — 3. Create Logto apps (SPA, Traditional Web App with `skipConsent`, M2M with Management API role + server API role) 3b. Create API resource scopes (1 platform + 9 tenant + 3 server scopes) 4. Create org roles (owner, operator, viewer with API resource scope assignments) + M2M server role (`cameleer-m2m-server` with `server:admin` scope) -5. Create admin user (SaaS admin with Logto console access). Email is the primary user identity in SaaS mode — admin email defaults to `@` if `SAAS_ADMIN_EMAIL` is not set. +5. Create admin user (SaaS admin with Logto console access). `SAAS_ADMIN_USER` is the admin's email address in SaaS mode — used as both the Logto username and primaryEmail. No separate `SAAS_ADMIN_EMAIL`. 7b. Configure Logto Custom JWT for access tokens (maps org roles -> `roles` claim: owner->server:admin, operator->server:operator, viewer->server:viewer; saas-vendor global role -> server:admin) 8. Configure Logto sign-in branding (Cameleer colors `#C6820E`/`#D4941E`, logo from `/platform/logo.svg`) 8c. Configure sign-in experience (sign-in only) — sets `signInMode: "SignIn"` with username+password method. Registration is disabled by default; the vendor admin enables it via the Email Connector UI after configuring SMTP delivery. diff --git a/docker/logto-bootstrap.sh b/docker/logto-bootstrap.sh index 5f56d2c..3d4dfca 100644 --- a/docker/logto-bootstrap.sh +++ b/docker/logto-bootstrap.sh @@ -27,9 +27,8 @@ API_RESOURCE_NAME="Cameleer SaaS API" # Users (configurable via env vars) SAAS_ADMIN_USER="${SAAS_ADMIN_USER:-admin}" SAAS_ADMIN_PASS="${SAAS_ADMIN_PASS:-admin}" -# Admin email: use provided value, or derive from username@host. -# SaaS enforces email as the user identity — admin must have one. -SAAS_ADMIN_EMAIL="${SAAS_ADMIN_EMAIL:-${SAAS_ADMIN_USER}@${PUBLIC_HOST:-localhost}}" +# In SaaS mode, SAAS_ADMIN_USER is the admin's email address. +# Use it as both username and primaryEmail in Logto. # No server config — servers are provisioned dynamically by the admin console @@ -397,12 +396,12 @@ ADMIN_USER_ID=$(api_get "/api/users?search=$SAAS_ADMIN_USER" | jq -r ".[] | sele if [ -n "$ADMIN_USER_ID" ]; then log "Platform owner exists: $ADMIN_USER_ID" else - log "Creating platform owner '$SAAS_ADMIN_USER' (email: $SAAS_ADMIN_EMAIL)..." + log "Creating platform owner '$SAAS_ADMIN_USER'..." ADMIN_RESPONSE=$(api_post "/api/users" "{ \"username\": \"$SAAS_ADMIN_USER\", \"password\": \"$SAAS_ADMIN_PASS\", \"name\": \"Platform Owner\", - \"primaryEmail\": \"$SAAS_ADMIN_EMAIL\" + \"primaryEmail\": \"$SAAS_ADMIN_USER\" }") ADMIN_USER_ID=$(echo "$ADMIN_RESPONSE" | jq -r '.id') log "Created platform owner: $ADMIN_USER_ID" diff --git a/docs/user-manual.md b/docs/user-manual.md index 8fd0aea..ef2b3c8 100644 --- a/docs/user-manual.md +++ b/docs/user-manual.md @@ -441,9 +441,8 @@ Copy `.env.example` to `.env` and configure as needed: | `CAMELEER_SAAS_IDENTITY_SPACLIENTID` | SPA client ID for the frontend | _(empty)_ | | `PUBLIC_HOST` | Public hostname for Traefik, Logto, and SaaS routing | `localhost` | | `PUBLIC_PROTOCOL` | Public protocol (`http` or `https`) | `https` | -| `SAAS_ADMIN_USER` | Platform admin username | `admin` | +| `SAAS_ADMIN_USER` | Platform admin login (must be an email in SaaS mode) | `admin` | | `SAAS_ADMIN_PASS` | Platform admin password | `admin` | -| `SAAS_ADMIN_EMAIL` | Platform admin email (primary identity) | `@` | | `TENANT_ADMIN_USER` | Tenant admin username | `camel` | | `TENANT_ADMIN_PASS` | Tenant admin password | `camel` | diff --git a/installer b/installer index 0da2616..21ea951 160000 --- a/installer +++ b/installer @@ -1 +1 @@ -Subproject commit 0da26160c640b3e99651eba743d3a16e95183e16 +Subproject commit 21ea9515a2e0a8e2deabf637331c1fdf62faf147 diff --git a/src/main/java/net/siegeln/cameleer/saas/config/CLAUDE.md b/src/main/java/net/siegeln/cameleer/saas/config/CLAUDE.md index f23a93b..46c3158 100644 --- a/src/main/java/net/siegeln/cameleer/saas/config/CLAUDE.md +++ b/src/main/java/net/siegeln/cameleer/saas/config/CLAUDE.md @@ -2,7 +2,7 @@ ## User identity -**Email is the primary user identity** in SaaS mode. All users must have an email address — Logto enforces this via `signUp.identifiers: ["email"]` when registration is enabled. The bootstrap creates the admin user with `primaryEmail` set to `SAAS_ADMIN_EMAIL` (defaults to `@`). Self-service registration requires email verification via a configured email connector (vendor UI at `/vendor/email`). +**Email is the primary user identity** in SaaS mode. All users must have an email address — Logto enforces this via `signUp.identifiers: ["email"]` when registration is enabled. `SAAS_ADMIN_USER` IS the email address (no separate `SAAS_ADMIN_EMAIL`). The bootstrap creates the admin user with `SAAS_ADMIN_USER` as both username and `primaryEmail`. The installer enforces email format in SaaS mode. Self-service registration requires email verification via a configured email connector (vendor UI at `/vendor/email`). ## Auth enforcement diff --git a/ui/sign-in/src/SignInPage.tsx b/ui/sign-in/src/SignInPage.tsx index 2bd2312..b0576fa 100644 --- a/ui/sign-in/src/SignInPage.tsx +++ b/ui/sign-in/src/SignInPage.tsx @@ -186,7 +186,7 @@ export function SignInPage() { {/* --- Sign-in form --- */} {mode === 'signIn' && (
- +