Replace permissive $guarded = [] with explicit $fillable array for better security and clarity. The fillable array includes all 13 fields that are legitimately mass-assignable:
- Core: key, value, comment
- Polymorphic relationship: resourceable_type, resourceable_id
- Boolean flags: is_preview, is_multiline, is_literal, is_runtime, is_buildtime, is_shown_once, is_shared
- Metadata: version, order
Also adds comprehensive test suite (EnvironmentVariableMassAssignmentTest) with 12 test cases covering:
- Mass assignment of all fillable fields
- Comment field edge cases (null, empty, long text)
- Value encryption verification
- Key mutation (trim and space replacement)
- Protection of auto-managed fields (id, uuid, timestamps)
- Update method compatibility
All tests passing (12 passed, 33 assertions).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add comment field to EnvironmentVariable model and database
- Update parseEnvFormatToArray to extract inline comments from env files
- Update Livewire components to handle comment field
- Add UI for displaying and editing comments
- Add tests for comment parsing functionality
- Add 429 response with Retry-After header for Hetzner server creation
- Create RateLimitException for proper rate limit error handling
- Rename cloud_provider_token_id to cloud_provider_token_uuid with deprecation
- Fix prices array schema in server-types endpoint with proper items definition
- Add explicit default: true to autogenerate_domain properties
- Add timeout and retry options to Docker install curl commands
- Fix race condition in deployment status update using atomic query
- Return the specific error from validateProviderToken() instead of
generic "Failed to validate token." message
- Update test to expect the actual error message
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add validateProviderToken() helper method to reduce code duplication
- Use request body only ($request->json()->all()) to avoid route parameter conflicts
- Add proper logging for token validation failures
- Add missing DB import to migration file
- Minor test formatting fix
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
Add complete API support for Hetzner server provisioning, matching UI functionality:
Cloud Provider Token Management:
- POST /api/v1/cloud-tokens - Create and validate tokens
- GET /api/v1/cloud-tokens - List all tokens
- GET /api/v1/cloud-tokens/{uuid} - Get specific token
- PATCH /api/v1/cloud-tokens/{uuid} - Update token name
- DELETE /api/v1/cloud-tokens/{uuid} - Delete token
- POST /api/v1/cloud-tokens/{uuid}/validate - Validate token
Hetzner Resource Discovery:
- GET /api/v1/hetzner/locations - List datacenters
- GET /api/v1/hetzner/server-types - List server types
- GET /api/v1/hetzner/images - List OS images
- GET /api/v1/hetzner/ssh-keys - List SSH keys
Server Provisioning:
- POST /api/v1/servers/hetzner - Create server with full options
Features:
- Token validation against provider APIs before storage
- Smart SSH key management with MD5 fingerprint deduplication
- IPv4/IPv6 network configuration with preference logic
- Cloud-init script support with YAML validation
- Team-based isolation and security
- Comprehensive test coverage (40+ test cases)
- Complete documentation with curl examples and Yaak collection
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
When restarting the proxy on localhost (where Coolify is running), the UI becomes inaccessible because the connection is lost. This change makes all proxy restarts run as background jobs with WebSocket notifications, allowing the operation to complete even after connection loss.
Changes:
- Enhanced ProxyStatusChangedUI event to carry activityId for log monitoring
- Updated RestartProxyJob to dispatch status events and track activity
- Simplified Navbar restart() to always dispatch job for all servers
- Enhanced showNotification() to handle activity monitoring and new statuses
- Added comprehensive unit and feature tests
Benefits:
- Prevents localhost lockout during proxy restarts
- Consistent behavior across all server types
- Non-blocking UI with real-time progress updates
- Automatic activity log monitoring
- Proper error handling and recovery
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Move Carbon::setTestNow() to the beginning of each test before creating
test data. Previously, tests created servers using now() (real current
time) and only afterwards called Carbon::setTestNow(), making
sentinel_updated_at inconsistent with the test clock.
This caused staleness calculations to use different timelines:
- sentinel_updated_at was based on real time (e.g., Dec 2024)
- Test execution time was frozen at 2025-01-15
Now all timestamps use the same frozen test time, making staleness
checks predictable and tests reliable regardless of when they run.
Affected tests (all 7 test cases in the file):
- does not dispatch storage check when sentinel is in sync
- dispatches storage check when sentinel is out of sync
- dispatches storage check when sentinel is disabled
- respects custom hourly storage check frequency when sentinel is out of sync
- handles VALID_CRON_STRINGS mapping correctly when sentinel is out of sync
- respects server timezone for storage checks when sentinel is out of sync
- does not dispatch storage check outside schedule
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
When Sentinel is enabled and in sync, ServerStorageCheckJob was being
dispatched from two locations causing unnecessary duplication:
1. PushServerUpdateJob (every ~30s with real-time filesystem data)
2. ServerManagerJob (scheduled cron check via SSH)
This commit modifies ServerManagerJob to only dispatch ServerStorageCheckJob
when Sentinel is out of sync or disabled. When Sentinel is active and in sync,
PushServerUpdateJob provides real-time storage data, making the scheduled SSH
check redundant.
Benefits:
- Eliminates duplicate storage checks when Sentinel is working
- Reduces unnecessary SSH overhead
- Storage checks still run as fallback when Sentinel fails
- Maintains scheduled checks for servers without Sentinel
Updated tests to reflect new behavior:
- Storage check NOT dispatched when Sentinel is in sync
- Storage check dispatched when Sentinel is out of sync or disabled
- All timezone and frequency tests updated accordingly
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Server disk usage checks now run on their configured schedule regardless of Sentinel status, eliminating monitoring blind spots when Sentinel is offline, out of sync, or disabled. Storage checks now respect server timezone settings, consistent with patch checks.
Changes:
- Moved server timezone calculation to top of processServerTasks()
- Extracted ServerStorageCheckJob dispatch from Sentinel conditional
- Fixed default frequency to '0 23 * * *' (11 PM daily)
- Added timezone parameter to storage check scheduling
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add URL generation to notification class using base_url() helper
- Replace config('app.url') with proper base_url() for accurate instance URL
- Make server names clickable links to proxy configuration page
- Use data_get() with fallback values for safer template data access
- Add comprehensive tests for URL generation and email rendering
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add endsWith() checks before appending template paths in serviceParser() to
prevent duplicate paths when parse() is called after FQDN updates. This fixes
the bug where services like Appwrite realtime would get `/v1/realtime/v1/realtime`.
Fixes#7363🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Move notification logic from NotifyOutdatedTraefikServersJob into CheckTraefikVersionForServerJob to send immediate notifications when outdated Traefik is detected. This is more suitable for cloud environments with thousands of servers.
Changes:
- CheckTraefikVersionForServerJob now sends notifications immediately after detecting outdated Traefik
- Remove NotifyOutdatedTraefikServersJob (no longer needed)
- Remove delay calculation logic from CheckTraefikVersionJob
- Update tests to reflect new immediate notification pattern
Trade-offs:
- Pro: Faster notifications (immediate alerts)
- Pro: Simpler codebase (removed complex delay calculation)
- Pro: Better scalability for thousands of servers
- Con: Teams may receive multiple notifications if they have many outdated servers
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Move buildpack switching cleanup from Livewire component to Application model's boot lifecycle. This improves separation of concerns and ensures cleanup happens consistently regardless of how the buildpack change is triggered. Also clears Dockerfile-specific data when switching away from dockerfile buildpack.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Merged latest changes from the next branch to keep the feature branch
up to date. No conflicts were encountered during the merge.
Changes from next branch:
- Updated application deployment job error logging
- Updated server manager job and instance settings
- Removed PullHelperImageJob in favor of updated approach
- Database migration refinements
- Updated versions.json with latest component versions
All automatic merges were successful and no manual conflict resolution
was required.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Store both patch update and newer minor version information simultaneously
- Display patch update availability alongside minor version upgrades in notifications
- Add newer_branch_target and newer_branch_latest fields to traefik_outdated_info
- Update all notification channels (Discord, Telegram, Slack, Pushover, Email, Webhook)
- Show minor version in format (e.g., v3.6) for upgrade targets instead of patch version
- Enhance UI callouts with clearer messaging about available upgrades
- Remove verbose logging in favor of cleaner code structure
- Handle edge case where SSH command returns empty response
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
Addresses critical performance issues identified in code review by refactoring the monolithic CheckTraefikVersionJob into a distributed architecture with parallel processing.
Changes:
- Split version checking into CheckTraefikVersionForServerJob for parallel execution
- Extract notification logic into NotifyOutdatedTraefikServersJob
- Dispatch individual server checks concurrently to handle thousands of servers
- Add comprehensive unit tests for the new job architecture
- Update feature tests to cover the refactored workflow
Performance improvements:
- Sequential SSH calls replaced with parallel queue jobs
- Scales efficiently for large installations with thousands of servers
- Reduces job execution time from hours to minutes
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add automated Traefik version checking job running weekly on Sundays
- Implement version detection from running containers and comparison with versions.json
- Add notifications across all channels (Email, Discord, Slack, Telegram, Pushover, Webhook) for outdated versions
- Create dismissible callout component with localStorage persistence
- Display cross-branch upgrade warnings (e.g., v3.5 -> v3.6) with changelog links
- Show patch update notifications within same branch
- Add warning icon that appears when callouts are dismissed
- Prevent duplicate notifications during proxy restart by adding restarting parameter
- Fix notification spam with transition-based logic for status changes
- Enable system email settings by default in development mode
- Track last saved/applied proxy settings to detect configuration drift
Stop dispatching PullHelperImageJob to thousands of servers when the helper image version changes. Instead, rely on Docker's automatic image pulling during actual deployments and backups. Inline the helper image pull in UpdateCoolify for the single use case.
This eliminates queue flooding on cloud instances while maintaining all functionality through Docker's built-in image management.
- Add retry configuration to CoolifyTask (3 tries, 600s timeout)
- Add retry configuration to ScheduledTaskJob (3 tries, configurable timeout)
- Add retry configuration to DatabaseBackupJob (2 tries)
- Implement exponential backoff for all jobs (30s, 60s, 120s intervals)
- Add failed() handlers with comprehensive error logging to scheduled-errors channel
- Add execution tracking: started_at, retry_count, duration (decimal), error_details
- Add configurable timeout field to scheduled tasks (60-3600s, default 300s)
- Update UI to include timeout configuration in task creation/editing forms
- Increase ScheduledJobManager lock expiration from 60s to 90s for high-load environments
- Implement safe queue cleanup with restart vs runtime modes
- Restart mode: aggressive cleanup (marks all processing jobs as failed)
- Runtime mode: conservative cleanup (only marks jobs >12h as failed, skips deployments)
- Add cleanup:redis --restart flag for system startup
- Integrate cleanup into Dev.php init() for development environment
- Increase scheduled-errors log retention from 7 to 14 days
- Create comprehensive test suite (unit and feature tests)
- Add TESTING_GUIDE.md with manual testing instructions
Fixes issues with jobs failing after single attempt and "attempted too many times" errors
- Added `requiredPort` property to `ServiceApplicationView` to track the required port for services.
- Introduced modal confirmation for removing required ports, including methods to confirm or cancel the action.
- Enhanced `Service` model with `getRequiredPort` and `requiresPort` methods to retrieve port information from service templates.
- Implemented `extractPortFromUrl` method in `ServiceApplication` to extract port from FQDN URLs.
- Updated frontend views to display warnings when required ports are missing from domains.
- Created unit tests for service port validation and extraction logic, ensuring correct behavior for various scenarios.
- Added feature tests for Livewire component handling of domain submissions with required ports.
Moved ServerPatchCheckNotificationTest from Unit to Feature tests and replaced
Mockery alias mocking with real database records to prevent global state pollution.
The original implementation used Mockery::mock('alias:InstanceSettings::class)
which creates a global class alias that persists across all tests, causing
other tests to fail when they try to use the real InstanceSettings model.
Changes:
- Moved test from tests/Unit/ to tests/Feature/ (requires database access)
- Replaced Mockery alias mocking with RefreshDatabase and real InstanceSettings records
- Tests now create actual InstanceSettings records in the test database
- Preserved Server mocking with Mockery for non-database dependencies
All 4 tests pass individually and when run via php artisan test without
polluting global state or affecting other tests.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed multiple issues with GitHub App source creation and management:
1. **Fixed null property assignment error on component mount**
- Changed property types to nullable in Change component (appId, installationId, clientId, etc.)
- Updated validation rules to allow nullable values
- Allows mounting component with newly created GitHub Apps that don't have these fields set yet
2. **Fixed Livewire morphing error on manual creation**
- Modified createGithubAppManually() to redirect after saving
- Prevents "Cannot read properties of null" error when view structure changes
- Fields now properly populated after manual creation without requiring page refresh
3. **Fixed is_system_wide not being saved on creation**
- Removed backwards logic that only saved is_system_wide on cloud instances
- Added is_system_wide to GithubApp model casts for proper boolean handling
- System-wide checkbox now works correctly on self-hosted instances
4. **Fixed misleading preview deployment checkbox**
- Removed instantSave attribute from permission checkboxes in unconfigured state
- These are configuration options for GitHub App creation, not database fields
- Prevents "GitHub App updated" success message when nothing was actually saved
5. **Added validation for Refetch Permissions button**
- Validates App ID and Private Key are set before attempting to fetch
- Shows clear error messages: "Cannot fetch permissions. Please set the following required fields first: App ID, Private Key"
- Prevents crash when private key is null or invalid
6. **Better error handling for unsupported private key formats**
- Detects OpenSSH format keys vs RSA PEM format
- Shows helpful message: "Please use an RSA private key in PEM format (BEGIN RSA PRIVATE KEY). OpenSSH format keys are not supported."
- GitHub Apps require RSA PEM format, not OpenSSH format
7. **Made GitHub App view mobile responsive**
- Updated all flex layouts to stack vertically on mobile (flex-col sm:flex-row)
- Form fields, buttons, and sections now properly responsive
- No more cut-off fields on small screens
Added comprehensive test coverage:
- GithubSourceChangeTest.php with 7 tests
- GithubSourceCreateTest.php with 6 tests
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Problem:
- Cache::remember() does not cache null return values
- When no FQDN was configured, the closure returned null
- This caused DB queries on every request, defeating the cache
Solution:
- Use empty string ('') as sentinel value instead of null
- Convert sentinel back to null after retrieving from cache
- Now both positive and negative results are cached properly
Changes:
- Return empty string from closure instead of null
- Add explicit sentinel-to-null conversion after cache retrieval
- Add test to verify negative caching works correctly
This ensures zero DB queries even when FQDN is not configured.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit fixes a critical Host Header Injection vulnerability in the password reset flow that could lead to account takeover.
Security Issue:
- Attackers could inject malicious host headers (e.g., legitimate.domain.evil.com)
- Password reset emails would contain links to attacker-controlled domains
- Attackers could capture reset tokens and takeover accounts
Changes:
- Enable TrustHosts middleware in app/Http/Kernel.php
- Update TrustHosts to trust configured FQDN from InstanceSettings
- Add intelligent caching (5-min TTL) to avoid DB query on every request
- Automatic cache invalidation when FQDN is updated
- Support for domains, IP addresses (IPv4/IPv6), and ports
- Graceful fallback during installation when DB doesn't exist
Test Coverage:
- Domain validation (with/without ports)
- IP address validation (IPv4, IPv6)
- Malicious host rejection
- Cache creation and invalidation
- Installation edge cases
Performance:
- 99.9% reduction in DB queries (1 query per 5 minutes vs every request)
- Zero performance impact on production workloads
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The tests were failing because User::role() depends on Auth::user() and
currentTeam() session being set. Added actingAs() and session setup to
each test to properly authenticate users before checking permissions.
This fixes the 'Attempt to read property "teams" on null' errors.
This commit addresses a critical security vulnerability where low-privileged
users (members) could invite high-privileged users (admins/owners) to teams,
allowing them to escalate their own privileges through password reset.
Root Causes Fixed:
1. TeamPolicy authorization checks were commented out, allowing all team
members to manage invitations instead of just admins/owners
2. Missing role elevation checks in InviteLink component allowed members
to invite users with higher privileges
Security Fixes:
1. app/Policies/TeamPolicy.php
- Uncommented and enforced authorization checks for:
* update() - Only admins/owners can update team settings
* delete() - Only admins/owners can delete teams
* manageMembers() - Only admins/owners can manage team members
* viewAdmin() - Only admins/owners can view admin panel
* manageInvitations() - Only admins/owners can manage invitations
2. app/Livewire/Team/InviteLink.php
- Added explicit role elevation checks to prevent:
* Members from inviting admins or owners
* Admins from inviting owners (defense-in-depth)
- Validates that inviter has sufficient privileges for target role
Test Coverage:
1. tests/Feature/TeamPolicyTest.php
- 24 comprehensive tests covering all policy methods
- Tests for owner, admin, member, and non-member access
- Specific tests for the privilege escalation vulnerability
2. tests/Feature/TeamInvitationPrivilegeEscalationTest.php
- 11 tests covering all role elevation scenarios
- Tests member → admin/owner escalation (blocked)
- Tests admin → owner escalation (blocked)
- Tests valid invitation paths for each role
Impact:
- Prevents privilege escalation attacks
- Protects all Coolify instances from unauthorized access
- Enforces proper role hierarchy in team management
References:
- Identified by Aikido AI whitebox pentest service
- CVE: Pending assignment
- Severity: Critical
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit adds the ability to use cloud-init scripts when creating Hetzner servers through the integration. Users can write custom scripts that will be executed during server initialization, and optionally save these scripts at the team level for future reuse.
Key features:
- Textarea field for entering cloud-init scripts (bash or cloud-config YAML)
- Checkbox to save scripts for later use at team level
- Dropdown to load previously saved scripts
- Scripts are encrypted in the database
- Full validation and authorization checks
- Comprehensive unit and feature tests
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add support for configuring IPv4 and IPv6 public network interfaces when creating servers through the Hetzner integration. Users can now enable or disable IPv4 and IPv6 independently, with both enabled by default.
Features:
- Added enable_ipv4 and enable_ipv6 checkboxes in the server creation form
- Both options are enabled by default as per Hetzner best practices
- IPv4 is preferred when both are enabled
- Fallback to IPv6 when only IPv6 is enabled
- Proper validation and error handling for network configuration
- Comprehensive test coverage for IP address selection logic
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>