- Delete environment/, app/, deployment/, runtime/ packages (source + tests)
- Delete apikey/ package (tied to environments, table will be dropped)
- Strip AsyncConfig to empty @EnableAsync (no more deploymentExecutor bean)
- Remove EnvironmentService dependency from TenantService
- Remove environment/app isolation from TenantIsolationInterceptor
- Remove environment seeding from BootstrapDataSeeder
- Refactor ServerApiClient to use LogtoConfig instead of RuntimeConfig
- Add server-endpoint property to LogtoConfig (was in RuntimeConfig)
- Remove runtime config section and multipart config from application.yml
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- ServerApiClient: use X-Cameleer-Protocol-Version: 1 (server expects "1", not "2")
- Disable Hibernate show-sql in dev profile (too verbose)
- CLAUDE.md: document deployment pipeline architecture, M2M server role in bootstrap,
runtime-base image in CI
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The default cameleer-runtime-base:latest has no registry prefix, so
Docker can't pull it. Use the full gitea.siegeln.net/cameleer/ path.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Logto's OIDC endpoint may respond before the Management API is fully
initialized. Add a retry loop that checks GET /api/roles returns valid
JSON before making any API calls. Fixes intermittent bootstrap failure
on cold starts with 'Cannot index string with string "name"'.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The @Bean named 'deploymentExecutor' (ThreadPoolTaskExecutor) collided
with the @Service class DeploymentExecutor. Rename the bean to
'deploymentTaskExecutor'.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The Alpine-based docker builder uses BusyBox grep which doesn't
support Perl regex (-P). Switch to sed for extracting the agent
version from Maven metadata XML.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The serverApiClient.isAvailable() stubbing in setUp() is unused by
the observability test, causing UnnecessaryStubbingException in CI.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1. Docker socket security: remove root group from Dockerfile, use
group_add in docker-compose.yml for runtime-only socket access
2. M2M server communication: create ServerApiClient using Logto
client_credentials grant with API resource scope. Add M2M server
role in bootstrap. Replace hacky admin/admin login in
AgentStatusService.
3. Async deployment: extract DeploymentExecutor as separate @Service
so Spring's @Async proxy works (self-invocation bypasses proxy).
Deploy now returns immediately, health check runs in background.
4. Bootstrap: M2M server role (cameleer-m2m-server) with server:admin
scope, idempotent creation outside the M2M app creation block.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Three fixes for the deployment pipeline:
1. Health check path: /health -> /cameleer/health (matches agent)
2. Container cleanup: stop AND remove old container before starting
new one, plus orphan cleanup by container name to prevent conflicts
3. Container status: read health.status instead of state.status so
waitForHealthy correctly detects the "healthy" state
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Docker Compose prefixes network names with the project name, so the
actual network is cameleer-saas_cameleer, not just cameleer. Pass
CAMELEER_DOCKER_NETWORK env var using COMPOSE_PROJECT_NAME.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The httpclient5 transport needs junixsocket for Unix domain sockets.
Switch to docker-java-transport-zerodep which has built-in Unix socket
support with zero external dependencies.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The mounted /var/run/docker.sock is owned by root:root with rw-rw----
permissions. The cameleer user needs to be in the root group to
read/write the socket for building images and managing containers.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Default docker-java config resolved to localhost:2375 (TCP) inside the
container. Explicitly set docker host to unix:///var/run/docker.sock
which is volume-mounted from the host.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Check for spaClientId and m2mClientSecret in the cached bootstrap
file. If both exist, exit immediately instead of re-running all
phases. Delete /data/logto-bootstrap.json to force a re-run.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add CI step to build cameleer-runtime-base image by downloading the
agent shaded JAR from Gitea Maven registry and pushing the image.
Wire CAMELEER_AUTH_TOKEN from docker-compose into RuntimeConfig so
deployed containers authenticate with cameleer3-server. Add agent.jar
to gitignore for local builds.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Spring Boot defaults to 1MB max file size which rejected all JAR
uploads. Set to 200MB to match the configured max-jar-size. Also
create /data/jars with cameleer user ownership in the Dockerfile
so the non-root process can write uploaded JARs.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add per-app memory limit and CPU shares (stored on AppEntity, used by
DeploymentService with fallback to global defaults). JAR upload is now
optional at creation time. Both create modals show the computed slug in
the dialog title and use consistent Cancel-left/Action-right button
layout with inline styles to avoid Modal CSS conflicts.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Read user profile from Logto ID token in OrgResolver, store in
Zustand org store, display in sidebar footer and TopBar avatar
- Fix license limits showing "—" by aligning frontend LIMIT_LABELS
keys with backend snake_case convention (max_agents, retention_days,
max_environments)
- Bump @cameleer/design-system to v0.1.38 (font-size floor)
- Add dev volume mount for local UI hot-reload without image rebuild
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Change rolesClaim from "scope" to "roles" to match the custom claim
injected by the Logto Custom JWT script
- Add Phase 7b: configure Logto Custom JWT for access tokens that maps
org roles (admin→server:admin, member→server:viewer) and global roles
(platform-admin→server:admin) into a standard "roles" claim
- Add additionalScopes field to OIDC config
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Traefik couldn't auto-link the logto router when two services
(logto, logto-console) exist on the same container. This broke
ALL default tenant routing (sign-in, OIDC, API).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The admin console (port 3002) calls the Management API on the
default tenant (port 443). Add Traefik CORS headers to allow
cross-origin requests from the admin console origin.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ADMIN_ENDPOINT is now HTTPS so admin-tenant calls need the
forwarded proto header. Default-tenant calls stay unchanged.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ADMIN_ENDPOINT set to HTTPS so OIDC issuer matches browser URL.
NODE_TLS_REJECT_UNAUTHORIZED=0 lets Logto's internal ky-based
OIDC self-discovery accept the self-signed cert through Traefik.
Remove in production with real certs.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Admin console HTTPS via Traefik conflicts with Logto's
ADMIN_ENDPOINT self-discovery. Parking this for now.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ADMIN_ENDPOINT=http://localhost:3002 for Logto self-calls.
TRUST_PROXY_HEADER makes Logto use X-Forwarded-Proto from Traefik
to generate HTTPS URLs for browser-facing OIDC flows.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add PUBLIC_HOST as network alias on the logto container so its
internal ADMIN_ENDPOINT calls (http://PUBLIC_HOST:3002) resolve
inside Docker directly, bypassing Traefik. Browser traffic goes
through Traefik on host port 3002 with TLS termination.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove tls=true from the logto-console router so the entrypoint
accepts plain HTTP. Logto's internal self-calls via ADMIN_ENDPOINT
use HTTP and pass through Traefik transparently. Browsers can
access via HTTP on port 3002.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Use separate port 3443 for TLS-terminated admin console access.
Port 3002 stays directly mapped from logto in dev for Logto's
internal OIDC self-discovery via ADMIN_ENDPOINT.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Traefik-only change: new entrypoint + router for TLS termination.
No changes to Logto ADMIN_ENDPOINT or bootstrap script.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Revert all Traefik port 3002 and ADMIN_ENDPOINT changes that broke
bootstrap. Admin console HTTPS access needs a different approach.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ADMIN_ENDPOINT is http://localhost:3002, but bootstrap sent
Host: PUBLIC_HOST:3002 which didn't match. Let curl use the
default Host from LOGTO_ADMIN_ENDPOINT (logto:3002) which Logto
resolves to the admin tenant internally.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Logto calls ADMIN_ENDPOINT internally for OIDC discovery. Using
PUBLIC_HOST resolved to the host machine where Traefik now owns
port 3002, causing a routing loop. localhost resolves inside the
container directly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Default tenant (port 3001) works without it — adding it caused
Internal server error. Only the admin tenant needs it because
ADMIN_ENDPOINT changed to HTTPS.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
All Logto endpoints are configured with HTTPS but bootstrap calls
internal HTTP. Every curl call needs the forwarded proto header.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Logto's ADMIN_ENDPOINT is now HTTPS but bootstrap calls the internal
HTTP endpoint directly. TRUST_PROXY_HEADER needs X-Forwarded-Proto
to resolve the correct scheme.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add admin-console entrypoint to Traefik with TLS termination.
Route port 3002 through Traefik to logto:3002. Update Logto
ADMIN_ENDPOINT to use HTTPS.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Admin tenant defaults to Register mode (onboarding flow). Since we
create the admin user via API, we need to switch to SignIn mode so
the custom sign-in UI can authenticate against the admin console.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The admin tenant requires both the 'user' role (base access) and
'default:admin' role (Management API). Missing the 'user' role
causes a 403 at the identification step.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The admin console runs on a separate tenant with its own user store.
Previous approach tried to assign a non-existent 'admin:admin' role
on the default tenant. Now creates the user on the admin tenant via
port 3002, assigns 'default:admin' role for Management API access,
and adds to t-default organization.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace --no-cache with --cache-from/--cache-to registry caching,
matching the cameleer3-server CI pattern. The ephemeral CI runner
destroys BuildKit local cache after each job, so only registry
caching persists between runs.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Sidebar, sign-in page, and favicons all use the single SVG
- Postinstall copies SVG for SaaS HTML favicon (gitignored)
- Sign-in favicon committed (baked into Logto Docker image)
- Remove old PNG favicon references
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The Dockerfile copies package.json before ui/ contents, so public/
doesn't exist during npm ci. Skip the copy gracefully.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>