Commit Graph

175 Commits

Author SHA1 Message Date
hsiegeln
edd1d45a1a docs: Logto admin credentials + branding design spec
All checks were successful
CI / build (push) Successful in 47s
CI / docker (push) Successful in 8s
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 10:24:52 +02:00
hsiegeln
574c719148 docs: server role mapping design spec
All checks were successful
CI / build (push) Successful in 1m6s
CI / docker (push) Successful in 10s
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 10:05:12 +02:00
hsiegeln
0082576063 docs: update architecture docs for single-domain /platform routing
All checks were successful
CI / build (push) Successful in 38s
CI / docker (push) Successful in 10s
Reflects current state: path-based routing, SaaS at /platform,
Logto catch-all, TLS init container, server integration env vars,
custom JwtDecoder for ES384, skip consent for SSO.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 09:43:14 +02:00
hsiegeln
5a8d38a946 fix: enable skip consent on Traditional app for first-party SSO
All checks were successful
CI / build (push) Successful in 1m3s
CI / docker (push) Successful in 8s
SonarQube Analysis / sonarqube (push) Successful in 1m24s
Without this, Logto returns consent_required when the server tries
SSO because the scopes were never explicitly granted.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 01:30:25 +02:00
hsiegeln
d74aafc7b3 fix: update Traditional app redirect URIs for Traefik routing
All checks were successful
CI / build (push) Successful in 46s
CI / docker (push) Successful in 8s
Server OIDC callback is at /oidc/callback (without /server/ prefix due
to strip-prefix). Register both variants until server reads
X-Forwarded-Prefix.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 01:10:40 +02:00
hsiegeln
329f5b80df feat: add CORS allowed origins for server behind reverse proxy
All checks were successful
CI / build (push) Successful in 45s
CI / docker (push) Successful in 7s
Browser sends Origin header on fetch calls even same-origin. Server
needs the public host in its CORS allowlist. Derived from PUBLIC_HOST.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 00:40:00 +02:00
hsiegeln
e16094d83f feat: enable OIDC TLS skip-verify for server in Docker dev
All checks were successful
CI / build (push) Successful in 46s
CI / docker (push) Successful in 7s
Self-signed certs cause PKIX errors when the server fetches OIDC
discovery. CAMELEER_OIDC_TLS_SKIP_VERIFY=true disables cert
verification for OIDC calls only (server-team feature, pending build).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 00:24:22 +02:00
hsiegeln
730ead38a0 fix: add strip-prefix back for server-ui asset serving
All checks were successful
CI / build (push) Successful in 46s
CI / docker (push) Successful in 6s
Nginx needs to see /assets/... not /server/assets/... to find the files.
Strip-prefix + BASE_PATH=/server now works correctly with the fixed image.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 23:59:16 +02:00
hsiegeln
5ded08cace fix: remove patched entrypoint, use server team's fixed image
Some checks failed
CI / build (push) Successful in 50s
CI / docker (push) Has been cancelled
Server team fixed the BASE_PATH sed ordering bug. Remove our entrypoint
override and let the image's own entrypoint handle it.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 23:58:20 +02:00
hsiegeln
5981a3db71 fix: patch server-ui entrypoint to fix sed ordering bug
All checks were successful
CI / build (push) Successful in 1m19s
CI / docker (push) Successful in 9s
The server-ui's entrypoint inserts <base href> THEN rewrites all
href="/" — including the just-inserted base tag, causing doubling.
Patched entrypoint rewrites asset paths first, then inserts <base>.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 23:53:03 +02:00
hsiegeln
4c6625efaa fix: restore BASE_PATH=/server, remove strip-prefix
Server-ui needs BASE_PATH for React Router basename. Without strip-prefix,
no X-Forwarded-Prefix doubling. Server-ui handles full /server/ path itself.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 23:40:11 +02:00
hsiegeln
9bd8ddfad5 fix: remove BASE_PATH, let X-Forwarded-Prefix from strip-prefix handle it
All checks were successful
CI / build (push) Successful in 41s
CI / docker (push) Successful in 6s
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 23:38:20 +02:00
hsiegeln
a700d3a8ed fix: add strip-prefix back to server-ui route
All checks were successful
CI / build (push) Successful in 41s
CI / docker (push) Successful in 6s
Server-ui injects BASE_PATH=/server/ into <base href>. Without strip-prefix,
Traefik forwards /server/ path AND server-ui adds /server/ again = double prefix.
Strip /server before forwarding so server-ui sees / and produces correct <base href="/server/">.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 23:33:57 +02:00
hsiegeln
1b2c962261 fix: root → /platform/ redirect via Traefik file config
All checks were successful
CI / build (push) Successful in 40s
CI / docker (push) Successful in 5s
Docker-compose label escaping mangles regex patterns. Use a separate
Traefik dynamic config file instead — clean regex, proper 302 redirect.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 23:30:38 +02:00
hsiegeln
43967dcf2e fix: add /platform prefix to signIn/signOut redirect URIs
All checks were successful
CI / build (push) Successful in 42s
CI / docker (push) Successful in 41s
LoginPage and useAuth used window.location.origin without the /platform
base path, causing redirect_uri mismatch with Logto's registered URIs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 23:25:53 +02:00
hsiegeln
5a847e075c fix: remove root redirect, /platform/ is the entry point
All checks were successful
CI / build (push) Successful in 42s
CI / docker (push) Successful in 5s
Server-side path rewrite breaks React Router (browser URL stays at /
but basename is /platform). The SPA entry point is /platform/ — users
bookmark that. Root / goes to Logto catch-all which is correct for
direct OIDC flows.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 23:21:53 +02:00
hsiegeln
bbace4698f fix: use replacepathregex (path-only) instead of redirectregex (full URL)
All checks were successful
CI / build (push) Successful in 41s
CI / docker (push) Successful in 5s
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 23:19:28 +02:00
hsiegeln
e5836bb9d5 fix: Go regexp replacement syntax ($1 not ${1})
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 23:18:04 +02:00
hsiegeln
8a59c23266 fix: capture group in redirectregex for root redirect
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 23:17:14 +02:00
hsiegeln
4f4d9777ce fix: use replacepath middleware for root → /platform/ rewrite
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 23:16:26 +02:00
hsiegeln
e3921576e5 fix: add explicit priority and broader regex for root redirect
All checks were successful
CI / build (push) Successful in 42s
CI / docker (push) Successful in 5s
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 23:15:02 +02:00
hsiegeln
d32a03bb7b fix: redirect root / to /platform/ for better UX
All checks were successful
CI / build (push) Successful in 42s
CI / docker (push) Successful in 6s
Users hitting the root URL now get redirected to the SaaS app instead
of seeing Logto's unknown-session page.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 23:13:20 +02:00
hsiegeln
4997f7a6a9 feat: move SaaS app to /platform base path, Logto becomes catch-all
All checks were successful
CI / build (push) Successful in 48s
CI / docker (push) Successful in 41s
Eliminates all Logto path enumeration in Traefik. Routing is now:
- /platform/* → cameleer-saas (SPA + API)
- /server/* → server-ui
- /* (catch-all) → Logto (sign-in, OIDC, assets, everything)

Spring context-path handles backend prefix transparently. No changes
needed in controllers, SecurityConfig, or interceptors.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 23:06:41 +02:00
hsiegeln
4ab72425ae fix: route Logto experience API paths (/api/interaction, /api/experience)
All checks were successful
CI / build (push) Successful in 41s
CI / docker (push) Successful in 6s
Logto's sign-in form calls /api/interaction/* and /api/experience/* to
submit credentials, but these were routed to cameleer-saas by the /api
catch-all. Added explicit Logto API paths with higher Traefik priority.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 22:52:12 +02:00
hsiegeln
191be6ab40 fix: route Logto sign-in experience paths through Traefik
All checks were successful
CI / build (push) Successful in 40s
CI / docker (push) Successful in 6s
Logto uses root-level paths for its sign-in UI (/sign-in, /register,
/consent, /single-sign-on, /social, /unknown-session) that were falling
through to the SPA catch-all and getting 401.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 22:47:39 +02:00
hsiegeln
bc384a6d2d fix: permit /_app/** static assets in SecurityConfig
All checks were successful
CI / build (push) Successful in 40s
CI / docker (push) Successful in 31s
SPA assets moved from /assets/ to /_app/ for single-domain routing,
but SecurityConfig still permitted the old path.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 22:40:41 +02:00
hsiegeln
28a90f5fc7 fix: add BASE_PATH=/server/ to server-ui, remove strip prefix
All checks were successful
CI / build (push) Successful in 41s
CI / docker (push) Successful in 6s
Server-ui now handles base path natively via BASE_PATH env var.
Traefik forwards full path without stripping.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 22:38:37 +02:00
hsiegeln
9568e7f127 feat: single-domain path-based routing (no subdomains required)
All checks were successful
CI / build (push) Successful in 46s
CI / docker (push) Successful in 41s
Move SPA assets from /assets/ to /_app/ (Vite assetsDir config) so
Traefik can route /assets/* to Logto without conflict. All services
on one hostname with path-based routing:

- /oidc/*, /interaction/*, /assets/* → Logto
- /server/* → server-ui (prefix stripped)
- /api/* → cameleer-saas
- /* (catch-all) → cameleer-saas SPA

Customer needs only 1 DNS record. Server gets OIDC_JWK_SET_URI for
Docker-internal JWK fetch (standard Spring split config).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 21:10:03 +02:00
hsiegeln
9a8881c4cc docs: single-domain routing design spec
Path-based routing on one hostname. SPA assets move to /_app/,
Logto gets /assets/ + /oidc/ + /interaction/. Server-ui at /server/.
Includes requirements for server team (split JWK/issuer, BASE_PATH).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 20:46:00 +02:00
hsiegeln
e167d5475e feat: production-ready TLS with self-signed cert init container
All checks were successful
CI / build (push) Successful in 39s
CI / docker (push) Successful in 40s
Standard OIDC architecture: subdomain routing (auth.HOST, server.HOST),
TLS via Traefik, self-signed cert auto-generated on first boot.

- Add traefik-certs init container (generates wildcard self-signed cert)
- Enable TLS on all Traefik routers (websecure entrypoint)
- HTTP→HTTPS redirect in traefik.yml
- Host-based routing for all services (no more path conflicts)
- PUBLIC_PROTOCOL env var (https default, configurable)
- Protocol-aware redirect URIs in bootstrap
- Protocol-aware UI fallbacks

Customer bootstrap: set PUBLIC_HOST + DNS records + docker compose up.
For production TLS, configure Traefik ACME (Let's Encrypt).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 18:14:25 +02:00
hsiegeln
3694d4a7d6 fix: use server.localhost subdomain for server-ui (same /assets conflict)
All checks were successful
CI / build (push) Successful in 37s
CI / docker (push) Successful in 39s
Server UI assets also use absolute /assets/* paths that conflict with
the SPA catch-all. Same fix as Logto: Host-based routing at
server.localhost gives it its own namespace.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 17:48:11 +02:00
hsiegeln
0472528cd6 fix: move Logto to auth.localhost subdomain to avoid /assets path conflict
All checks were successful
CI / build (push) Successful in 39s
CI / docker (push) Successful in 38s
Logto's login page references /assets/* which conflicts with the SPA's
assets at the same path. Using Host-based routing (auth.localhost) gives
Logto its own namespace - all paths on that subdomain go to Logto,
eliminating the conflict. *.localhost resolves to 127.0.0.1 and is a
secure context.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 17:42:17 +02:00
hsiegeln
c58ca34b2c fix: route all public traffic through Traefik at localhost:80
All checks were successful
CI / build (push) Successful in 38s
CI / docker (push) Successful in 39s
Logto ENDPOINT now points at Traefik (http://localhost) instead of
directly at port 3001. All services share the same base URL, eliminating
OIDC issuer mismatches and crypto.subtle secure context issues.

- Remove :3001 from all public-facing Logto URLs
- Add cameleer3-server-ui to Traefik at /server/ with prefix strip
- Dashboard link uses /server/ path instead of port 8082
- Bootstrap Host headers match Logto ENDPOINT (no port)
- Redirect URIs simplified (Traefik handles port 80)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 17:32:36 +02:00
hsiegeln
3a93b68ea5 fix: split JWK fetch (Docker-internal) from issuer validation (localhost)
All checks were successful
CI / build (push) Successful in 39s
CI / docker (push) Successful in 7s
crypto.subtle requires a secure context, so the browser must access
everything via localhost. The custom JwtDecoder already supports this
split: jwk-set-uri uses Docker-internal logto:3001 for network fetch,
while issuer-uri uses localhost:3001 for string-only claim validation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 17:16:04 +02:00
hsiegeln
e90ca29920 fix: centralize public hostname into single PUBLIC_HOST env var
All checks were successful
CI / build (push) Successful in 39s
CI / docker (push) Successful in 36s
All public-facing URLs (Logto OIDC, redirect URIs, dashboard links) now
derive from PUBLIC_HOST in .env instead of scattered localhost references.
Resolves Docker networking ambiguity where localhost inside containers
doesn't reach the host machine.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 17:07:20 +02:00
hsiegeln
423803b303 fix: use Docker-internal URL for server OIDC issuer in bootstrap
All checks were successful
CI / build (push) Successful in 38s
CI / docker (push) Successful in 5s
Bootstrap was sending LOGTO_PUBLIC_ENDPOINT (http://localhost:3001)
as the OIDC issuer URI to the server. Inside Docker, localhost is
unreachable. Changed to LOGTO_ENDPOINT (http://logto:3001).

Also: .env must set LOGTO_ISSUER_URI=http://logto:3001/oidc (not
localhost) since this env var feeds cameleer3-server's OIDC decoder.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 16:38:02 +02:00
hsiegeln
cfb16d5048 fix: bootstrap OIDC config — add retry and null guard
All checks were successful
CI / build (push) Successful in 37s
CI / docker (push) Successful in 7s
Phase 7 server health check failed intermittently due to timing.
Add 3-attempt retry loop with 2s sleep. Guard against jq returning
literal "null" string for TRAD_SECRET. Add debug logging for both
preconditions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 16:31:41 +02:00
hsiegeln
45b60a0aee feat: add cameleer3-server-ui container to Docker Compose
All checks were successful
CI / build (push) Successful in 39s
CI / docker (push) Successful in 38s
The cameleer3-server deploys backend and UI as separate containers.
Add the cameleer3-server-ui image (nginx SPA + API reverse proxy)
to the Compose stack, exposed on port 8082 in dev. Update sidebar
"View Dashboard" link to point to the UI container.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 16:23:48 +02:00
hsiegeln
9b77f810c1 fix: use correct health endpoint and HTTP method for server integration
All checks were successful
CI / build (push) Successful in 40s
CI / docker (push) Successful in 31s
ConnectivityHealthCheck: /actuator/health → /api/v1/health (actuator
now requires auth on cameleer3-server after OIDC was added).

Bootstrap: POST → PUT for /api/v1/admin/oidc (server expects PUT,
POST returned 405 causing OIDC config to silently fail).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 15:59:24 +02:00
hsiegeln
1ef8c9dceb refactor: merge tenant isolation into single HandlerInterceptor
All checks were successful
CI / build (push) Successful in 38s
CI / docker (push) Successful in 37s
Replace TenantResolutionFilter + TenantOwnershipValidator (15 manual
calls across 5 controllers) with a single TenantIsolationInterceptor
that uses Spring HandlerMapping path variables for fail-closed tenant
isolation. New endpoints with {tenantId}, {environmentId}, or {appId}
path variables are automatically isolated without manual code.

Simplify OrgResolver from dual-token fetch to single token — Logto
merges all scopes into either token type.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 15:48:04 +02:00
hsiegeln
051f7fdae9 feat: auth hardening — scope enforcement, tenant isolation, and docs
All checks were successful
CI / build (push) Successful in 38s
CI / docker (push) Successful in 39s
Add @PreAuthorize annotations to all API controllers (14 endpoints
across 6 controllers) enforcing OAuth2 scopes: apps:manage, apps:deploy,
billing:manage, observe:read, platform:admin.

Enforce tenant isolation: TenantResolutionFilter now rejects cross-tenant
access on /api/tenants/{id}/* paths. New TenantOwnershipValidator checks
environment/app ownership for paths without tenantId. Platform admins
bypass both layers.

Fix frontend: OrgResolver split into two useEffect hooks so scopes
refresh on org switch. Scopes now served from /api/config (single source
of truth). Bootstrap cleaned — standalone org permissions removed.

Update docs/architecture.md, docs/user-manual.md, and CLAUDE.md to
reflect all auth hardening changes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 15:32:53 +02:00
hsiegeln
b459a69083 docs: add architecture document
All checks were successful
CI / build (push) Successful in 38s
CI / docker (push) Successful in 7s
Comprehensive technical reference covering system topology, auth model
(Logto OIDC, scopes, token types, Spring Security pipeline), data model
(7 tables from Flyway migrations), deployment flow, agent-server protocol,
API endpoints, security boundaries, frontend architecture, and full
configuration reference. All class names, paths, and properties verified
against the codebase.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 14:19:05 +02:00
hsiegeln
c5596d8ea4 docs: add user manual
Task-oriented guide for SaaS customers and self-hosted operators
covering login, environments, applications, deployments, observability,
licenses, platform admin, roles, self-hosted setup, and troubleshooting.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 14:15:40 +02:00
hsiegeln
e3baaeee84 fix: replace stale permission prop with scope in EnvironmentDetailPage
All checks were successful
CI / build (push) Successful in 39s
CI / docker (push) Successful in 38s
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 14:13:37 +02:00
hsiegeln
298f6e3e71 feat: scope-based authorization — read standard scope claim, remove custom roles extraction
Some checks failed
CI / build (push) Failing after 18s
CI / docker (push) Has been skipped
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 14:04:16 +02:00
hsiegeln
9c2a1d27b7 feat: replace hardcoded permission map with direct OAuth2 scope checks
Remove role-to-permission mapping (usePermissions, RequirePermission) and replace
with direct scope reads from the Logto access token JWT. OrgResolver decodes the
scope claim after /api/me resolves and stores scopes in Zustand. RequireScope and
useScopes replace the old hooks/components across all pages.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 14:04:06 +02:00
hsiegeln
277d5ea638 feat: define OAuth2 scopes on API resource and assign to Logto roles
Creates 10 API resource scopes (platform:admin + 9 tenant-level) on the
Cameleer SaaS API resource, assigns all to platform-admin role, creates
matching organization scopes, and wires them declaratively to org roles
(admin gets all, member gets deploy/observe). All operations are idempotent.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 14:01:43 +02:00
hsiegeln
6ccf7f3fcb fix: ProtectedRoute spinner fix, TokenSync cleanup, dev hot-reload
All checks were successful
CI / build (push) Successful in 37s
CI / docker (push) Successful in 38s
- ProtectedRoute: only gate on initial auth load, not every async op
- TokenSync: OrgResolver is sole source of org data, remove fetchUserInfo
- docker-compose.dev: mount ui/dist for hot-reload

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 13:11:44 +02:00
hsiegeln
cfa989bd5e fix: allow JwtDecoder bean override in test context
- Add @Primary + @ConditionalOnMissingBean so TestSecurityConfig.jwtDecoder()
  wins over SecurityConfig.jwtDecoder() without needing a real OIDC endpoint
- Add spring.main.allow-bean-definition-overriding=true and
  cameleer.clickhouse.enabled=false to src/test/resources/application-test.yml
  so Testcontainers @ServiceConnection can supply the datasource
- Disable ClickHouse in test profile (src/main/resources/application-test.yml)
  so the explicit ClickHouseConfig DataSource bean is not created, allowing
  @ServiceConnection to wire the Testcontainers Postgres datasource
- Fix TenantControllerTest and LicenseControllerTest to explicitly grant
  ROLE_platform-admin authority via .authorities() on the test JWT, since
  spring-security-test does not run the custom JwtAuthenticationConverter
- Fix EnvironmentService.createDefaultForTenant() to use an internal
  bootstrap path that skips license enforcement (chicken-and-egg: no license
  exists at tenant creation time yet)
- Remove now-unnecessary license stub from EnvironmentServiceTest

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 13:06:31 +02:00
hsiegeln
4da9cf23cb infra: add OIDC config to bootstrap output, stop reading Logto DB for secrets
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 12:44:27 +02:00