Commit Graph
17591 Commits
Author SHA1 Message Date
peaklabs-dev 9683fd3466 chore(deps): restore composer version ranges 2026-10-03 22:51:07 +02:00
Andras Bacsai c0d81d4c9c fix(auth): accept Entra ID xms_edov claim as verified OIDC email
Microsoft Entra ID never sends email_verified, so existing accounts could
not be linked through OIDC. Treat a boolean true xms_edov claim (email
domain owner verified) as equivalent, via a shared
OidcUser::claimsVerifyEmail() helper used for both ID token and userinfo
claims.

Document the Entra ID issuer URL and required optional claims in the
OIDC settings helper text, and add tests covering xms_edov handling.
2026-10-02 18:51:16 +02:00
Andras Bacsai f95ddd97fa fix: enforce webhook lockouts and stop cancelled runner provisioning
- Manual webhooks (GitHub, GitLab, Gitea, Bitbucket) now reject all
  deliveries for a locked-out scope before reading secrets or verifying
  signatures, including correctly signed ones, until the window expires.
- ProvisionGithubRunnerJob checks whether the execution has already
  finished before and after each provisioning step, and only sets
  ready_at or Failed while it is still occupying/provisioning. A runner
  that is cancelled, timed out or completed mid-provisioning is no longer
  started, retried or overwritten.
- VolumeCloneJob creates bind-volume clones as plain local volumes
  without the source driver options, so the clone never mounts the
  source host directory.
- Image pulls for additional servers no longer ignore docker pull errors.
- Add tests for each change.
2026-10-02 18:24:52 +02:00
Andras Bacsai 5fd7191d4a fix(auth): allow OAuth sign-up with an unverified provider email
The provider email verification check now runs only when an account with
that email already exists. A new account follows the registration
settings, the same way password registration does, which also doesn't
verify the email. Linking to an existing account still needs a verified
email, except for password-less users created before OAuth identities.

Add tests for new-user registration from Authentik, Discord and GitLab
when the provider email is unverified. Also test that the "Registration
is disabled" error is shown when registration is turned off.
2026-10-02 17:07:54 +02:00
Andras Bacsai d6f89d1feb fix(proxy): remove legacy Traefik dashboard labels only in the traefik service
The line edits ran on the whole proxy compose file. When another service
had the same labels, the safety check failed and the dashboard fix was
skipped without a warning. Limit the edits to the traefik service block,
so other services stay unchanged and the fix is applied.
2026-10-02 17:03:55 +02:00
Andras Bacsai 8c2725a3f8 fix(scheduler): send at most one missed-run notification per hour per schedule
When queue workers are down, an every-minute task or Docker cleanup
missed a run every minute, and each missed run sent a notification.
Each missed run still gets its own failed execution, but the team now
gets at most one missed-run notification per schedule each hour,
through NotificationThrottle.
2026-10-02 17:03:55 +02:00
Andras Bacsai a7cc32f249 fix(api): hide the legacy password quoting flag of KeyDB and Dragonfly
legacy_password_quoting is internal start-command state. Add it to the
hidden fields of both models, so the database API does not return it.
2026-10-02 17:03:55 +02:00
Andras Bacsai 2f3a5088ee fix(ui): offset focus ring on highlighted buttons with outline gap
Highlighted buttons use the accent color, so the flush box-shadow focus
ring looked like a stray border. Use an offset accent outline instead and
keep the regular depth shadow on focus and hover-focus.
2026-10-02 16:58:42 +02:00
Andras Bacsai 5b1fcd6e13 feat(auth): show OAuth-only registration notice on login page
Move the user creation check from OauthLoginService into
OauthSetting::allowsUserCreation(), so the login view can use it.
The login view now gets a can_register_with_oauth flag. The flag is
true when an enabled OAuth provider allows creating new users. The
view uses it to show the new auth.register_with_oauth message.
Add translations for the message in all locales, plus test coverage.
2026-10-02 16:45:33 +02:00
Andras Bacsai b1ddde2b64 fix(auth): enforce Google hosted domain and block password-user linking
Treat the Google "Hosted domain" setting as an enforced Workspace
restriction: compare it to the hd claim case-insensitively with spaces
trimmed, and accept "*" for any Workspace account while still rejecting
personal Google accounts.

Exempt pre-upgrade OAuth users from the email verification check only
when they have no password. A flagged user with a password no longer gets
an unverified provider email linked to their account.

Update the hosted domain helper text and add tests for both cases.
2026-10-02 16:11:21 +02:00
Andras Bacsai 8521f973dd fix(auth): show specific error messages for denied OAuth logins
Add OauthLoginException, which carries a translation key for the message
shown on the login page. The exception message only goes to the log.
OauthLoginService now throws it for an unverified email, an account that
is already linked to another identity, and disabled registration.
OauthController shows the matching message for each case. Other
HttpExceptions fall back to a generic OAuth failure message.

The email verification check now runs before the linked-identity check,
so an unverified email cannot reveal that an account is already linked.

Add translations for the new auth.failed.oauth* keys in all locales and
cover the new error messages with feature tests.
2026-10-02 16:07:48 +02:00
Andras Bacsai 4e4659dad7 chore(nightly): point v4 version in nightly versions.json to main
Replace the pinned 4.4.0 v4 version with "main" so nightly builds
track the main branch instead of a fixed release.
2026-10-02 16:02:11 +02:00
Andras BacsaiandClaude Opus 5.5 10542000c2 test(notifications): cover a migrated unreachable marker that is older than the throttle window
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 16:01:25 +02:00
Andras BacsaiandClaude Opus 5.5 5e1e199ad4 chore(scripts): handle a missing coolify-realtime container in the backup restore VM script
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 16:01:25 +02:00
Andras BacsaiandClaude Opus 5.5 8106cb7cfa chore(nightly): sync nightly install and upgrade scripts with the root scripts
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 16:01:25 +02:00
Andras BacsaiandClaude Opus 5.5 1c68eafa84 fix(backups): make overlapping and concurrent backup runs safe
- Skip the occurrence of a backup whose previous run still holds the
  overlap lock, so stale recovery does not run it later; do not rerun a
  lost occurrence when a newer one already completed.
- Skip a database backup while a start, restart, or import of the database
  is in progress, and record the skip in the execution list.
- Give every backup file a unique name per run and keep the service
  database backup folder of earlier releases.
- Record missed scheduled tasks and Docker cleanups as failed executions
  and notify the team.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 16:01:20 +02:00
Andras BacsaiandClaude Opus 5.5 7ec46faecb fix(previews): do not bind preview volumes to the production host device
A preview volume no longer gets driver_opts that name a host device, so it
does not mount the same folder or disk as the production volume. tmpfs
options are kept.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 16:01:20 +02:00
Andras BacsaiandClaude Opus 5.5 a0fa21b448 fix(reverb): use the uv event loop and raise the open-file limit
The default stream_select() loop fails once a descriptor reaches 1024
(FD_SETSIZE). Install ext-uv and, only when an event loop extension is
available, raise the soft open-file limit of Reverb up to 65536.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 16:01:20 +02:00
Andras BacsaiandClaude Opus 5.5 1501a44cfe fix(sentinel): pass the traffic API token to curl on stdin
The token is no longer a process argument that other users on the server
can read in /proc. Also correct the docker login comment about where the
registry password is visible.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 16:01:20 +02:00
Andras BacsaiandClaude Opus 5.5 f3af1c9197 fix(sudo): do not add sudo to pipes inside quoted strings
Pipes in single or double quotes (Go templates, sh -c scripts) are text,
so the non-root sudo parser now leaves them unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 16:01:20 +02:00
Andras BacsaiandClaude Opus 5.5 43b7405c4c fix(security): authorize integration token creation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 16:01:10 +02:00
Andras BacsaiandClaude Opus 5.5 9862f75dc2 fix(audit): reduce audit log noise and keep instance events out of team logs
- Ignore status, heartbeat, and check fields that background checks write
  during a user request.
- Record repeated unauthenticated failures (failed logins, API auth,
  webhook signatures) once per source in a short window.
- An explicit null team_id in the context is kept, so instance events are
  not stored on the admin's current team.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 16:01:10 +02:00
Andras BacsaiandClaude Opus 5.5 f75ffd329f fix(auth): let OAuth-only users enable 2FA without a password confirmation
Users with a linked OAuth identity or no password cannot confirm a
password they do not know. The password.confirm middleware now follows
User::requiresPasswordConfirmation(), like destructive actions.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 16:01:10 +02:00
Andras BacsaiandClaude Opus 5.5 3dabda5c56 fix(oauth): require a verified provider email for password users and use the instance callback URL
- Only password-less users from before OAuth identities skip the provider
  email verification claim. Otherwise an unverified provider account with
  the email of a password user (including root) could sign in as them.
- Build the default callback URL from the instance URL, not the request
  Host header, and do not save it during a login.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 16:01:10 +02:00
Andras BacsaiandClaude Opus 5.5 55bbe92d94 fix(github-runner): skip pull request jobs early and keep runner group restrictions
- Do not provision a runner for a queued pull request job when no matching
  runner configuration allows pull request jobs (the run event is read from
  GitHub; the job-started hook still refuses it if the lookup fails).
- Do not open an existing runner group to all or public repositories, so
  restrictions an organization admin set on GitHub stay.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 16:01:00 +02:00
Andras BacsaiandClaude Opus 5.5 5cb68cda16 fix(api): return JSON validation errors and reject unknown fields on PATCH /team
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 16:01:00 +02:00
Andras BacsaiandClaude Opus 5.5 0b581454cf fix(api): match database backup configs by database type and id
Different database types can share an id, so the database list showed
backup configs of other databases.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 16:01:00 +02:00
Andras BacsaiandClaude Opus 5.5 aa2592b17c fix(security): stop leaking locked shared variables and proxy secrets
- References to locked (shown once) shared variables keep their reference
  text in the UI, copy action, and API instead of the locked value.
- Remove the saved proxy configuration and validation logs from nested
  servers (for example destination.server) in API responses unless the
  token can read sensitive data.
- The services list API now applies removeSensitiveData to every service.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 16:01:00 +02:00
Andras BacsaiandClaude Opus 5.5 a5ec854e35 fix(services): resolve the old denoKV template key to deno-kv
A renamed template keeps its old key as an alias, so API callers and
services stored with the old key still find the template.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 16:00:52 +02:00
Andras BacsaiandClaude Opus 5.5 2e2e43fddc fix(services): finish service cleanup when a volume cannot be removed
- Try to remove every volume and always delete the configuration
  directory (it holds the .env with secrets), then report the volumes that
  failed.
- Delete volume backup schedules also when a service is removed from
  Coolify only.
- The delete API no longer claims the server is unreachable when the
  caller asked to delete from Coolify only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 16:00:52 +02:00
Andras BacsaiandClaude Opus 5.5 b98e810e3f fix(proxy): keep Compose merge tags and remove the log rotation sidecar
- Parse and dump the proxy Compose file with custom tags, so `!reset` and
  `!override` survive the traffic analytics update and `{}`/`[]` keep
  their type.
- Remove the Traefik log rotation sidecar when the proxy stops or another
  proxy type starts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 16:00:51 +02:00
Andras BacsaiandClaude Opus 5.5 2e44d2295b fix(deployments): fail with a clear error for an unsupported static image
Older versions accepted any static image through the API. Such a stored
value now stops the deployment with a message that lists the supported
web servers instead of a ValueError.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 16:00:51 +02:00
Andras BacsaiandClaude Opus 5.5 9978929779 fix(databases): keep the server password of existing KeyDB and Dragonfly databases
The start command now quotes the password. Databases created before this
release keep the unquoted v4.3.23 argument when quoting would change the
password the server receives, until their password changes. Remote secret
passwords are always quoted exactly.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 16:00:41 +02:00
Andras BacsaiandClaude Opus 5.5 9bdbbeef7a fix(secret-manager): use remote secret values exactly and check them before a restart
- Write remote secret values as exact dotenv literals in the service .env
  file and escape "$" where they go directly into a generated compose file
  (environment, start commands, healthchecks).
- Resolve remote secrets before a service or database restart stops the
  running resource, and show the secret manager error instead of a generic
  start failure (new RemoteSecretException).
- Cache fetched key names for the autocomplete and rate limit how often a
  user can contact the secret manager.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 16:00:41 +02:00
Andras BacsaiandClaude Opus 5.5 25a4076bd0 fix(api): require the write ability for database imports
Uploading and starting a database import overwrites data, so a deploy
token is no longer enough. Import activity IDs must be numeric.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 16:00:29 +02:00
Andras BacsaiandClaude Opus 5.5 476ba3c6ab fix(database-import): delete staged API uploads that were never imported
Add an hourly cleanup:database-import-uploads command that removes staged
import uploads older than a day, while holding the import lock of the
database so a running import keeps its file.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 16:00:29 +02:00
Andras BacsaiandClaude Opus 5.5 1ee34a5ab2 fix(database-import): stop the restore when the import task fails or loses SSH
An import task can now run until the SSH command timeout (capped below the
Horizon worker timeout). When the CoolifyTask fails, times out, or its SSH
command ends with 124/255, Coolify stops the restore in the database
container and keeps the import in progress until the stop has run, so the
database stays blocked. The restore reports its own 124/255 as 1.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 16:00:29 +02:00
Andras BacsaiandClaude Opus 5.5 332a970b27 feat(cdn): add Cloudflare cache purge to sync:cdn
Add a --purge option that purges the synced CDN files (and the legacy
/coolify-nightly/ paths for nightly) from the Cloudflare cache, and remind
the operator to run it after the CDN pull request is deployed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 16:00:29 +02:00
Andras Bacsai a14ef2d4ee chore(cdn): remove BunnyCDN sync and rename sync:bunny to sync:cdn
CDN files are synced through the coollabs-cdn GitHub repository only.
Remove the --bunny direct upload path, the BUNNY_API_KEY and
BUNNY_STORAGE_API_KEY settings, and rename the command to sync:cdn.
2026-10-02 11:36:04 +02:00
Andras Bacsai 99b2bce9ee fix(database-import): hide import output and avoid temp DB collisions
Return database import output from the API only when the token has
read:sensitive and the user can update the resource. Otherwise the
output field is null.

Give the temporary PostgreSQL restore databases random suffixes. Before
restoring, check that neither name is already taken instead of dropping
any database that already uses it. The old database is no longer
dropped before the swap, so unrelated databases are left alone.

Add API tests for output visibility by role and token ability. Add
restore script tests that run against stateful PostgreSQL client stubs.
2026-10-02 09:41:37 +02:00
Andras Bacsai 51e6949c36 fix(sentinel): stop hourly check job pile-up and self-heal Sentinel
- Start Sentinel with --restart unless-stopped so Docker restarts it
  after a crash or reboot.
- Make CheckAndStartSentinelJob unique per server so copies cannot
  collect in a slow queue.
- Read X-Sentinel-Version on push and update an outdated Sentinel
  without SSH; skip the hourly SSH version check for servers that
  report their version.
2026-10-02 09:41:04 +02:00
Andras BacsaiandClaude Opus 5.5 f9d10c22fb fix(backups): stop volume backup recovery retry storms
The scheduler dispatched VolumeBackupRecoveryJob every minute for each
pending execution, and each job retried 20 times with a 60 second backoff.
A stuck execution kept about 20 failing jobs active at a time.

- Run each recovery job once; the scheduler is the only retry path.
- Make the job unique per execution so duplicates do not pile up.
- Dispatch recovery for an execution at most once every 5 minutes.
- Clear the pending flag and record the reason when recovery cannot
  succeed (server, resource, S3 storage, or filename no longer exists),
  so it does not fail forever and block new backups.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 09:35:13 +02:00
Andras Bacsai f575b2d179 feat(scheduler): run one scheduled job dispatcher for each schedule type
The scheduler ran one dispatcher for database backups, tasks, volume backups,
and Docker cleanups after each other. A slow type, for example many tasks due
at the same minute, made the whole run longer, and the shared overlap lock
then made all types skip the next minute.

Run `scheduled:dispatch --type=<type>` every minute for backups, tasks,
volume-backups, and docker-cleanups. Each type runs in its own background
process with its own scheduler and job overlap locks, and publishes and
recovers only the deliveries of its own job type. Running the command without
a type still processes all types.
2026-10-02 09:19:54 +02:00
Andras Bacsai a218e8cdc6 fix(scheduler): make scheduled:diagnostics read-only and show the real state
The command called shouldRunCronNow() with the dedup keys, and that helper
writes the key it checks. For server-check, server-storage-check, and
server-patch-check these are the keys of ServerManagerJob, so running the
command in a minute when a check was due made the real check skip that run.

It also read cache keys for backups, tasks, and Docker cleanups that the
scheduled job manager no longer uses. Read scheduled_job_states instead, and
list open and failed deliveries, where an old "enqueued" row shows a queued
job that was lost.
2026-10-02 08:07:30 +02:00
Andras Bacsai 50be16be8f revert(scheduler): go back to the cron scan and keep the reliability fixes
The missed daily backups on Coolify Cloud came from Redis evicting queue keys
(allkeys-lfu on a full instance), not from the scan of all schedules. The
next_run_at design (452259aac, 5c5740533) did not prevent that, and it added
stored state that must be kept in sync on every frequency, timezone, and server
change. It was not released, so remove it:
- revert next_run_at, its model hooks, the "Next run" UI, and the diagnostics
  changes; restore scheduled_job_states and the queue overlap lock
- a new migration removes the next_run_at columns and creates
  scheduled_job_states again where the removed migration already ran

Keep the changes that fix real problems:
- run the manager every minute from the scheduler with `scheduled:dispatch`,
  so a busy queue cannot delay it past the catch-up window
- load the server chain of scheduled backups with each chunk, which removes
  about 7,000 queries per run on Coolify Cloud
- catch Throwable for each schedule, so one broken schedule does not stop the
  others
- publish database and volume backups again when their queued job was not
  started after 60 minutes (for example, lost in Redis); log lost tasks and
  Docker cleanups as missed. A lost backup is not run again when a newer
  occurrence of the same schedule exists.
2026-10-02 08:05:18 +02:00
Andras Bacsai bef08d84de chore(horizon): lower job trim times
Keep completed and recent jobs for 10 minutes, pending for 30 minutes,
and failed and monitored jobs for 1 day.
2026-10-02 07:19:39 +02:00
Andras Bacsai 0ed423aaac feat(webhooks): require 16 characters for new manual webhook secrets
New or changed manual webhook secrets (GitHub, GitLab, Bitbucket,
Gitea) must have at least 16 characters, in the Webhooks page and in
the application API. Stored secrets keep working: an unchanged value
passes, and an empty value still disables the webhook. Each secret
field gets a Generate button that fills a random 40-character secret;
the user saves it after adding it to the Git provider.
2026-10-01 20:31:00 +02:00
Andras Bacsai c85c697e80 fix(templates): remove duplicate Homarr key and quoted Bulwark values
Homarr set SECRET_ENCRYPTION_KEY twice; only the
SERVICE_HEX_64_HOMARR line was ever used, so the duplicate is removed
and existing keys do not change. Bulwark quoted list-syntax values, so
the quotes reached the container and JMAP_SERVER_URL was never created
as a required variable. Existing services keep their stored compose.
2026-10-01 20:31:00 +02:00
Andras Bacsai 7a4709da69 fix(ui): scope env refreshes, block duplicate container names, use a confirm modal
- refreshEnvs is now sent only to the environment variable list, and
  rows no longer listen to it, so adding or removing variables cannot
  queue an update for a removed row ("Snapshot missing"). Locking a row
  refreshes that row directly; the compose editor gets its own event.
- saveCustomName() no longer saves a custom container name that another
  application on the server already uses; it restores the stored value.
- Cancelling a GitHub runner uses the Coolify confirmation modal instead
  of a native browser dialog.
2026-10-01 20:31:00 +02:00
Andras Bacsai 3379f85c0a fix(security): authorize import file checks and Vultr status refresh
ImportForm::checkFile() and checkS3File() ran without authorization, so
a member with view access could check whether paths exist on the server
or probe S3. They now require update access, like the restore actions.
Server\Show::checkVultrInstanceStatus() now requires view access, like
the Hetzner and DigitalOcean checks.
2026-10-01 20:31:00 +02:00