Include the invitation UUID in generated magic link tokens and validate the
matching stored invitation link before logging the user in, preventing stale
or same-email invitations from being reused.
Skip Docker healthcheck configuration when standalone database health checks are disabled, and document default health check settings in the database API schema.
Add configurable health check settings for standalone databases and apply them to generated Docker Compose services. Allow disabling health checks and cover the behavior with feature tests.
Strip leading port segments from scp-style GitLab repository URLs so manual webhook matching compares the repository path consistently. Cover both ported and unported SSH URL forms.
Ignore health status changes in Sentinel push deduplication when the container lifecycle state is unchanged.
Scope stale resource checks to Sentinel servers whose heartbeat is stale, and avoid refreshing resource last_online_at on unchanged statuses.
- resolve the GitHub App by a stable identifier during installation
callbacks so installing and re-installing keeps working over the
full lifetime of the App
- verify the installation id received from the callback against the
GitHub API before persisting it
- support re-installing an already configured GitHub App instead of
blocking it
- require an authenticated session and rate limit the setup callback
routes
- extend manifest setup state validity to match GitHub's manifest
code lifetime
Adds feature coverage for the GitHub App setup and installation
callbacks.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Git hosts treat owner/repo names case-insensitively, but the exact
repository match used a case-sensitive comparison, so a payload whose
casing differed from the stored git remote would fail to match and
skip a legitimate deployment.
Lowercase both canonical repository paths before comparing.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The manual webhook handlers selected target applications with a
`git_repository LIKE %full_name%` substring query, so a payload
repository name could match unintended applications when repository
names overlap.
Add a `MatchesManualWebhookApplications` trait that validates the
incoming `owner/repo` value and matches `Application.git_repository`
by exact normalized path. Github, Gitlab, Gitea and Bitbucket manual
handlers now use it, reject invalid repository input early, and return
a consistent generic webhook failure payload.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Move Sentinel push handling into a controller and dispatch server update jobs only when container state changes or the force interval elapses. Add opt-in PostgreSQL read/write replica configuration and tune periodic proxy network and storage checks to reduce unnecessary work.
Add feature coverage for replica config, Sentinel push deduplication, deployment log scrolling, and server update job optimizations.
Decode base64 custom_nginx_configuration before model assignment so it is not double-encoded, and allow null values when clearing the setting. Add API coverage for create, update, invalid input, and clearing behavior.
Require Docker buildx before Railpack builds, normalize environment
variable keys before validation, and align private deploy key API docs with
the supported dockerfile build pack.
Drop the unstable applications/dockercompose route and controller path now that
service creation is handled by POST /api/v1/services. Add coverage to ensure the
deprecated endpoint stays unregistered while the services endpoint remains
available.
- `/mcp/enable` and `/mcp/disable` now use POST (state-mutating ops)
- `ListServices` queries DB directly instead of loading all projects into memory
- `ListApplications` validates tag arg rejects empty string (not just falsy)
Add Model Context Protocol server exposing Coolify infrastructure data
to AI assistants. Includes tools for listing/fetching servers, projects,
applications, databases, and services, scoped to authenticated team tokens.
- Add CoolifyServer with 10 read-only tools (list/get for all resource types)
- Add BuildsResponse and ResolvesTeam traits for shared tool logic
- Add EnsureMcpEnabled middleware guarding /mcp routes
- Add enable/disable MCP API endpoints (root-only)
- Add is_mcp_server_enabled toggle in instance settings and advanced UI
- Add migration for is_mcp_server_enabled column
- Add feature tests for MCP endpoints and toggle API
- Scrub sensitive keys (passwords, tokens, raw IDs) from all responses
Add DetectsSkipDeployCommits trait with two strategies: shouldSkipDeploy
(all commits must contain the marker) for push events, and
shouldSkipDeployAny (any single marker triggers skip) for PR/MR titles
and latest-commit signals.
Apply trait to Bitbucket, Gitea, GitHub, GitLab webhook controllers and
ProcessGithubPullRequestWebhook job. PRs pass pullRequestTitle through
to the job constructor for evaluation.
Introduce a dedicated `audit` log channel (daily rotation, configurable retention via
LOG_AUDIT_DAYS) and a small `auditLog()` / `auditLogWebhookFailure()` helper used to
record state-changing API operations and webhook events.
Instrumented:
- API mutation endpoints (create / update / delete / start / stop / restart) across
applications, services, databases (incl. backups, env vars, storage), servers,
projects + environments, scheduled tasks, private keys, GitHub apps, cloud provider
tokens, Hetzner server provisioning, instance enable/disable.
- Webhook signature verification outcomes for GitHub, GitLab, Bitbucket, Gitea and
Stripe, plus the Sentinel push endpoint.
- Authentication and authorization outcomes via the global exception handler and
the `ApiAbility` middleware (unauthenticated, ability-denied, policy-denied).
The helper is wrapped in try/catch so logging failures never affect the request
path. Successful operations log at `info`; suspicious/denied requests log at
`warning`. Operators wanting a failures-only feed can set `LOG_AUDIT_LEVEL=warning`.
Includes a feature test suite covering the helper, the webhook providers and the
new auth/authorization log paths.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add ValidationPatterns helpers for database identifiers and passwords,
apply them across database Livewire components and the API controller,
encode MongoDB init script values via json_encode, and pass the MySQL
user through escapeshellarg when generating SSL chown commands.
Move the email-verification URL hash from sha1 to sha256 and verify it
directly in the controller using hash_equals, instead of going through
Laravel's EmailVerificationRequest (which only compares against sha1).
The signed URL still carries the authoritative HMAC; the hash upgrade
keeps the identity binding aligned with modern hashing guidance.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The invitation-link login path previously marked the account as
email-verified as a side effect of authenticating, without the user ever
proving control of the mailbox. Remove that branch so every account
goes through the standard signed-URL verification flow.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Replace exception text in 5xx JSON responses with stable, action-specific
messages so API consumers get a consistent payload regardless of which
underlying client (Guzzle, PDO, filesystem) raised the exception. The
previous responses concatenated the raw upstream error, which produced
inconsistent messages and unnecessary noise for clients trying to parse
errors programmatically.
Touched endpoints:
- GET /api/v1/hetzner/{locations,server-types,images,ssh-keys}
- POST /api/v1/servers/hetzner
- DELETE /api/v1/databases/{uuid}/backups/{uuid}
- DELETE /api/v1/databases/{uuid}/backups/{uuid}/executions/{uuid}
- /download/backup/{uuid}
The RateLimitException branch and AuthenticationException flow keep their
existing curated messages.
Adds Pest coverage for the four Hetzner GET endpoints to lock the response
shape on upstream failure.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add allowlist of backup file extensions (sql, sql.gz, tar, tgz, zip,
dump, bak, bson, archive, bz2, xz, and compound variants) and enforce
a 10 GiB maximum file size on the backup upload endpoint. Validation
runs early on each chunk using the dropzone metadata and again on the
assembled file. Also drops the unused createFilename helper and the
commented-out S3 block.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Tighten validation on volume name and host path inputs across Livewire + API storage endpoints and escape shell arguments in volume clone and compose preview cleanup paths.
Replace `ownedByCurrentTeam()` (session-based) with `ownedByCurrentTeamAPI($teamId)`
(explicit team ID) when resolving S3 storage in create_backup and update_backup.
Session-based team resolution is unreliable in API context where auth is token-based.
Add `S3Storage::ownedByCurrentTeamAPI(int $teamId)` scope and update feature tests
to use real model instances instead of Mockery mocks.