Skip to content

v4.2 - #10872

Merged
andrasbacsai merged 338 commits into
v4.xfrom
next
Jul 19, 2026
Merged

v4.2#10872
andrasbacsai merged 338 commits into
v4.xfrom
next

Conversation

@andrasbacsai

@andrasbacsai andrasbacsai commented Jul 8, 2026 •

Copy link
Copy Markdown
Member

⚠️ Breaking Change

Team members with the Member role now have read-only access. They can continue to view team resources and configuration, but can no longer create, update, delete, deploy, start, stop, or otherwise modify resources. Review your team roles before upgrading and promote users who still require write access.

State-changing API endpoints now require POST. Legacy GET requests return 405 Method Not Allowed. Update API clients and deploy webhooks that use these endpoints:

  • /enable
  • /disable
  • /deploy
  • /servers/{uuid}/validate
  • /applications/{uuid}/start
  • /applications/{uuid}/restart
  • /applications/{uuid}/stop
  • /databases/{uuid}/start
  • /databases/{uuid}/restart
  • /databases/{uuid}/stop
  • /services/{uuid}/start
  • /services/{uuid}/restart
  • /services/{uuid}/stop
  • /services/{uuid}/applications/{app_uuid}/start
  • /services/{uuid}/applications/{app_uuid}/restart
  • /services/{uuid}/applications/{app_uuid}/stop

What's Changed

Features

Fixes

Improvements

Breaking Changes

Issues fixed by this release

Fixes #5293
Fixes #6407
Fixes #6488
Fixes #6518
Fixes #6881
Fixes #7232
Fixes #7989
Fixes #8104
Fixes #9204
Fixes #9211
Fixes #9495
Fixes #9943
Fixes #9860
Fixes #10280
Fixes #10525
Fixes #10556
Fixes #10573
Fixes #10597
Fixes #10632
Fixes #10633
Fixes #10692
Fixes #10732
Fixes #10736
Fixes #10769
Fixes #10827
Fixes #10830
Fixes #10870

revolunet and others added 30 commits May 28, 2026 10:26
Authorize cloud provider token access, audit sensitive operations, and
standardize public IDs across deployment and resource flows.
Prevent users without update permission from reading notification credentials and manual webhook secrets in Livewire state or rendered forms.
Require read:sensitive for nested server logdrain and sentinel fields in
application and database API responses.

Limit deployment configuration column migration SQL to PostgreSQL.
Introduce a shared status badge component and reuse it across resource status views, service cards, and the server navbar. Replace icon-only refresh controls with consistent badge buttons and cover the new status layout with feature tests.
Unify application, database, and service headings with mobile-friendly
resource menus, route-aware configuration tabs, and confirmation triggers for
advanced actions. Standardize restart warnings with status badges and cover the
responsive menu and badge rendering behavior with feature tests.
Move GitHub App JWT generation and slug synchronization into shared helpers so installation URLs use the canonical GitHub slug. Encode GHE organization path segments and keep the app-scoped fallback for blank organizations.
Normalize GitHub organization values and derive API URLs for GitHub.com,
GHE.com, and enterprise hosts when creating or updating GitHub Apps.
andrasbacsai and others added 5 commits July 10, 2026 14:29
Replace the legacy sync:bunny flags with an interactive CDN sync flow for service templates and release metadata. Serve official service templates from the Coollabs CDN, update version metadata, and remove obsolete helper scripts.
Bumps [web-auth/webauthn-lib](https://github.com/web-auth/webauthn-lib) from 5.3.3 to 5.3.5.
- [Commits](web-auth/webauthn-lib@5.3.3...5.3.5)

---
updated-dependencies:
- dependency-name: web-auth/webauthn-lib
  dependency-version: 5.3.5
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Persist DigitalOcean, Hetzner, and Vultr servers before public IP
assignment, then backfill placeholder addresses from provider state.

Treat partial Sentinel snapshots as non-authoritative and document the
destinations API with OpenAPI schemas.
Track provider state independently, skip SSH work for placeholder IPs, and clean up failed cloud server provisioning.
Expand configuration snapshots, handle defaults from older snapshots, and refresh configuration state after Livewire setting changes.
Add service database CRUD, logs, and lifecycle actions, and document service application operations with OpenAPI tests.
Enable scheduled ClickHouse backups across the job, API, and UI, and
guard unsupported database types via isBackupSolutionAvailable().
Convert Stripe subscription sync from a job to an action with clearer
discrepancy resolution, and add cloud:export-users plus
cloud:cleanup-unverified-users with tests.
Wrap Vultr server creation in DB transactions and delete the remote
instance when local persistence fails (API and Livewire). Scope
plus/dot email normalization to gmail.com/googlemail.com only.
Use throw:false on DigitalOcean/Vultr HTTP retries, and normalize
service log line counts via normalizeLogLines.
Keep the dev container as root for s6 init so composer can create
vendor/ on root-owned mounts, then chown writable paths to www-data.
Move init-setup into a shell script and expose VITE_HOST/PORT for
remote HMR (LAN/Tailscale) with Vite listening on 0.0.0.0.
Make start/stop/restart, deploy, enable/disable, and server validate
POST-only, with GET returning 405. Server validate accepts optional
install and uses ValidateAndInstallServerJob. Update OpenAPI and tests.
@andrasbacsai
andrasbacsai merged commit f715fb6 into v4.x Jul 19, 2026
4 checks passed
StuMason added a commit to StuMason/coolify-mcp that referenced this pull request Jul 29, 2026
Coolify v4.2 (coollabsio/coolify#10872) requires POST on state-changing
endpoints and returns a hard 405 for GET. Six client call sites still sent
GET: service start/stop/restart, deployByTagOrUuid, enableApi/disableApi,
and validateServer (which passed no method, so it defaulted to GET).

Checked upstream routes/api.php at v4.1.2, v4.0.0 and older betas to work
out whether fixing this breaks anyone still on 4.1. It splits in two:

  - Service start/stop/restart and /deploy were already registered
    Route::match(['get','post']) well before v4.2, so they now send POST
    unconditionally. Application and database start/stop have been POSTing
    against 4.1 estates all along, which is the same route shape, so this
    is proven rather than assumed.

  - /enable, /disable and /servers/{uuid}/validate genuinely diverge:
    Route::get only up to v4.1.2, Route::post only from v4.2. Neither
    method works everywhere, so these send POST and retry once with GET on
    a 405, caching the resolved method per endpoint.

The retry is safe because a 405 is raised by the router before the
controller runs, so nothing executed and no state change can double-fire.
Only 405 triggers the fallback; every other status propagates untouched,
and a failed GET retry is not cached so it re-probes rather than trusting
an unproven fallback.

request() now throws CoolifyApiError carrying the HTTP status, since the
fallback needs to distinguish 405 from everything else and the message
text alone could not. The message is unchanged, so anything matching on
error.message is unaffected.

Also from #292: PrivateKey.private_key, EnvironmentVariable.value and
EnvVarSummary.value are now optional, because v4.2 (coollabsio/coolify#9893)
withholds secrets unless the token has sensitive-read scope, and a required
type made that arrive as a silent undefined. Create*Request types are
unchanged, as request payloads are unaffected.

405 now carries an error hint explaining the method move, and the 401/403
hint mentions v4.2 Member-role tokens being read-only.

Reported by @StreamlinedStartup with the call sites already identified.

Closes #292
StuMason added a commit to StuMason/coolify-mcp that referenced this pull request Jul 29, 2026
)

* fix: Coolify v4.2 compatibility without breaking pre-4.2 instances

Coolify v4.2 (coollabsio/coolify#10872) requires POST on state-changing
endpoints and returns a hard 405 for GET. Six client call sites still sent
GET: service start/stop/restart, deployByTagOrUuid, enableApi/disableApi,
and validateServer (which passed no method, so it defaulted to GET).

Checked upstream routes/api.php at v4.1.2, v4.0.0 and older betas to work
out whether fixing this breaks anyone still on 4.1. It splits in two:

  - Service start/stop/restart and /deploy were already registered
    Route::match(['get','post']) well before v4.2, so they now send POST
    unconditionally. Application and database start/stop have been POSTing
    against 4.1 estates all along, which is the same route shape, so this
    is proven rather than assumed.

  - /enable, /disable and /servers/{uuid}/validate genuinely diverge:
    Route::get only up to v4.1.2, Route::post only from v4.2. Neither
    method works everywhere, so these send POST and retry once with GET on
    a 405, caching the resolved method per endpoint.

The retry is safe because a 405 is raised by the router before the
controller runs, so nothing executed and no state change can double-fire.
Only 405 triggers the fallback; every other status propagates untouched,
and a failed GET retry is not cached so it re-probes rather than trusting
an unproven fallback.

request() now throws CoolifyApiError carrying the HTTP status, since the
fallback needs to distinguish 405 from everything else and the message
text alone could not. The message is unchanged, so anything matching on
error.message is unaffected.

Also from #292: PrivateKey.private_key, EnvironmentVariable.value and
EnvVarSummary.value are now optional, because v4.2 (coollabsio/coolify#9893)
withholds secrets unless the token has sensitive-read scope, and a required
type made that arrive as a silent undefined. Create*Request types are
unchanged, as request payloads are unaffected.

405 now carries an error hint explaining the method move, and the 401/403
hint mentions v4.2 Member-role tokens being read-only.

Reported by @StreamlinedStartup with the call sites already identified.

Closes #292

* fix: self-heal the v4.2 method cache and tighten the fallback contract

Addresses review feedback on #296.

The remembered-GET path was terminal: once an endpoint was cached as
legacy-GET, a Coolify instance upgraded to v4.2 mid-session would 405 on
every subsequent call until the MCP server restarted. The cache now
self-heals in both directions — a 405 from a remembered GET drops the
stale preference and re-probes POST. A non-405 failure from the
remembered GET still propagates without re-probing, so a genuine error
never triggers a second state-changing request.

The fallback key is now a closed union (LEGACY_GET_ENDPOINTS) instead of
a bare string, so a future call site cannot silently reuse another
endpoint's cache entry. Documented why the key is an endpoint identifier
rather than the request path: version compatibility is a property of the
instance, not the resource, so keying on the path would re-probe per uuid.

The 405 hint no longer asserts the v4.2 GET-to-POST story for every 405.
It led with that explanation regardless of endpoint, which would mislead
anyone hitting a genuine 405 elsewhere.

Also recorded three Coolify API gotchas in CLAUDE.md: the non-uniform
v4.2 method split, v4.2 secret-hiding plus Member-role read-only, and the
preview-twin env var hook (see #257) with the warning never to dedupe env
vars by key alone.

* docs: trim the 405 error hint

Review feedback on #296: the hint led with an explanation of the client's
own retry internals, which reads as noise to anyone hitting a 405 on an
endpoint the fallback does not cover. Says what changed and what to check,
without narrating the implementation.
@github-actions github-actions Bot locked as resolved and limited conversation to collaborators Aug 19, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.