Replace absolute UNIQUE constraint on tenants.slug with a partial unique
index that excludes DELETED rows. This allows re-creating a tenant with
the same slug after deletion.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds CAMELEER_SERVER_SECURITY_INFRASTRUCTUREENDPOINTS=false to the env
var list injected into provisioned tenant server containers, disabling
the Database and ClickHouse admin endpoints (returns 404) on SaaS-
managed instances. The server defaults to true (standalone mode).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
architecture.md runtime/deployment section rewritten with correct
CAMELEER_SAAS_PROVISIONING_* and CAMELEER_SERVER_* env vars.
user-manual.md updated container resource env vars and removed
stale CAMELEER_TENANT_SLUG reference. HOWTO.md cleaned up.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Align DockerTenantProvisioner env vars with the server's new
cameleer.server.security.oidc.* namespace:
CAMELEER_SERVER_SECURITY_OIDC_ISSUERURI
CAMELEER_SERVER_SECURITY_OIDC_JWKSETURI
CAMELEER_SERVER_SECURITY_OIDC_AUDIENCE
CAMELEER_SERVER_SECURITY_OIDC_TLSSKIPVERIFY
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Move all SaaS configuration properties under the cameleer.saas.*
namespace with all-lowercase dot-separated names and mechanical env var
mapping. Aligns with the server (cameleer.server.*) and agent
(cameleer.agent.*) conventions.
Changes:
- Move cameleer.identity.* → cameleer.saas.identity.*
- Move cameleer.provisioning.* → cameleer.saas.provisioning.*
- Move cameleer.certs.* → cameleer.saas.certs.*
- Rename kebab-case properties to concatenated lowercase
- Update all env vars to CAMELEER_SAAS_* mechanical mapping
- Update DockerTenantProvisioner to pass CAMELEER_SERVER_* env vars
to provisioned server containers (matching server's new convention)
- Spring JWT config now derives from SaaS properties via cross-reference
- Clean up orphaned properties in application-local.yml
- Update docker-compose.yml, docker-compose.dev.yml, .env.example
- Update CLAUDE.md, HOWTO.md, architecture.md, user-manual.md
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Traefik v3 ignores tls.stores.default in the static config, causing it
to serve its auto-generated fallback cert instead of the platform cert.
Moving the default certificate store to the dynamic config (file
provider) fixes this — Traefik now serves the correct cert and also
picks up cert rotations without a restart.
This was the root cause of OIDC PKIX failures: the server imported the
CA into its JVM truststore, but Traefik was serving a different cert
entirely.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
PATCH /api/users/{id}/password, not /api/users/{id}. The general user
update endpoint rejected the password field with 422.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Restart only stops/starts existing containers with the same image. The new
upgrade action removes server + UI containers, force-pulls the latest
Docker images, then re-provisions (preserving app containers, volumes, and
networks). Available to both vendor (tenant detail) and tenant admin
(dashboard).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- POST /api/tenant/server/admin-password — resets server's built-in
admin password via M2M API call to the tenant's server
- Settings page: "Server Admin Password" card
- ServerApiClient.resetServerAdminPassword() calls server's password
reset endpoint with M2M token
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- POST /api/tenant/password — change own Logto password
- POST /api/tenant/team/{userId}/password — reset team member password
- Settings page: "Change Password" card with confirm field
- Team page: "Reset Password" button per member with inline form
- LogtoManagementClient.updateUserPassword() via Logto Management API
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
DockerTenantProvisioner.remove() now cleans up all tenant Docker resources:
containers (by cameleer.tenant label), env networks, tenant network, JAR volume.
TenantDataCleanupService drops the tenant's PostgreSQL schema and deletes all
ClickHouse data for GDPR compliance.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When no org is resolved, redirect to /tenant instead of the
non-existent /server/ path. Fixes login redirect loop.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When Docker containers have been removed (e.g. manual cleanup or image
update), restart now falls back to full re-provisioning instead of
failing with 404. Applies to both vendor and tenant portal restart.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Replace inline FileField and native <input type="file"> with
FileInput from @cameleer/design-system (drag-and-drop, icons, clear)
- Update CertificatesPage and SsoPage to use FileInput + FormField
- Fix /certs volume permissions (chmod 775) so cameleer user can write
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The self-signed bootstrap cert has no CA bundle, so newly created tenants
with ca_applied_at=NULL are not actually stale. Skip the count when the
active cert has hasCa=false.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Extend VendorTenantSummary with agentCount, environmentCount, and
agentLimit fields. Fetch counts in parallel using CompletableFuture
per tenant, only calling server API for ACTIVE tenants with RUNNING
servers. Agent limit extracted from license limits JSONB.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The dashboard was showing hardcoded zeroes for agent and environment usage.
Now fetches real counts via M2M API from the tenant's server.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
All private key writes now use writeAtomicRestricted which sets POSIX
owner-read/write permissions after writing. Gracefully skips on
non-POSIX filesystems (Windows dev).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Encrypted PKCS#8 private keys are decrypted during staging using the
provided password. The decrypted key is stored for Traefik (which needs
cleartext PEM). Unencrypted keys continue to work without a password.
- CertificateManager.stage() accepts optional keyPassword
- DockerCertificateManager handles EncryptedPrivateKeyInfo decryption
- UI: password field in upload form (vendor CertificatesPage)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Tenants can upload multiple CA certificates for enterprise SSO providers
that use private certificate authorities.
- New tenant_ca_certs table (V013) with PEM storage in DB
- Stage/activate/delete lifecycle per CA cert
- Aggregated ca.pem rebuild on activate/delete (atomic .wip swap)
- REST API: GET/POST/DELETE on /api/tenant/ca
- UI: CA Certificates section on SSO page with upload, activate, remove
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
RequireScope and LandingRedirect now wait for scopesReady flag before
evaluating, preventing the race where org-scoped tokens load before
global tokens and the vendor gets incorrectly redirected.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Truncate fingerprint with hover tooltip
- Remove duplicate warning icon in stale banner
- Style file inputs to match design system
- Bump grid min-width for better card spacing
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Certificate management (provider interface, lifecycle, bootstrap, UI)
- Async tenant provisioning with polling UX
- Server restart capability (vendor + tenant)
- Audit log actor name resolution from Logto
- SSO connector management, vendor audit page
- Updated API reference with all current endpoints
- Fixed architecture table (per-tenant containers are dynamic)
- Updated migration list through V012
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Vendor persona doesn't need "Open Server Dashboard" in sidebar footer.
Removed inline Fingerprint icon from Identity (Logto) menu item.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
AuditService now looks up username/name/email from Logto Management API
when actorEmail is null, with an in-memory cache to avoid repeated calls.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Tests now mock tenantRepository.findById() since provisionAsync re-loads
the tenant entity, and assert on the entity directly rather than the
return value of createAndProvision().
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Vendor: POST /api/vendor/tenants/{id}/restart (platform:admin scope)
Tenant: POST /api/tenant/server/restart (tenant:manage scope)
Both call TenantProvisioner.stop() then start() on the server + UI
containers. Restart button on vendor TenantDetailPage (Actions card)
and tenant TenantDashboardPage (Server card). Allowed in any status
including PROVISIONING.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Backend: extract Docker provisioning into @Async method so the API
returns immediately with status=PROVISIONING. The tenant record, Logto
org, admin user, and license are created synchronously; container
provisioning, health check, license push, and OIDC config happen in a
background thread.
Frontend: navigate to tenant detail page immediately after creation.
Detail page polls every 3s while status=PROVISIONING and shows a
spinner indicator. Toast notification when provisioning completes.
Fixes#52.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Update to @cameleer/design-system@0.1.40 where TopBar no longer depends
on GlobalFilterProvider or CommandPaletteProvider. Remove these
unnecessary provider wrappers from main.tsx. Fixes#53.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add LogtoManagementClient methods for SSO connector CRUD + org JIT
- Add TenantSsoService with tenant isolation (validates connector-org link)
- Add TenantSsoController at /api/tenant/sso with test endpoint
- Create SsoPage with provider selection, dynamic config form, test button
- Remove old OIDC config endpoints from tenant portal (server OIDC is
now platform-managed, set during provisioning)
- Sidebar: OIDC -> SSO with Shield icon
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Server now hardcodes Logto org scopes in the auth flow, so the
provisioner no longer needs to push them via OIDC config.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add /platform/ to SPA postLogoutRedirectUris in bootstrap (fixes#54)
- Use amber color + bold for active vendor sidebar items
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace JPQL @Query with dynamic SQL via JdbcTemplate to avoid
Hibernate null parameter type issues (bytea vs text). Conditionally
appends WHERE clauses only for non-null filters, matching the proven
pattern from cameleer3-server's PostgresAuditRepository.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Hibernate binds null String params as bytea, causing PostgreSQL
lower(bytea) error. Convert null search to empty string in service
layer, use empty-string check in JPQL instead of IS NULL.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>