Chainguard free tier only offers :latest (currently JDK 26, unpinned);
the :openjdk-21 tag requires a paid subscription, breaking CI.
Switch both Dockerfiles to bellsoft/liberica-runtime-container:jre-21-slim-glibc:
- Pinned to JDK 21 LTS
- Smallest image (199 MB vs 441/491 MB)
- glibc-based Alpaquita Linux, sh-only (no bash, no pkg manager)
- Free, multi-arch (amd64 + arm64)
- Has sh — required by cameleer-server's DeploymentExecutor (withCmd "sh -c")
Use nobody:nobody (65534) instead of Chainguard's nonroot (65532).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Note that the loader source now lives at docker/runtime-loader/, that
the contract is owned by cameleer-server's DockerRuntimeOrchestrator
(don't change env vars / mount path / exit codes without a coordinated
commit there), and that cameleer-server's LoaderHardeningIT is the
cross-repo regression guard. Also document the chown-/app/jars line
(strip it and tenant deploys break with "wget: Permission denied").
Move the init-container loader image build from cameleer-server CI into
this repo so all sidecar/infra image builds (runtime-base, postgres,
clickhouse, traefik, logto, and now runtime-loader) live in one place.
The loader is consumed by cameleer-server's DockerRuntimeOrchestrator as
a per-replica init container that fetches the tenant JAR from a signed
URL into a named volume before the main container starts. Source +
Dockerfile copied verbatim from cameleer-server@c2efb7fb (the image with
the volume-permission fix). The published tag path is unchanged
(gitea.siegeln.net/cameleer/cameleer-runtime-loader:latest), so running
tenant servers continue pulling the same image.
Build step matches the runtime-base/postgres/clickhouse/traefik pattern
(unconditional rebuild on every push, sha + branch tags, --provenance=false
for Gitea). cameleer-server will follow up with a commit removing its
loader-build step and switching its LoaderHardeningIT to pull the
published image instead of building from a local Dockerfile.
Server moved GET /agents to /environments/{envSlug}/agents and removed
GET /admin/apps. Replace three broken individual calls with a single
GET /admin/license/usage call that returns all counts.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace eclipse-temurin:21-jre-alpine with cgr.dev/chainguard/jre:openjdk-21
for the SaaS management plane image. Use Chainguard's built-in nonroot user
instead of custom adduser. Build stages unchanged (ephemeral).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace eclipse-temurin:21-jre-alpine (musl) with cgr.dev/chainguard/jre:openjdk-21
(Wolfi/glibc, daily CVE refresh, signed images + SBOM). Remove the dead ENTRYPOINT
block — DeploymentExecutor overrides it at container creation anyway.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- TOTP otpauth URI issuer changed from "Cameleer" to "Cameleer - <org>"
so authenticator apps display the organization name
- Passkeys without a custom name now show parsed device info (e.g.
"Chrome on Windows") instead of "Unnamed passkey"
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Resolve org role names to Logto role IDs in invite and role change flows
(fixes entity.relation_foreign_key_not_found on invite)
- Handle existing Logto users on re-invite instead of failing with
email_already_in_use
- Delete users from Logto when removed from last org membership
- Consolidate tenant settings page into 3 cards: Tenant Details, MFA,
Authentication Policy — remove duplicate MFA Enforcement and Change
Password (now in Account Settings)
- Make passkey list scrollable
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Same fix as registration verify — @simplewebauthn/browser returns
type: "public-key" but Logto expects type: "WebAuthn".
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Four fixes for the MFA sign-in flow:
1. Fix passkey verify crash: extract authenticationOptions from Logto
response (was passing full response as optionsJSON). Pass
verificationId to the verify endpoint.
2. Default to passkey verification when no MFA method preference is
stored (was showing method picker which offered TOTP to passkey-only
users).
3. Show backup codes after MFA enrollment: new mfaEnrollBackupCodes
mode with copy/download buttons and confirmation checkbox. Users
must save codes before completing sign-in.
4. Remove duplicate error alerts in enrollment screens (top-level
alert handles all modes).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- TOTP secret: /verification/totp/secret (not /verification/totp)
- Backup codes: generate via /verification/backup-code/generate first,
then bind with the returned verificationId. Cannot bind BackupCode
without generating codes first.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Bind BackupCode after primary MFA factor (WebAuthn or TOTP) to satisfy
Logto's requirement that backup codes accompany any MFA method.
- Add TOTP enrollment option alongside passkey on the enrollment screen:
"Use passkey" / "Use authenticator app" / "Set up later".
- TOTP enrollment shows QR code + secret + 6-digit verification inline
in the sign-in UI, using Experience API endpoints.
- Added createTotpSecret() and verifyTotpSetup() to experience-api.ts.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Bootstrap only set [Totp, BackupCode] — WebAuthn was missing. Now
matches LogtoStartupConfig: all three factors available from first boot.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Two fundamental fixes:
- user.missing_mfa now triggers MfaEnrollmentError (enroll UI) instead
of MfaRequiredError (verify UI). Users without MFA were shown a TOTP
code prompt they couldn't fill.
- Logto MFA factors always set to [Totp, WebAuthn, BackupCode] with
UserControlled policy on startup. Availability is always-on for all
users. The vendor auth policy controls enforcement (via
MfaEnforcementFilter), not what Logto offers during sign-in.
- Removed syncMfaConfigToLogto from VendorAuthPolicyController — vendor
policy changes no longer modify Logto's sign-in experience.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Profile API returns empty string instead of "null" when Logto user
has no display name set (String.valueOf(null) → "null" bug).
- SettingsPage: add overflowY auto + flex 1 so content scrolls within
the AppShell layout (which uses overflow: hidden).
- Remove redundant passkey offer from onboarding page — passkey
enrollment now happens during sign-in via the Experience API.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When MFA mode is off but passkeys are enabled, WebAuthn + BackupCode
factors are still synced to Logto. Previously, MFA off cleared all
factors including WebAuthn, so passkey enrollment was never offered.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove the SaaS backend proxy approach for passkey registration (Account
API binding, Management API proxy, password modal in PasskeySection).
Instead, offer passkey enrollment natively during sign-in via Logto's
Experience API — the correct architectural layer.
Sign-in flow: when Logto returns MFA enrollment available (422), show a
"Secure your account" screen with Register passkey / Set up later. Uses
Experience API WebAuthn registration endpoints. Works for all users
(SaaS and future server users) since the sign-in UI is shared.
PasskeySection in account settings now only manages existing passkeys
(list/rename/delete) and directs users to register during sign-in.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Logto's /api/my-account/ endpoints reject the opaque access token with
401 even though /api/verifications/ accepts it. The bind step now goes
through the SaaS backend which calls the Management API instead.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Logto Account API requires identity verification (logto-verification-id
header) for sensitive MFA operations. Adds a password prompt modal before
the WebAuthn ceremony — verifies password first, then proceeds with
passkey registration using the verification record ID.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Vendor sidebar collapsed and tenant sidebar appeared when navigating to
/settings/account because onVendorRoute was false for non-/vendor paths.
Now vendor users stay on vendor sidebar for all routes except /tenant/*.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Logto's PATCH /api/account-center expects mfa as 'Off'|'ReadOnly'|'Edit',
not a nested object. Fixes 400 Bad Request on startup.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Sync vendor auth policy to Logto sign-in experience on save and on
startup. Always include WebAuthn + TOTP + BackupCode in MFA factors
when MFA is enabled — no reason to gate passkeys behind a toggle.
- Enable Logto Account Center on startup for user-facing MFA management.
- Add passkey registration to account settings via Logto Account API.
Frontend calls Logto directly (same domain) for the WebAuthn ceremony:
generate options, browser credential creation, verify, and bind.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Three fixes for MFA enrollment and sign-in:
- Defer TOTP registration with Logto until after 6-digit code verification.
Previously setupTotp() immediately registered the secret, so abandoning
enrollment mid-way left MFA active without a working authenticator.
- Move entire MFA enrollment flow (QR code, verify, backup codes) into a
Modal dialog instead of replacing the Card content inline.
- Fix sign-in MFA flow: submitMfa() no longer calls identifyUser() after
TOTP verify — user is already identified, and passing the MFA
verificationId to identification returned 422 ("method not activated").
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Repositions the "Forgot password?" link from above the sign-in button
to below it, matching the desired layout. Updates link style to be
centered with link color instead of right-aligned muted text.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Logto's secretQrCode is a data:image/png;base64 URI, not an otpauth://
string. QRCodeSVG crashes trying to encode it ("Data too long"). Now
renders data URIs as <img> and only uses QRCodeSVG for otpauth:// URIs.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Logto's /api/roles/{id}/users endpoint rejects page=1 with
guard.invalid_pagination. Remove explicit pagination params and
let Logto use its defaults.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The TenantPortalService constructor gained an AccountService parameter
in the consolidation refactor — the test was missing it.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Remove dead IllegalArgumentException catch blocks in TenantPortalController
(delegated methods now throw ResponseStatusException, handled by Spring)
- Add password reset notification email in VendorAdminService.resetAdminPassword
- Add verifyIsVendorAdmin guard to resetAdminPassword and resetAdminMfa
to prevent platform admins from resetting arbitrary non-admin users
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Vendor admins use global roles, not org roles — passing null orgId
would previously cause addUserToOrganization to call
/api/organizations/null/users and fail.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds verifyUserPassword (for current-password check before password change) and
four global role methods (listRoleUsers, getRoleByName, assignGlobalRole,
revokeGlobalRole) needed by the upcoming AccountService and VendorAdminService.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Two features: multi-vendor admin management (invite/create, remove,
reset password/MFA) and shared account settings page (profile, password
change with current-password verification, MFA self-service). Includes
consolidation plan extracting user-level identity operations from
TenantPortalService into new AccountService.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>