diff --git a/.AI_INSTRUCTIONS_SYNC.md b/.AI_INSTRUCTIONS_SYNC.md
deleted file mode 100644
index b268064af..000000000
--- a/.AI_INSTRUCTIONS_SYNC.md
+++ /dev/null
@@ -1,41 +0,0 @@
-# AI Instructions Synchronization Guide
-
-**This file has moved!**
-
-All AI documentation and synchronization guidelines are now in the `.ai/` directory.
-
-## New Locations
-
-- **Sync Guide**: [.ai/meta/sync-guide.md](.ai/meta/sync-guide.md)
-- **Maintaining Docs**: [.ai/meta/maintaining-docs.md](.ai/meta/maintaining-docs.md)
-- **Documentation Hub**: [.ai/README.md](.ai/README.md)
-
-## Quick Overview
-
-All AI instructions are now organized in `.ai/` directory:
-
-```
-.ai/
-├── README.md # Navigation hub
-├── core/ # Project information
-├── development/ # Dev workflows
-├── patterns/ # Code patterns
-└── meta/ # Documentation guides
-```
-
-### For AI Assistants
-
-- **Claude Code**: Use `CLAUDE.md` (references `.ai/` files)
-- **Cursor IDE**: Use `.cursor/rules/coolify-ai-docs.mdc` (references `.ai/` files)
-- **All Tools**: Browse `.ai/` directory for detailed documentation
-
-### Key Principles
-
-1. **Single Source of Truth**: Each piece of information exists in ONE file only
-2. **Cross-Reference**: Other files reference the source, don't duplicate
-3. **Organized by Topic**: Core, Development, Patterns, Meta
-4. **Version Consistency**: All versions in `.ai/core/technology-stack.md`
-
-## For More Information
-
-See [.ai/meta/sync-guide.md](.ai/meta/sync-guide.md) for complete synchronization guidelines and [.ai/meta/maintaining-docs.md](.ai/meta/maintaining-docs.md) for documentation maintenance instructions.
diff --git a/.agents/skills/debugging-output-and-previewing-html-using-ray/SKILL.md b/.agents/skills/debugging-output-and-previewing-html-using-ray/SKILL.md
new file mode 100644
index 000000000..4583bd56e
--- /dev/null
+++ b/.agents/skills/debugging-output-and-previewing-html-using-ray/SKILL.md
@@ -0,0 +1,414 @@
+---
+name: debugging-output-and-previewing-html-using-ray
+description: Use when user says "send to Ray," "show in Ray," "debug in Ray," "log to Ray," "display in Ray," or wants to visualize data, debug output, or show diagrams in the Ray desktop application.
+metadata:
+ author: Spatie
+ tags:
+ - debugging
+ - logging
+ - visualization
+ - ray
+---
+
+# Ray Skill
+
+## Overview
+
+Ray is Spatie's desktop debugging application for developers. Send data directly to Ray by making HTTP requests to its local server.
+
+This can be useful for debugging applications, or to preview design, logos, or other visual content.
+
+This is what the `ray()` PHP function does under the hood.
+
+## Connection Details
+
+| Setting | Default | Environment Variable |
+|---------|---------|---------------------|
+| Host | `localhost` | `RAY_HOST` |
+| Port | `23517` | `RAY_PORT` |
+| URL | `http://localhost:23517/` | - |
+
+## Request Format
+
+**Method:** POST
+**Content-Type:** `application/json`
+**User-Agent:** `Ray 1.0`
+
+### Basic Request Structure
+
+```json
+{
+ "uuid": "unique-identifier-for-this-ray-instance",
+ "payloads": [
+ {
+ "type": "log",
+ "content": { },
+ "origin": {
+ "file": "/path/to/file.php",
+ "line_number": 42,
+ "hostname": "my-machine"
+ }
+ }
+ ],
+ "meta": {
+ "ray_package_version": "1.0.0"
+ }
+}
+```
+
+### Fields
+
+| Field | Type | Description |
+|-------|------|-------------|
+| `uuid` | string | Unique identifier for this Ray instance. Reuse the same UUID to update an existing entry. |
+| `payloads` | array | Array of payload objects to send |
+| `meta` | object | Optional metadata (ray_package_version, project_name, php_version) |
+
+### Origin Object
+
+Every payload includes origin information:
+
+```json
+{
+ "file": "/Users/dev/project/app/Controller.php",
+ "line_number": 42,
+ "hostname": "dev-machine"
+}
+```
+
+## Payload Types
+
+### Log (Send Values)
+
+```json
+{
+ "type": "log",
+ "content": {
+ "values": ["Hello World", 42, {"key": "value"}]
+ },
+ "origin": { "file": "test.php", "line_number": 1, "hostname": "localhost" }
+}
+```
+
+### Custom (HTML/Text Content)
+
+```json
+{
+ "type": "custom",
+ "content": {
+ "content": "
HTML Content
With formatting
",
+ "label": "My Label"
+ },
+ "origin": { "file": "test.php", "line_number": 1, "hostname": "localhost" }
+}
+```
+
+### Table
+
+```json
+{
+ "type": "table",
+ "content": {
+ "values": {"name": "John", "email": "john@example.com", "age": 30},
+ "label": "User Data"
+ },
+ "origin": { "file": "test.php", "line_number": 1, "hostname": "localhost" }
+}
+```
+
+### Color
+
+Set the color of the preceding log entry:
+
+```json
+{
+ "type": "color",
+ "content": {
+ "color": "green"
+ },
+ "origin": { "file": "test.php", "line_number": 1, "hostname": "localhost" }
+}
+```
+
+**Available colors:** `green`, `orange`, `red`, `purple`, `blue`, `gray`
+
+### Screen Color
+
+Set the background color of the screen:
+
+```json
+{
+ "type": "screen_color",
+ "content": {
+ "color": "green"
+ },
+ "origin": { "file": "test.php", "line_number": 1, "hostname": "localhost" }
+}
+```
+
+### Label
+
+Add a label to the entry:
+
+```json
+{
+ "type": "label",
+ "content": {
+ "label": "Important"
+ },
+ "origin": { "file": "test.php", "line_number": 1, "hostname": "localhost" }
+}
+```
+
+### Size
+
+Set the size of the entry:
+
+```json
+{
+ "type": "size",
+ "content": {
+ "size": "lg"
+ },
+ "origin": { "file": "test.php", "line_number": 1, "hostname": "localhost" }
+}
+```
+
+**Available sizes:** `sm`, `lg`
+
+### Notify (Desktop Notification)
+
+```json
+{
+ "type": "notify",
+ "content": {
+ "value": "Task completed!"
+ },
+ "origin": { "file": "test.php", "line_number": 1, "hostname": "localhost" }
+}
+```
+
+### New Screen
+
+```json
+{
+ "type": "new_screen",
+ "content": {
+ "name": "Debug Session"
+ },
+ "origin": { "file": "test.php", "line_number": 1, "hostname": "localhost" }
+}
+```
+
+### Measure (Timing)
+
+```json
+{
+ "type": "measure",
+ "content": {
+ "name": "my-timer",
+ "is_new_timer": true,
+ "total_time": 0,
+ "time_since_last_call": 0,
+ "max_memory_usage_during_total_time": 0,
+ "max_memory_usage_since_last_call": 0
+ },
+ "origin": { "file": "test.php", "line_number": 1, "hostname": "localhost" }
+}
+```
+
+For subsequent measurements, set `is_new_timer: false` and provide actual timing values.
+
+### Simple Payloads (No Content)
+
+These payloads only need a `type` and empty `content`:
+
+```json
+{
+ "type": "separator",
+ "content": {},
+ "origin": { "file": "test.php", "line_number": 1, "hostname": "localhost" }
+}
+```
+
+| Type | Purpose |
+|------|---------|
+| `separator` | Add visual divider |
+| `clear_all` | Clear all entries |
+| `hide` | Hide this entry |
+| `remove` | Remove this entry |
+| `confetti` | Show confetti animation |
+| `show_app` | Bring Ray to foreground |
+| `hide_app` | Hide Ray window |
+
+## Combining Multiple Payloads
+
+Send multiple payloads in one request. Use the same `uuid` to apply modifiers (color, label, size) to a log entry:
+
+```json
+{
+ "uuid": "abc-123",
+ "payloads": [
+ {
+ "type": "log",
+ "content": { "values": ["Important message"] },
+ "origin": { "file": "test.php", "line_number": 1, "hostname": "localhost" }
+ },
+ {
+ "type": "color",
+ "content": { "color": "red" },
+ "origin": { "file": "test.php", "line_number": 1, "hostname": "localhost" }
+ },
+ {
+ "type": "label",
+ "content": { "label": "ERROR" },
+ "origin": { "file": "test.php", "line_number": 1, "hostname": "localhost" }
+ },
+ {
+ "type": "size",
+ "content": { "size": "lg" },
+ "origin": { "file": "test.php", "line_number": 1, "hostname": "localhost" }
+ }
+ ],
+ "meta": {}
+}
+```
+
+## Example: Complete Request
+
+Send a green, labeled log message:
+
+```bash
+curl -X POST http://localhost:23517/ \
+ -H "Content-Type: application/json" \
+ -H "User-Agent: Ray 1.0" \
+ -d '{
+ "uuid": "my-unique-id-123",
+ "payloads": [
+ {
+ "type": "log",
+ "content": {
+ "values": ["User logged in", {"user_id": 42, "name": "John"}]
+ },
+ "origin": {
+ "file": "/app/AuthController.php",
+ "line_number": 55,
+ "hostname": "dev-server"
+ }
+ },
+ {
+ "type": "color",
+ "content": { "color": "green" },
+ "origin": { "file": "/app/AuthController.php", "line_number": 55, "hostname": "dev-server" }
+ },
+ {
+ "type": "label",
+ "content": { "label": "Auth" },
+ "origin": { "file": "/app/AuthController.php", "line_number": 55, "hostname": "dev-server" }
+ }
+ ],
+ "meta": {
+ "project_name": "my-app"
+ }
+ }'
+```
+
+## Availability Check
+
+Before sending data, you can check if Ray is running:
+
+```
+GET http://localhost:23517/_availability_check
+```
+
+Ray responds with HTTP 404 when available (the endpoint doesn't exist, but the server is running).
+
+## Getting Ray Information
+
+### Get Windows
+
+Retrieve information about all open Ray windows:
+
+```
+GET http://localhost:23517/windows
+```
+
+Returns an array of window objects with their IDs and names:
+
+```json
+[
+ {"id": 1, "name": "Window 1"},
+ {"id": 2, "name": "Debug Session"}
+]
+```
+
+### Get Theme Colors
+
+Retrieve the current theme colors being used by Ray:
+
+```
+GET http://localhost:23517/theme
+```
+
+Returns the theme information including color palette:
+
+```json
+{
+ "name": "Dark",
+ "colors": {
+ "primary": "#000000",
+ "secondary": "#1a1a1a",
+ "accent": "#3b82f6"
+ }
+}
+```
+
+**Use Case:** When sending custom HTML content to Ray, use these theme colors to ensure your content matches Ray's current theme and looks visually integrated.
+
+**Example:** Send HTML with matching colors:
+
+```bash
+
+# First, get the theme
+
+THEME=$(curl -s http://localhost:23517/theme)
+PRIMARY_COLOR=$(echo $THEME | jq -r '.colors.primary')
+
+# Then send HTML using those colors
+
+curl -X POST http://localhost:23517/ \
+ -H "Content-Type: application/json" \
+ -d '{
+ "uuid": "theme-matched-html",
+ "payloads": [{
+ "type": "custom",
+ "content": {
+ "content": "
Themed Content
",
+ "label": "Themed HTML"
+ },
+ "origin": {"file": "script.sh", "line_number": 1, "hostname": "localhost"}
+ }]
+ }'
+```
+
+## Payload Type Reference
+
+| Type | Content Fields | Purpose |
+|------|----------------|---------|
+| `log` | `values` (array) | Send values to Ray |
+| `custom` | `content`, `label` | HTML or text content |
+| `table` | `values`, `label` | Display as table |
+| `color` | `color` | Set entry color |
+| `screen_color` | `color` | Set screen background |
+| `label` | `label` | Add label to entry |
+| `size` | `size` | Set entry size (sm/lg) |
+| `notify` | `value` | Desktop notification |
+| `new_screen` | `name` | Create new screen |
+| `measure` | `name`, `is_new_timer`, timing fields | Performance timing |
+| `separator` | (empty) | Visual divider |
+| `clear_all` | (empty) | Clear all entries |
+| `hide` | (empty) | Hide entry |
+| `remove` | (empty) | Remove entry |
+| `confetti` | (empty) | Confetti animation |
+| `show_app` | (empty) | Show Ray window |
+| `hide_app` | (empty) | Hide Ray window |
\ No newline at end of file
diff --git a/.agents/skills/developing-with-fortify/SKILL.md b/.agents/skills/developing-with-fortify/SKILL.md
new file mode 100644
index 000000000..2ff71a4b4
--- /dev/null
+++ b/.agents/skills/developing-with-fortify/SKILL.md
@@ -0,0 +1,116 @@
+---
+name: developing-with-fortify
+description: Laravel Fortify headless authentication backend development. Activate when implementing authentication features including login, registration, password reset, email verification, two-factor authentication (2FA/TOTP), profile updates, headless auth, authentication scaffolding, or auth guards in Laravel applications.
+---
+
+# Laravel Fortify Development
+
+Fortify is a headless authentication backend that provides authentication routes and controllers for Laravel applications.
+
+## Documentation
+
+Use `search-docs` for detailed Laravel Fortify patterns and documentation.
+
+## Usage
+
+- **Routes**: Use `list-routes` with `only_vendor: true` and `action: "Fortify"` to see all registered endpoints
+- **Actions**: Check `app/Actions/Fortify/` for customizable business logic (user creation, password validation, etc.)
+- **Config**: See `config/fortify.php` for all options including features, guards, rate limiters, and username field
+- **Contracts**: Look in `Laravel\Fortify\Contracts\` for overridable response classes (`LoginResponse`, `LogoutResponse`, etc.)
+- **Views**: All view callbacks are set in `FortifyServiceProvider::boot()` using `Fortify::loginView()`, `Fortify::registerView()`, etc.
+
+## Available Features
+
+Enable in `config/fortify.php` features array:
+
+- `Features::registration()` - User registration
+- `Features::resetPasswords()` - Password reset via email
+- `Features::emailVerification()` - Requires User to implement `MustVerifyEmail`
+- `Features::updateProfileInformation()` - Profile updates
+- `Features::updatePasswords()` - Password changes
+- `Features::twoFactorAuthentication()` - 2FA with QR codes and recovery codes
+
+> Use `search-docs` for feature configuration options and customization patterns.
+
+## Setup Workflows
+
+### Two-Factor Authentication Setup
+
+```
+- [ ] Add TwoFactorAuthenticatable trait to User model
+- [ ] Enable feature in config/fortify.php
+- [ ] Run migrations for 2FA columns
+- [ ] Set up view callbacks in FortifyServiceProvider
+- [ ] Create 2FA management UI
+- [ ] Test QR code and recovery codes
+```
+
+> Use `search-docs` for TOTP implementation and recovery code handling patterns.
+
+### Email Verification Setup
+
+```
+- [ ] Enable emailVerification feature in config
+- [ ] Implement MustVerifyEmail interface on User model
+- [ ] Set up verifyEmailView callback
+- [ ] Add verified middleware to protected routes
+- [ ] Test verification email flow
+```
+
+> Use `search-docs` for MustVerifyEmail implementation patterns.
+
+### Password Reset Setup
+
+```
+- [ ] Enable resetPasswords feature in config
+- [ ] Set up requestPasswordResetLinkView callback
+- [ ] Set up resetPasswordView callback
+- [ ] Define password.reset named route (if views disabled)
+- [ ] Test reset email and link flow
+```
+
+> Use `search-docs` for custom password reset flow patterns.
+
+### SPA Authentication Setup
+
+```
+- [ ] Set 'views' => false in config/fortify.php
+- [ ] Install and configure Laravel Sanctum
+- [ ] Use 'web' guard in fortify config
+- [ ] Set up CSRF token handling
+- [ ] Test XHR authentication flows
+```
+
+> Use `search-docs` for integration and SPA authentication patterns.
+
+## Best Practices
+
+### Custom Authentication Logic
+
+Override authentication behavior using `Fortify::authenticateUsing()` for custom user retrieval or `Fortify::authenticateThrough()` to customize the authentication pipeline. Override response contracts in `AppServiceProvider` for custom redirects.
+
+### Registration Customization
+
+Modify `app/Actions/Fortify/CreateNewUser.php` to customize user creation logic, validation rules, and additional fields.
+
+### Rate Limiting
+
+Configure via `fortify.limiters.login` in config. Default configuration throttles by username + IP combination.
+
+## Key Endpoints
+
+| Feature | Method | Endpoint |
+|------------------------|----------|---------------------------------------------|
+| Login | POST | `/login` |
+| Logout | POST | `/logout` |
+| Register | POST | `/register` |
+| Password Reset Request | POST | `/forgot-password` |
+| Password Reset | POST | `/reset-password` |
+| Email Verify Notice | GET | `/email/verify` |
+| Resend Verification | POST | `/email/verification-notification` |
+| Password Confirm | POST | `/user/confirm-password` |
+| Enable 2FA | POST | `/user/two-factor-authentication` |
+| Confirm 2FA | POST | `/user/confirmed-two-factor-authentication` |
+| 2FA Challenge | POST | `/two-factor-challenge` |
+| Get QR Code | GET | `/user/two-factor-qr-code` |
+| Recovery Codes | GET/POST | `/user/two-factor-recovery-codes` |
\ No newline at end of file
diff --git a/.agents/skills/livewire-development/SKILL.md b/.agents/skills/livewire-development/SKILL.md
new file mode 100644
index 000000000..755d20713
--- /dev/null
+++ b/.agents/skills/livewire-development/SKILL.md
@@ -0,0 +1,131 @@
+---
+name: livewire-development
+description: >-
+ Develops reactive Livewire 3 components. Activates when creating, updating, or modifying
+ Livewire components; working with wire:model, wire:click, wire:loading, or any wire: directives;
+ adding real-time updates, loading states, or reactivity; debugging component behavior;
+ writing Livewire tests; or when the user mentions Livewire, component, counter, or reactive UI.
+---
+
+# Livewire Development
+
+## When to Apply
+
+Activate this skill when:
+- Creating new Livewire components
+- Modifying existing component state or behavior
+- Debugging reactivity or lifecycle issues
+- Writing Livewire component tests
+- Adding Alpine.js interactivity to components
+- Working with wire: directives
+
+## Documentation
+
+Use `search-docs` for detailed Livewire 3 patterns and documentation.
+
+## Basic Usage
+
+### Creating Components
+
+Use the `php artisan make:livewire [Posts\CreatePost]` Artisan command to create new components.
+
+### Fundamental Concepts
+
+- State should live on the server, with the UI reflecting it.
+- All Livewire requests hit the Laravel backend; they're like regular HTTP requests. Always validate form data and run authorization checks in Livewire actions.
+
+## Livewire 3 Specifics
+
+### Key Changes From Livewire 2
+
+These things changed in Livewire 3, but may not have been updated in this application. Verify this application's setup to ensure you follow existing conventions.
+- Use `wire:model.live` for real-time updates, `wire:model` is now deferred by default.
+- Components now use the `App\Livewire` namespace (not `App\Http\Livewire`).
+- Use `$this->dispatch()` to dispatch events (not `emit` or `dispatchBrowserEvent`).
+- Use the `components.layouts.app` view as the typical layout path (not `layouts.app`).
+
+### New Directives
+
+- `wire:show`, `wire:transition`, `wire:cloak`, `wire:offline`, `wire:target` are available for use.
+
+### Alpine Integration
+
+- Alpine is now included with Livewire; don't manually include Alpine.js.
+- Plugins included with Alpine: persist, intersect, collapse, and focus.
+
+## Best Practices
+
+### Component Structure
+
+- Livewire components require a single root element.
+- Use `wire:loading` and `wire:dirty` for delightful loading states.
+
+### Using Keys in Loops
+
+
+
+@foreach ($items as $item)
+
+ {{ $item->name }}
+
+@endforeach
+
+
+
+### Lifecycle Hooks
+
+Prefer lifecycle hooks like `mount()`, `updatedFoo()` for initialization and reactive side effects:
+
+
+
+public function mount(User $user) { $this->user = $user; }
+public function updatedSearch() { $this->resetPage(); }
+
+
+
+## JavaScript Hooks
+
+You can listen for `livewire:init` to hook into Livewire initialization:
+
+
+
+document.addEventListener('livewire:init', function () {
+ Livewire.hook('request', ({ fail }) => {
+ if (fail && fail.status === 419) {
+ alert('Your session expired');
+ }
+ });
+
+ Livewire.hook('message.failed', (message, component) => {
+ console.error(message);
+ });
+});
+
+
+
+## Testing
+
+
+
+Livewire::test(Counter::class)
+ ->assertSet('count', 0)
+ ->call('increment')
+ ->assertSet('count', 1)
+ ->assertSee(1)
+ ->assertStatus(200);
+
+
+
+
+
+$this->get('/posts/create')
+ ->assertSeeLivewire(CreatePost::class);
+
+
+
+## Common Pitfalls
+
+- Forgetting `wire:key` in loops causes unexpected behavior when items change
+- Using `wire:model` expecting real-time updates (use `wire:model.live` instead in v3)
+- Not validating/authorizing in Livewire actions (treat them like HTTP requests)
+- Including Alpine.js separately when it's already bundled with Livewire 3
\ No newline at end of file
diff --git a/.agents/skills/pest-testing/SKILL.md b/.agents/skills/pest-testing/SKILL.md
new file mode 100644
index 000000000..67455e7e6
--- /dev/null
+++ b/.agents/skills/pest-testing/SKILL.md
@@ -0,0 +1,174 @@
+---
+name: pest-testing
+description: >-
+ Tests applications using the Pest 4 PHP framework. Activates when writing tests, creating unit or feature
+ tests, adding assertions, testing Livewire components, browser testing, debugging test failures,
+ working with datasets or mocking; or when the user mentions test, spec, TDD, expects, assertion,
+ coverage, or needs to verify functionality works.
+---
+
+# Pest Testing 4
+
+## When to Apply
+
+Activate this skill when:
+
+- Creating new tests (unit, feature, or browser)
+- Modifying existing tests
+- Debugging test failures
+- Working with browser testing or smoke testing
+- Writing architecture tests or visual regression tests
+
+## Documentation
+
+Use `search-docs` for detailed Pest 4 patterns and documentation.
+
+## Basic Usage
+
+### Creating Tests
+
+All tests must be written using Pest. Use `php artisan make:test --pest {name}`.
+
+### Test Organization
+
+- Unit/Feature tests: `tests/Feature` and `tests/Unit` directories.
+- Browser tests: `tests/Browser/` directory.
+- Do NOT remove tests without approval - these are core application code.
+
+### Basic Test Structure
+
+
+
+it('is true', function () {
+ expect(true)->toBeTrue();
+});
+
+
+
+### Running Tests
+
+- Run minimal tests with filter before finalizing: `php artisan test --compact --filter=testName`.
+- Run all tests: `php artisan test --compact`.
+- Run file: `php artisan test --compact tests/Feature/ExampleTest.php`.
+
+## Assertions
+
+Use specific assertions (`assertSuccessful()`, `assertNotFound()`) instead of `assertStatus()`:
+
+
+
+it('returns all', function () {
+ $this->postJson('/api/docs', [])->assertSuccessful();
+});
+
+
+
+| Use | Instead of |
+|-----|------------|
+| `assertSuccessful()` | `assertStatus(200)` |
+| `assertNotFound()` | `assertStatus(404)` |
+| `assertForbidden()` | `assertStatus(403)` |
+
+## Mocking
+
+Import mock function before use: `use function Pest\Laravel\mock;`
+
+## Datasets
+
+Use datasets for repetitive tests (validation rules, etc.):
+
+
+
+it('has emails', function (string $email) {
+ expect($email)->not->toBeEmpty();
+})->with([
+ 'james' => 'james@laravel.com',
+ 'taylor' => 'taylor@laravel.com',
+]);
+
+
+
+## Pest 4 Features
+
+| Feature | Purpose |
+|---------|---------|
+| Browser Testing | Full integration tests in real browsers |
+| Smoke Testing | Validate multiple pages quickly |
+| Visual Regression | Compare screenshots for visual changes |
+| Test Sharding | Parallel CI runs |
+| Architecture Testing | Enforce code conventions |
+
+### Browser Test Example
+
+Browser tests run in real browsers for full integration testing:
+
+- Browser tests live in `tests/Browser/`.
+- Use Laravel features like `Event::fake()`, `assertAuthenticated()`, and model factories.
+- Use `RefreshDatabase` for clean state per test.
+- Interact with page: click, type, scroll, select, submit, drag-and-drop, touch gestures.
+- Test on multiple browsers (Chrome, Firefox, Safari) if requested.
+- Test on different devices/viewports (iPhone 14 Pro, tablets) if requested.
+- Switch color schemes (light/dark mode) when appropriate.
+- Take screenshots or pause tests for debugging.
+
+
+
+it('may reset the password', function () {
+ Notification::fake();
+
+ $this->actingAs(User::factory()->create());
+
+ $page = visit('/sign-in');
+
+ $page->assertSee('Sign In')
+ ->assertNoJavaScriptErrors()
+ ->click('Forgot Password?')
+ ->fill('email', 'nuno@laravel.com')
+ ->click('Send Reset Link')
+ ->assertSee('We have emailed your password reset link!');
+
+ Notification::assertSent(ResetPassword::class);
+});
+
+
+
+### Smoke Testing
+
+Quickly validate multiple pages have no JavaScript errors:
+
+
+
+$pages = visit(['/', '/about', '/contact']);
+
+$pages->assertNoJavaScriptErrors()->assertNoConsoleLogs();
+
+
+
+### Visual Regression Testing
+
+Capture and compare screenshots to detect visual changes.
+
+### Test Sharding
+
+Split tests across parallel processes for faster CI runs.
+
+### Architecture Testing
+
+Pest 4 includes architecture testing (from Pest 3):
+
+
+
+arch('controllers')
+ ->expect('App\Http\Controllers')
+ ->toExtendNothing()
+ ->toHaveSuffix('Controller');
+
+
+
+## Common Pitfalls
+
+- Not importing `use function Pest\Laravel\mock;` before using mock
+- Using `assertStatus(200)` instead of `assertSuccessful()`
+- Forgetting datasets for repetitive validation tests
+- Deleting tests without approval
+- Forgetting `assertNoJavaScriptErrors()` in browser tests
\ No newline at end of file
diff --git a/.agents/skills/tailwindcss-development/SKILL.md b/.agents/skills/tailwindcss-development/SKILL.md
new file mode 100644
index 000000000..12bd896bb
--- /dev/null
+++ b/.agents/skills/tailwindcss-development/SKILL.md
@@ -0,0 +1,124 @@
+---
+name: tailwindcss-development
+description: >-
+ Styles applications using Tailwind CSS v4 utilities. Activates when adding styles, restyling components,
+ working with gradients, spacing, layout, flex, grid, responsive design, dark mode, colors,
+ typography, or borders; or when the user mentions CSS, styling, classes, Tailwind, restyle,
+ hero section, cards, buttons, or any visual/UI changes.
+---
+
+# Tailwind CSS Development
+
+## When to Apply
+
+Activate this skill when:
+
+- Adding styles to components or pages
+- Working with responsive design
+- Implementing dark mode
+- Extracting repeated patterns into components
+- Debugging spacing or layout issues
+
+## Documentation
+
+Use `search-docs` for detailed Tailwind CSS v4 patterns and documentation.
+
+## Basic Usage
+
+- Use Tailwind CSS classes to style HTML. Check and follow existing Tailwind conventions in the project before introducing new patterns.
+- Offer to extract repeated patterns into components that match the project's conventions (e.g., Blade, JSX, Vue).
+- Consider class placement, order, priority, and defaults. Remove redundant classes, add classes to parent or child elements carefully to reduce repetition, and group elements logically.
+
+## Tailwind CSS v4 Specifics
+
+- Always use Tailwind CSS v4 and avoid deprecated utilities.
+- `corePlugins` is not supported in Tailwind v4.
+
+### CSS-First Configuration
+
+In Tailwind v4, configuration is CSS-first using the `@theme` directive — no separate `tailwind.config.js` file is needed:
+
+
+@theme {
+ --color-brand: oklch(0.72 0.11 178);
+}
+
+
+### Import Syntax
+
+In Tailwind v4, import Tailwind with a regular CSS `@import` statement instead of the `@tailwind` directives used in v3:
+
+
+- @tailwind base;
+- @tailwind components;
+- @tailwind utilities;
++ @import "tailwindcss";
+
+
+### Replaced Utilities
+
+Tailwind v4 removed deprecated utilities. Use the replacements shown below. Opacity values remain numeric.
+
+| Deprecated | Replacement |
+|------------|-------------|
+| bg-opacity-* | bg-black/* |
+| text-opacity-* | text-black/* |
+| border-opacity-* | border-black/* |
+| divide-opacity-* | divide-black/* |
+| ring-opacity-* | ring-black/* |
+| placeholder-opacity-* | placeholder-black/* |
+| flex-shrink-* | shrink-* |
+| flex-grow-* | grow-* |
+| overflow-ellipsis | text-ellipsis |
+| decoration-slice | box-decoration-slice |
+| decoration-clone | box-decoration-clone |
+
+## Spacing
+
+Use `gap` utilities instead of margins for spacing between siblings:
+
+
+
+
Item 1
+
Item 2
+
+
+
+## Dark Mode
+
+If existing pages and components support dark mode, new pages and components must support it the same way, typically using the `dark:` variant:
+
+
+
-```
-
-## Database Development
-
-### Migration Best Practices
-```php
-// Create descriptive migration files
-class CreateApplicationDeploymentQueuesTable extends Migration
-{
- public function up(): void
- {
- Schema::create('application_deployment_queues', function (Blueprint $table) {
- $table->id();
- $table->foreignId('application_id')->constrained()->cascadeOnDelete();
- $table->string('status')->default('queued');
- $table->string('commit_sha')->nullable();
- $table->text('build_logs')->nullable();
- $table->text('deployment_logs')->nullable();
- $table->timestamp('started_at')->nullable();
- $table->timestamp('finished_at')->nullable();
- $table->timestamps();
-
- $table->index(['application_id', 'status']);
- $table->index('created_at');
- });
- }
-
- public function down(): void
- {
- Schema::dropIfExists('application_deployment_queues');
- }
-}
-```
-
-### Model Factory Development
-```php
-// Create comprehensive factories for testing
-class ApplicationFactory extends Factory
-{
- protected $model = Application::class;
-
- public function definition(): array
- {
- return [
- 'name' => $this->faker->words(2, true),
- 'fqdn' => $this->faker->domainName,
- 'git_repository' => 'https://github.com/' . $this->faker->userName . '/' . $this->faker->word . '.git',
- 'git_branch' => 'main',
- 'build_pack' => BuildPack::NIXPACKS,
- 'server_id' => Server::factory(),
- 'environment_id' => Environment::factory(),
- ];
- }
-
- public function withCustomDomain(): static
- {
- return $this->state(fn (array $attributes) => [
- 'fqdn' => $this->faker->domainName,
- ]);
- }
-}
-```
-
-## API Development
-
-### Controller Standards
-```php
-class ApplicationController extends Controller
-{
- public function __construct()
- {
- $this->middleware('auth:sanctum');
- $this->middleware('team.access');
- }
-
- public function index(Request $request): AnonymousResourceCollection
- {
- $applications = $request->user()
- ->currentTeam
- ->applications()
- ->with(['server', 'environment', 'latestDeployment'])
- ->paginate();
-
- return ApplicationResource::collection($applications);
- }
-
- public function store(StoreApplicationRequest $request): ApplicationResource
- {
- $application = $request->user()
- ->currentTeam
- ->applications()
- ->create($request->validated());
-
- return new ApplicationResource($application);
- }
-
- public function deploy(Application $application): JsonResponse
- {
- $this->authorize('deploy', $application);
-
- $deployment = app(ApplicationDeploymentService::class)
- ->deploy($application);
-
- return response()->json([
- 'message' => 'Deployment started successfully',
- 'deployment_id' => $deployment->id,
- ]);
- }
-}
-```
-
-### API Resource Development
-```php
-class ApplicationResource extends JsonResource
-{
- public function toArray($request): array
- {
- return [
- 'id' => $this->id,
- 'name' => $this->name,
- 'fqdn' => $this->fqdn,
- 'status' => $this->status,
- 'git_repository' => $this->git_repository,
- 'git_branch' => $this->git_branch,
- 'build_pack' => $this->build_pack,
- 'created_at' => $this->created_at,
- 'updated_at' => $this->updated_at,
-
- // Conditional relationships
- 'server' => new ServerResource($this->whenLoaded('server')),
- 'environment' => new EnvironmentResource($this->whenLoaded('environment')),
- 'latest_deployment' => new DeploymentResource($this->whenLoaded('latestDeployment')),
-
- // Computed attributes
- 'deployment_url' => $this->getDeploymentUrl(),
- 'can_deploy' => $this->canDeploy(),
- ];
- }
-}
-```
-
-## Livewire Component Development
-
-### Component Structure
-```php
-class ApplicationShow extends Component
-{
- public Application $application;
- public bool $showLogs = false;
-
- protected $listeners = [
- 'deployment.started' => 'refreshDeploymentStatus',
- 'deployment.completed' => 'refreshDeploymentStatus',
- ];
-
- public function mount(Application $application): void
- {
- $this->authorize('view', $application);
- $this->application = $application;
- }
-
- public function deploy(): void
- {
- $this->authorize('deploy', $this->application);
-
- try {
- app(ApplicationDeploymentService::class)->deploy($this->application);
-
- $this->dispatch('deployment.started', [
- 'application_id' => $this->application->id
- ]);
-
- session()->flash('success', 'Deployment started successfully');
- } catch (Exception $e) {
- session()->flash('error', 'Failed to start deployment: ' . $e->getMessage());
- }
- }
-
- public function refreshDeploymentStatus(): void
- {
- $this->application->refresh();
- }
-
- public function render(): View
- {
- return view('livewire.application.show', [
- 'deployments' => $this->application
- ->deployments()
- ->latest()
- ->limit(10)
- ->get()
- ]);
- }
-}
-```
-
-## Queue Job Development
-
-### Job Structure
-```php
-class DeployApplicationJob implements ShouldQueue
-{
- use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
-
- public int $tries = 3;
- public int $maxExceptions = 1;
-
- public function __construct(
- public ApplicationDeploymentQueue $deployment
- ) {}
-
- public function handle(
- DockerService $dockerService,
- ConfigurationGenerator $configGenerator
- ): void {
- $this->deployment->update(['status' => 'running', 'started_at' => now()]);
-
- try {
- // Generate configuration
- $config = $configGenerator->generateDockerCompose($this->deployment->application);
-
- // Build and deploy
- $imageTag = $dockerService->buildImage($this->deployment->application);
- $dockerService->deployContainer($this->deployment->application, $imageTag);
-
- $this->deployment->update([
- 'status' => 'success',
- 'finished_at' => now()
- ]);
-
- // Broadcast success
- broadcast(new DeploymentCompleted($this->deployment));
-
- } catch (Exception $e) {
- $this->deployment->update([
- 'status' => 'failed',
- 'error_message' => $e->getMessage(),
- 'finished_at' => now()
- ]);
-
- broadcast(new DeploymentFailed($this->deployment));
-
- throw $e;
- }
- }
-
- public function backoff(): array
- {
- return [1, 5, 10];
- }
-
- public function failed(Throwable $exception): void
- {
- $this->deployment->update([
- 'status' => 'failed',
- 'error_message' => $exception->getMessage(),
- 'finished_at' => now()
- ]);
- }
-}
-```
-
-## Testing Development
-
-### Test Structure
-```php
-// Feature test example
-test('user can deploy application via API', function () {
- $user = User::factory()->create();
- $application = Application::factory()->create([
- 'team_id' => $user->currentTeam->id
- ]);
-
- // Mock external services
- $this->mock(DockerService::class, function ($mock) {
- $mock->shouldReceive('buildImage')->andReturn('app:latest');
- $mock->shouldReceive('deployContainer')->andReturn(true);
- });
-
- $response = $this->actingAs($user)
- ->postJson("/api/v1/applications/{$application->id}/deploy");
-
- $response->assertStatus(200)
- ->assertJson([
- 'message' => 'Deployment started successfully'
- ]);
-
- expect($application->deployments()->count())->toBe(1);
- expect($application->deployments()->first()->status)->toBe('queued');
-});
-```
-
-## Documentation Standards
-
-### Code Documentation
-```php
-/**
- * Deploy an application to the specified server.
- *
- * This method creates a new deployment queue entry and dispatches
- * a background job to handle the actual deployment process.
- *
- * @param Application $application The application to deploy
- * @param array $options Additional deployment options
- * @return ApplicationDeploymentQueue The created deployment queue entry
- *
- * @throws DeploymentException When deployment cannot be started
- * @throws ServerConnectionException When server is unreachable
- */
-public function deploy(Application $application, array $options = []): ApplicationDeploymentQueue
-{
- // Implementation
-}
-```
-
-### API Documentation
-```php
-/**
- * @OA\Post(
- * path="/api/v1/applications/{application}/deploy",
- * summary="Deploy an application",
- * description="Triggers a new deployment for the specified application",
- * operationId="deployApplication",
- * tags={"Applications"},
- * security={{"bearerAuth":{}}},
- * @OA\Parameter(
- * name="application",
- * in="path",
- * required=true,
- * @OA\Schema(type="integer"),
- * description="Application ID"
- * ),
- * @OA\Response(
- * response=200,
- * description="Deployment started successfully",
- * @OA\JsonContent(
- * @OA\Property(property="message", type="string"),
- * @OA\Property(property="deployment_id", type="integer")
- * )
- * )
- * )
- */
-```
-
-## Performance Optimization
-
-### Database Optimization
-```php
-// Use eager loading to prevent N+1 queries
-$applications = Application::with([
- 'server:id,name,ip',
- 'environment:id,name',
- 'latestDeployment:id,application_id,status,created_at'
-])->get();
-
-// Use database transactions for consistency
-DB::transaction(function () use ($application) {
- $deployment = $application->deployments()->create(['status' => 'queued']);
- $application->update(['last_deployment_at' => now()]);
- DeployApplicationJob::dispatch($deployment);
-});
-```
-
-### Caching Strategies
-```php
-// Cache expensive operations
-public function getServerMetrics(Server $server): array
-{
- return Cache::remember(
- "server.{$server->id}.metrics",
- now()->addMinutes(5),
- fn () => $this->fetchServerMetrics($server)
- );
-}
-```
-
-## Deployment & Release Process
-
-### Version Management
-- **[versions.json](mdc:versions.json)** - Version tracking (355B, 19 lines)
-- **[CHANGELOG.md](mdc:CHANGELOG.md)** - Release notes (187KB, 7411 lines)
-- **[cliff.toml](mdc:cliff.toml)** - Changelog generation (3.2KB, 85 lines)
-
-### Release Workflow
-```bash
-# Create release branch
-git checkout -b release/v4.1.0
-
-# Update version numbers
-# Update CHANGELOG.md
-# Run full test suite
-./vendor/bin/pest
-npm run test
-
-# Create release commit
-git commit -m "chore: release v4.1.0"
-
-# Create and push tag
-git tag v4.1.0
-git push origin v4.1.0
-
-# Merge to main
-git checkout main
-git merge release/v4.1.0
-```
-
-## Contributing Guidelines
-
-### Pull Request Process
-1. **Fork** the repository
-2. **Create** feature branch from `main`
-3. **Implement** changes with tests
-4. **Run** code quality checks
-5. **Submit** pull request with clear description
-6. **Address** review feedback
-7. **Merge** after approval
-
-### Code Review Checklist
-- [ ] Code follows project standards
-- [ ] Tests cover new functionality
-- [ ] Documentation is updated
-- [ ] No breaking changes without migration
-- [ ] Performance impact considered
-- [ ] Security implications reviewed
-
-### Issue Reporting
-- Use issue templates
-- Provide reproduction steps
-- Include environment details
-- Add relevant logs/screenshots
-- Label appropriately
diff --git a/.ai/development/laravel-boost.md b/.ai/development/laravel-boost.md
deleted file mode 100644
index 7f5922d94..000000000
--- a/.ai/development/laravel-boost.md
+++ /dev/null
@@ -1,402 +0,0 @@
-
-=== foundation rules ===
-
-# Laravel Boost Guidelines
-
-The Laravel Boost guidelines are specifically curated by Laravel maintainers for this application. These guidelines should be followed closely to enhance the user's satisfaction building Laravel applications.
-
-## Foundational Context
-This application is a Laravel application and its main Laravel ecosystems package & versions are below. You are an expert with them all. Ensure you abide by these specific packages & versions.
-
-- php - 8.4.7
-- laravel/fortify (FORTIFY) - v1
-- laravel/framework (LARAVEL) - v12
-- laravel/horizon (HORIZON) - v5
-- laravel/prompts (PROMPTS) - v0
-- laravel/sanctum (SANCTUM) - v4
-- laravel/socialite (SOCIALITE) - v5
-- livewire/livewire (LIVEWIRE) - v3
-- laravel/dusk (DUSK) - v8
-- laravel/pint (PINT) - v1
-- laravel/telescope (TELESCOPE) - v5
-- pestphp/pest (PEST) - v3
-- phpunit/phpunit (PHPUNIT) - v11
-- rector/rector (RECTOR) - v2
-- laravel-echo (ECHO) - v2
-- tailwindcss (TAILWINDCSS) - v4
-- vue (VUE) - v3
-
-
-## Conventions
-- You must follow all existing code conventions used in this application. When creating or editing a file, check sibling files for the correct structure, approach, naming.
-- Use descriptive names for variables and methods. For example, `isRegisteredForDiscounts`, not `discount()`.
-- Check for existing components to reuse before writing a new one.
-
-## Verification Scripts
-- Do not create verification scripts or tinker when tests cover that functionality and prove it works. Unit and feature tests are more important.
-
-## Application Structure & Architecture
-- Stick to existing directory structure - don't create new base folders without approval.
-- Do not change the application's dependencies without approval.
-
-## Frontend Bundling
-- If the user doesn't see a frontend change reflected in the UI, it could mean they need to run `npm run build`, `npm run dev`, or `composer run dev`. Ask them.
-
-## Replies
-- Be concise in your explanations - focus on what's important rather than explaining obvious details.
-
-## Documentation Files
-- You must only create documentation files if explicitly requested by the user.
-
-
-=== boost rules ===
-
-## Laravel Boost
-- Laravel Boost is an MCP server that comes with powerful tools designed specifically for this application. Use them.
-
-## Artisan
-- Use the `list-artisan-commands` tool when you need to call an Artisan command to double check the available parameters.
-
-## URLs
-- Whenever you share a project URL with the user you should use the `get-absolute-url` tool to ensure you're using the correct scheme, domain / IP, and port.
-
-## Tinker / Debugging
-- You should use the `tinker` tool when you need to execute PHP to debug code or query Eloquent models directly.
-- Use the `database-query` tool when you only need to read from the database.
-
-## Reading Browser Logs With the `browser-logs` Tool
-- You can read browser logs, errors, and exceptions using the `browser-logs` tool from Boost.
-- Only recent browser logs will be useful - ignore old logs.
-
-## Searching Documentation (Critically Important)
-- Boost comes with a powerful `search-docs` tool you should use before any other approaches. This tool automatically passes a list of installed packages and their versions to the remote Boost API, so it returns only version-specific documentation specific for the user's circumstance. You should pass an array of packages to filter on if you know you need docs for particular packages.
-- The 'search-docs' tool is perfect for all Laravel related packages, including Laravel, Inertia, Livewire, Filament, Tailwind, Pest, Nova, Nightwatch, etc.
-- You must use this tool to search for Laravel-ecosystem documentation before falling back to other approaches.
-- Search the documentation before making code changes to ensure we are taking the correct approach.
-- Use multiple, broad, simple, topic based queries to start. For example: `['rate limiting', 'routing rate limiting', 'routing']`.
-- Do not add package names to queries - package information is already shared. For example, use `test resource table`, not `filament 4 test resource table`.
-
-### Available Search Syntax
-- You can and should pass multiple queries at once. The most relevant results will be returned first.
-
-1. Simple Word Searches with auto-stemming - query=authentication - finds 'authenticate' and 'auth'
-2. Multiple Words (AND Logic) - query=rate limit - finds knowledge containing both "rate" AND "limit"
-3. Quoted Phrases (Exact Position) - query="infinite scroll" - Words must be adjacent and in that order
-4. Mixed Queries - query=middleware "rate limit" - "middleware" AND exact phrase "rate limit"
-5. Multiple Queries - queries=["authentication", "middleware"] - ANY of these terms
-
-
-=== php rules ===
-
-## PHP
-
-- Always use curly braces for control structures, even if it has one line.
-
-### Constructors
-- Use PHP 8 constructor property promotion in `__construct()`.
- - public function __construct(public GitHub $github) { }
-- Do not allow empty `__construct()` methods with zero parameters.
-
-### Type Declarations
-- Always use explicit return type declarations for methods and functions.
-- Use appropriate PHP type hints for method parameters.
-
-
-protected function isAccessible(User $user, ?string $path = null): bool
-{
- ...
-}
-
-
-## Comments
-- Prefer PHPDoc blocks over comments. Never use comments within the code itself unless there is something _very_ complex going on.
-
-## PHPDoc Blocks
-- Add useful array shape type definitions for arrays when appropriate.
-
-## Enums
-- Typically, keys in an Enum should be TitleCase. For example: `FavoritePerson`, `BestLake`, `Monthly`.
-
-
-=== laravel/core rules ===
-
-## Do Things the Laravel Way
-
-- Use `php artisan make:` commands to create new files (i.e. migrations, controllers, models, etc.). You can list available Artisan commands using the `list-artisan-commands` tool.
-- If you're creating a generic PHP class, use `artisan make:class`.
-- Pass `--no-interaction` to all Artisan commands to ensure they work without user input. You should also pass the correct `--options` to ensure correct behavior.
-
-### Database
-- Always use proper Eloquent relationship methods with return type hints. Prefer relationship methods over raw queries or manual joins.
-- Use Eloquent models and relationships before suggesting raw database queries
-- Avoid `DB::`; prefer `Model::query()`. Generate code that leverages Laravel's ORM capabilities rather than bypassing them.
-- Generate code that prevents N+1 query problems by using eager loading.
-- Use Laravel's query builder for very complex database operations.
-
-### Model Creation
-- When creating new models, create useful factories and seeders for them too. Ask the user if they need any other things, using `list-artisan-commands` to check the available options to `php artisan make:model`.
-
-### APIs & Eloquent Resources
-- For APIs, default to using Eloquent API Resources and API versioning unless existing API routes do not, then you should follow existing application convention.
-
-### Controllers & Validation
-- Always create Form Request classes for validation rather than inline validation in controllers. Include both validation rules and custom error messages.
-- Check sibling Form Requests to see if the application uses array or string based validation rules.
-
-### Queues
-- Use queued jobs for time-consuming operations with the `ShouldQueue` interface.
-
-### Authentication & Authorization
-- Use Laravel's built-in authentication and authorization features (gates, policies, Sanctum, etc.).
-
-### URL Generation
-- When generating links to other pages, prefer named routes and the `route()` function.
-
-### Configuration
-- Use environment variables only in configuration files - never use the `env()` function directly outside of config files. Always use `config('app.name')`, not `env('APP_NAME')`.
-
-### Testing
-- When creating models for tests, use the factories for the models. Check if the factory has custom states that can be used before manually setting up the model.
-- Faker: Use methods such as `$this->faker->word()` or `fake()->randomDigit()`. Follow existing conventions whether to use `$this->faker` or `fake()`.
-- When creating tests, make use of `php artisan make:test [options] ` to create a feature test, and pass `--unit` to create a unit test. Most tests should be feature tests.
-
-### Vite Error
-- If you receive an "Illuminate\Foundation\ViteException: Unable to locate file in Vite manifest" error, you can run `npm run build` or ask the user to run `npm run dev` or `composer run dev`.
-
-
-=== laravel/v12 rules ===
-
-## Laravel 12
-
-- Use the `search-docs` tool to get version specific documentation.
-- This project upgraded from Laravel 10 without migrating to the new streamlined Laravel file structure.
-- This is **perfectly fine** and recommended by Laravel. Follow the existing structure from Laravel 10. We do not to need migrate to the new Laravel structure unless the user explicitly requests that.
-
-### Laravel 10 Structure
-- Middleware typically lives in `app/Http/Middleware/` and service providers in `app/Providers/`.
-- There is no `bootstrap/app.php` application configuration in a Laravel 10 structure:
- - Middleware registration happens in `app/Http/Kernel.php`
- - Exception handling is in `app/Exceptions/Handler.php`
- - Console commands and schedule register in `app/Console/Kernel.php`
- - Rate limits likely exist in `RouteServiceProvider` or `app/Http/Kernel.php`
-
-### Database
-- When modifying a column, the migration must include all of the attributes that were previously defined on the column. Otherwise, they will be dropped and lost.
-- Laravel 12 allows limiting eagerly loaded records natively, without external packages: `$query->latest()->limit(10);`.
-
-### Models
-- Casts can and likely should be set in a `casts()` method on a model rather than the `$casts` property. Follow existing conventions from other models.
-
-
-=== livewire/core rules ===
-
-## Livewire Core
-- Use the `search-docs` tool to find exact version specific documentation for how to write Livewire & Livewire tests.
-- Use the `php artisan make:livewire [Posts\\CreatePost]` artisan command to create new components
-- State should live on the server, with the UI reflecting it.
-- All Livewire requests hit the Laravel backend, they're like regular HTTP requests. Always validate form data, and run authorization checks in Livewire actions.
-
-## Livewire Best Practices
-- Livewire components require a single root element.
-- Use `wire:loading` and `wire:dirty` for delightful loading states.
-- Add `wire:key` in loops:
-
- ```blade
- @foreach ($items as $item)
-
- {{ $item->name }}
-
- @endforeach
- ```
-
-- Prefer lifecycle hooks like `mount()`, `updatedFoo()`) for initialization and reactive side effects:
-
-
- public function mount(User $user) { $this->user = $user; }
- public function updatedSearch() { $this->resetPage(); }
-
-
-
-## Testing Livewire
-
-
- Livewire::test(Counter::class)
- ->assertSet('count', 0)
- ->call('increment')
- ->assertSet('count', 1)
- ->assertSee(1)
- ->assertStatus(200);
-
-
-
-
- $this->get('/posts/create')
- ->assertSeeLivewire(CreatePost::class);
-
-
-
-=== livewire/v3 rules ===
-
-## Livewire 3
-
-### Key Changes From Livewire 2
-- These things changed in Livewire 2, but may not have been updated in this application. Verify this application's setup to ensure you conform with application conventions.
- - Use `wire:model.live` for real-time updates, `wire:model` is now deferred by default.
- - Components now use the `App\Livewire` namespace (not `App\Http\Livewire`).
- - Use `$this->dispatch()` to dispatch events (not `emit` or `dispatchBrowserEvent`).
- - Use the `components.layouts.app` view as the typical layout path (not `layouts.app`).
-
-### New Directives
-- `wire:show`, `wire:transition`, `wire:cloak`, `wire:offline`, `wire:target` are available for use. Use the documentation to find usage examples.
-
-### Alpine
-- Alpine is now included with Livewire, don't manually include Alpine.js.
-- Plugins included with Alpine: persist, intersect, collapse, and focus.
-
-### Lifecycle Hooks
-- You can listen for `livewire:init` to hook into Livewire initialization, and `fail.status === 419` for the page expiring:
-
-
-document.addEventListener('livewire:init', function () {
- Livewire.hook('request', ({ fail }) => {
- if (fail && fail.status === 419) {
- alert('Your session expired');
- }
- });
-
- Livewire.hook('message.failed', (message, component) => {
- console.error(message);
- });
-});
-
-
-
-=== pint/core rules ===
-
-## Laravel Pint Code Formatter
-
-- You must run `vendor/bin/pint --dirty` before finalizing changes to ensure your code matches the project's expected style.
-- Do not run `vendor/bin/pint --test`, simply run `vendor/bin/pint` to fix any formatting issues.
-
-
-=== pest/core rules ===
-
-## Pest
-
-### Testing
-- If you need to verify a feature is working, write or update a Unit / Feature test.
-
-### Pest Tests
-- All tests must be written using Pest. Use `php artisan make:test --pest `.
-- You must not remove any tests or test files from the tests directory without approval. These are not temporary or helper files - these are core to the application.
-- Tests should test all of the happy paths, failure paths, and weird paths.
-- Tests live in the `tests/Feature` and `tests/Unit` directories.
-- Pest tests look and behave like this:
-
-it('is true', function () {
- expect(true)->toBeTrue();
-});
-
-
-### Running Tests
-- Run the minimal number of tests using an appropriate filter before finalizing code edits.
-- To run all tests: `php artisan test`.
-- To run all tests in a file: `php artisan test tests/Feature/ExampleTest.php`.
-- To filter on a particular test name: `php artisan test --filter=testName` (recommended after making a change to a related file).
-- When the tests relating to your changes are passing, ask the user if they would like to run the entire test suite to ensure everything is still passing.
-
-### Pest Assertions
-- When asserting status codes on a response, use the specific method like `assertForbidden` and `assertNotFound` instead of using `assertStatus(403)` or similar, e.g.:
-
-it('returns all', function () {
- $response = $this->postJson('/api/docs', []);
-
- $response->assertSuccessful();
-});
-
-
-### Mocking
-- Mocking can be very helpful when appropriate.
-- When mocking, you can use the `Pest\Laravel\mock` Pest function, but always import it via `use function Pest\Laravel\mock;` before using it. Alternatively, you can use `$this->mock()` if existing tests do.
-- You can also create partial mocks using the same import or self method.
-
-### Datasets
-- Use datasets in Pest to simplify tests which have a lot of duplicated data. This is often the case when testing validation rules, so consider going with this solution when writing tests for validation rules.
-
-
-it('has emails', function (string $email) {
- expect($email)->not->toBeEmpty();
-})->with([
- 'james' => 'james@laravel.com',
- 'taylor' => 'taylor@laravel.com',
-]);
-
-
-
-=== tailwindcss/core rules ===
-
-## Tailwind Core
-
-- Use Tailwind CSS classes to style HTML, check and use existing tailwind conventions within the project before writing your own.
-- Offer to extract repeated patterns into components that match the project's conventions (i.e. Blade, JSX, Vue, etc..)
-- Think through class placement, order, priority, and defaults - remove redundant classes, add classes to parent or child carefully to limit repetition, group elements logically
-- You can use the `search-docs` tool to get exact examples from the official documentation when needed.
-
-### Spacing
-- When listing items, use gap utilities for spacing, don't use margins.
-
-
-
-
Superior
-
Michigan
-
Erie
-
-
-
-
-### Dark Mode
-- If existing pages and components support dark mode, new pages and components must support dark mode in a similar way, typically using `dark:`.
-
-
-=== tailwindcss/v4 rules ===
-
-## Tailwind 4
-
-- Always use Tailwind CSS v4 - do not use the deprecated utilities.
-- `corePlugins` is not supported in Tailwind v4.
-- In Tailwind v4, you import Tailwind using a regular CSS `@import` statement, not using the `@tailwind` directives used in v3:
-
-
-
-
-### Replaced Utilities
-- Tailwind v4 removed deprecated utilities. Do not use the deprecated option - use the replacement.
-- Opacity values are still numeric.
-
-| Deprecated | Replacement |
-|------------+--------------|
-| bg-opacity-* | bg-black/* |
-| text-opacity-* | text-black/* |
-| border-opacity-* | border-black/* |
-| divide-opacity-* | divide-black/* |
-| ring-opacity-* | ring-black/* |
-| placeholder-opacity-* | placeholder-black/* |
-| flex-shrink-* | shrink-* |
-| flex-grow-* | grow-* |
-| overflow-ellipsis | text-ellipsis |
-| decoration-slice | box-decoration-slice |
-| decoration-clone | box-decoration-clone |
-
-
-=== tests rules ===
-
-## Test Enforcement
-
-- Every change must be programmatically tested. Write a new test or update an existing test, then run the affected tests to make sure they pass.
-- Run the minimum number of tests needed to ensure code quality and speed. Use `php artisan test` with a specific filename or filter.
-
\ No newline at end of file
diff --git a/.ai/development/testing-patterns.md b/.ai/development/testing-patterns.md
deleted file mode 100644
index 875de8b3b..000000000
--- a/.ai/development/testing-patterns.md
+++ /dev/null
@@ -1,648 +0,0 @@
-# Coolify Testing Architecture & Patterns
-
-> **Cross-Reference**: These detailed testing patterns align with the testing guidelines in **[CLAUDE.md](mdc:CLAUDE.md)**. Both documents share the same core principles about Docker execution and mocking preferences.
-
-## Testing Philosophy
-
-Coolify employs **comprehensive testing strategies** using modern PHP testing frameworks to ensure reliability of deployment operations, infrastructure management, and user interactions.
-
-### Test Execution Rules
-
-**CRITICAL**: Tests are categorized by database dependency:
-
-#### Unit Tests (`tests/Unit/`)
-- **MUST NOT** use database connections
-- **MUST** use mocking for models and external dependencies
-- **CAN** run outside Docker: `./vendor/bin/pest tests/Unit`
-- Purpose: Test isolated logic, helper functions, and business rules
-
-#### Feature Tests (`tests/Feature/`)
-- **MAY** use database connections (factories, migrations, models)
-- **MUST** run inside Docker container: `docker exec coolify php artisan test`
-- **MUST** use `RefreshDatabase` trait if touching database
-- Purpose: Test API endpoints, workflows, and integration scenarios
-
-**Rule of thumb**: If your test needs `Server::factory()->create()` or any database operation, it's a Feature test and MUST run in Docker.
-
-### Prefer Mocking Over Database
-
-When writing tests, always prefer mocking over real database operations:
-
-```php
-// ❌ BAD: Unit test using database
-it('extracts custom commands', function () {
- $server = Server::factory()->create(['ip' => '1.2.3.4']);
- $commands = extract_custom_proxy_commands($server, $yaml);
- expect($commands)->toBeArray();
-});
-
-// ✅ GOOD: Unit test using mocking
-it('extracts custom commands', function () {
- $server = Mockery::mock('App\Models\Server');
- $server->shouldReceive('proxyType')->andReturn('traefik');
- $commands = extract_custom_proxy_commands($server, $yaml);
- expect($commands)->toBeArray();
-});
-```
-
-**Design principles for testable code:**
-- Use dependency injection instead of global state
-- Create interfaces for external dependencies (SSH, Docker, etc.)
-- Separate business logic from data persistence
-- Make functions accept interfaces instead of concrete models when possible
-
-## Testing Framework Stack
-
-### Core Testing Tools
-- **Pest PHP 3.8+** - Primary testing framework with expressive syntax
-- **Laravel Dusk** - Browser automation and end-to-end testing
-- **PHPUnit** - Underlying unit testing framework
-- **Mockery** - Mocking and stubbing for isolated tests
-
-### Testing Configuration
-- **[tests/Pest.php](mdc:tests/Pest.php)** - Pest configuration and global setup (1.5KB, 45 lines)
-- **[tests/TestCase.php](mdc:tests/TestCase.php)** - Base test case class (163B, 11 lines)
-- **[tests/CreatesApplication.php](mdc:tests/CreatesApplication.php)** - Application factory trait (375B, 22 lines)
-- **[tests/DuskTestCase.php](mdc:tests/DuskTestCase.php)** - Browser testing setup (1.4KB, 58 lines)
-
-## Test Directory Structure
-
-### Test Organization
-- **[tests/Feature/](mdc:tests/Feature)** - Feature and integration tests
-- **[tests/Unit/](mdc:tests/Unit)** - Unit tests for isolated components
-- **[tests/Browser/](mdc:tests/Browser)** - Laravel Dusk browser tests
-- **[tests/Traits/](mdc:tests/Traits)** - Shared testing utilities
-
-## Unit Testing Patterns
-
-### Model Testing
-```php
-// Testing Eloquent models
-test('application model has correct relationships', function () {
- $application = Application::factory()->create();
-
- expect($application->server)->toBeInstanceOf(Server::class);
- expect($application->environment)->toBeInstanceOf(Environment::class);
- expect($application->deployments)->toBeInstanceOf(Collection::class);
-});
-
-test('application can generate deployment configuration', function () {
- $application = Application::factory()->create([
- 'name' => 'test-app',
- 'git_repository' => 'https://github.com/user/repo.git'
- ]);
-
- $config = $application->generateDockerCompose();
-
- expect($config)->toContain('test-app');
- expect($config)->toContain('image:');
- expect($config)->toContain('networks:');
-});
-```
-
-### Service Layer Testing
-```php
-// Testing service classes
-test('configuration generator creates valid docker compose', function () {
- $generator = new ConfigurationGenerator();
- $application = Application::factory()->create();
-
- $compose = $generator->generateDockerCompose($application);
-
- expect($compose)->toBeString();
- expect(yaml_parse($compose))->toBeArray();
- expect($compose)->toContain('version: "3.8"');
-});
-
-test('docker image parser validates image names', function () {
- $parser = new DockerImageParser();
-
- expect($parser->isValid('nginx:latest'))->toBeTrue();
- expect($parser->isValid('invalid-image-name'))->toBeFalse();
- expect($parser->parse('nginx:1.21'))->toEqual([
- 'registry' => 'docker.io',
- 'namespace' => 'library',
- 'repository' => 'nginx',
- 'tag' => '1.21'
- ]);
-});
-```
-
-### Action Testing
-```php
-// Testing Laravel Actions
-test('deploy application action creates deployment queue', function () {
- $application = Application::factory()->create();
- $action = new DeployApplicationAction();
-
- $deployment = $action->handle($application);
-
- expect($deployment)->toBeInstanceOf(ApplicationDeploymentQueue::class);
- expect($deployment->status)->toBe('queued');
- expect($deployment->application_id)->toBe($application->id);
-});
-
-test('server validation action checks ssh connectivity', function () {
- $server = Server::factory()->create([
- 'ip' => '192.168.1.100',
- 'port' => 22
- ]);
-
- $action = new ValidateServerAction();
-
- // Mock SSH connection
- $this->mock(SshConnection::class, function ($mock) {
- $mock->shouldReceive('connect')->andReturn(true);
- $mock->shouldReceive('execute')->with('docker --version')->andReturn('Docker version 20.10.0');
- });
-
- $result = $action->handle($server);
-
- expect($result['ssh_connection'])->toBeTrue();
- expect($result['docker_installed'])->toBeTrue();
-});
-```
-
-## Feature Testing Patterns
-
-### API Testing
-```php
-// Testing API endpoints
-test('authenticated user can list applications', function () {
- $user = User::factory()->create();
- $team = Team::factory()->create();
- $user->teams()->attach($team);
-
- $applications = Application::factory(3)->create([
- 'team_id' => $team->id
- ]);
-
- $response = $this->actingAs($user)
- ->getJson('/api/v1/applications');
-
- $response->assertStatus(200)
- ->assertJsonCount(3, 'data')
- ->assertJsonStructure([
- 'data' => [
- '*' => ['id', 'name', 'fqdn', 'status', 'created_at']
- ]
- ]);
-});
-
-test('user cannot access applications from other teams', function () {
- $user = User::factory()->create();
- $otherTeam = Team::factory()->create();
-
- $application = Application::factory()->create([
- 'team_id' => $otherTeam->id
- ]);
-
- $response = $this->actingAs($user)
- ->getJson("/api/v1/applications/{$application->id}");
-
- $response->assertStatus(403);
-});
-```
-
-### Deployment Testing
-```php
-// Testing deployment workflows
-test('application deployment creates docker containers', function () {
- $application = Application::factory()->create([
- 'git_repository' => 'https://github.com/laravel/laravel.git',
- 'git_branch' => 'main'
- ]);
-
- // Mock Docker operations
- $this->mock(DockerService::class, function ($mock) {
- $mock->shouldReceive('buildImage')->andReturn('app:latest');
- $mock->shouldReceive('createContainer')->andReturn('container_id');
- $mock->shouldReceive('startContainer')->andReturn(true);
- });
-
- $deployment = $application->deploy();
-
- expect($deployment->status)->toBe('queued');
-
- // Process the deployment job
- $this->artisan('queue:work --once');
-
- $deployment->refresh();
- expect($deployment->status)->toBe('success');
-});
-
-test('failed deployment triggers rollback', function () {
- $application = Application::factory()->create();
-
- // Mock failed deployment
- $this->mock(DockerService::class, function ($mock) {
- $mock->shouldReceive('buildImage')->andThrow(new DeploymentException('Build failed'));
- });
-
- $deployment = $application->deploy();
-
- $this->artisan('queue:work --once');
-
- $deployment->refresh();
- expect($deployment->status)->toBe('failed');
- expect($deployment->error_message)->toContain('Build failed');
-});
-```
-
-### Webhook Testing
-```php
-// Testing webhook endpoints
-test('github webhook triggers deployment', function () {
- $application = Application::factory()->create([
- 'git_repository' => 'https://github.com/user/repo.git',
- 'git_branch' => 'main'
- ]);
-
- $payload = [
- 'ref' => 'refs/heads/main',
- 'repository' => [
- 'clone_url' => 'https://github.com/user/repo.git'
- ],
- 'head_commit' => [
- 'id' => 'abc123',
- 'message' => 'Update application'
- ]
- ];
-
- $response = $this->postJson("/webhooks/github/{$application->id}", $payload);
-
- $response->assertStatus(200);
-
- expect($application->deployments()->count())->toBe(1);
- expect($application->deployments()->first()->commit_sha)->toBe('abc123');
-});
-
-test('webhook validates payload signature', function () {
- $application = Application::factory()->create();
-
- $payload = ['invalid' => 'payload'];
-
- $response = $this->postJson("/webhooks/github/{$application->id}", $payload);
-
- $response->assertStatus(400);
-});
-```
-
-## Browser Testing (Laravel Dusk)
-
-### End-to-End Testing
-```php
-// Testing complete user workflows
-test('user can create and deploy application', function () {
- $user = User::factory()->create();
- $server = Server::factory()->create(['team_id' => $user->currentTeam->id]);
-
- $this->browse(function (Browser $browser) use ($user, $server) {
- $browser->loginAs($user)
- ->visit('/applications/create')
- ->type('name', 'Test Application')
- ->type('git_repository', 'https://github.com/laravel/laravel.git')
- ->type('git_branch', 'main')
- ->select('server_id', $server->id)
- ->press('Create Application')
- ->assertPathIs('/applications/*')
- ->assertSee('Test Application')
- ->press('Deploy')
- ->waitForText('Deployment started', 10)
- ->assertSee('Deployment started');
- });
-});
-
-test('user can monitor deployment logs in real-time', function () {
- $user = User::factory()->create();
- $application = Application::factory()->create(['team_id' => $user->currentTeam->id]);
-
- $this->browse(function (Browser $browser) use ($user, $application) {
- $browser->loginAs($user)
- ->visit("/applications/{$application->id}")
- ->press('Deploy')
- ->waitForText('Deployment started')
- ->click('@logs-tab')
- ->waitFor('@deployment-logs')
- ->assertSee('Building Docker image')
- ->waitForText('Deployment completed', 30);
- });
-});
-```
-
-### UI Component Testing
-```php
-// Testing Livewire components
-test('server status component updates in real-time', function () {
- $user = User::factory()->create();
- $server = Server::factory()->create(['team_id' => $user->currentTeam->id]);
-
- $this->browse(function (Browser $browser) use ($user, $server) {
- $browser->loginAs($user)
- ->visit("/servers/{$server->id}")
- ->assertSee('Status: Online')
- ->waitFor('@server-metrics')
- ->assertSee('CPU Usage')
- ->assertSee('Memory Usage')
- ->assertSee('Disk Usage');
-
- // Simulate server going offline
- $server->update(['status' => 'offline']);
-
- $browser->waitForText('Status: Offline', 5)
- ->assertSee('Status: Offline');
- });
-});
-```
-
-## Database Testing Patterns
-
-### Migration Testing
-```php
-// Testing database migrations
-test('applications table has correct structure', function () {
- expect(Schema::hasTable('applications'))->toBeTrue();
- expect(Schema::hasColumns('applications', [
- 'id', 'name', 'fqdn', 'git_repository', 'git_branch',
- 'server_id', 'environment_id', 'created_at', 'updated_at'
- ]))->toBeTrue();
-});
-
-test('foreign key constraints are properly set', function () {
- $application = Application::factory()->create();
-
- expect($application->server)->toBeInstanceOf(Server::class);
- expect($application->environment)->toBeInstanceOf(Environment::class);
-
- // Test cascade deletion
- $application->server->delete();
- expect(Application::find($application->id))->toBeNull();
-});
-```
-
-### Factory Testing
-```php
-// Testing model factories
-test('application factory creates valid models', function () {
- $application = Application::factory()->create();
-
- expect($application->name)->toBeString();
- expect($application->git_repository)->toStartWith('https://');
- expect($application->server_id)->toBeInt();
- expect($application->environment_id)->toBeInt();
-});
-
-test('application factory can create with custom attributes', function () {
- $application = Application::factory()->create([
- 'name' => 'Custom App',
- 'git_branch' => 'develop'
- ]);
-
- expect($application->name)->toBe('Custom App');
- expect($application->git_branch)->toBe('develop');
-});
-```
-
-## Queue Testing
-
-### Job Testing
-```php
-// Testing background jobs
-test('deploy application job processes successfully', function () {
- $application = Application::factory()->create();
- $deployment = ApplicationDeploymentQueue::factory()->create([
- 'application_id' => $application->id,
- 'status' => 'queued'
- ]);
-
- $job = new DeployApplicationJob($deployment);
-
- // Mock external dependencies
- $this->mock(DockerService::class, function ($mock) {
- $mock->shouldReceive('buildImage')->andReturn('app:latest');
- $mock->shouldReceive('deployContainer')->andReturn(true);
- });
-
- $job->handle();
-
- $deployment->refresh();
- expect($deployment->status)->toBe('success');
-});
-
-test('failed job is retried with exponential backoff', function () {
- $application = Application::factory()->create();
- $deployment = ApplicationDeploymentQueue::factory()->create([
- 'application_id' => $application->id
- ]);
-
- $job = new DeployApplicationJob($deployment);
-
- // Mock failure
- $this->mock(DockerService::class, function ($mock) {
- $mock->shouldReceive('buildImage')->andThrow(new Exception('Network error'));
- });
-
- expect(fn() => $job->handle())->toThrow(Exception::class);
-
- // Job should be retried
- expect($job->tries)->toBe(3);
- expect($job->backoff())->toBe([1, 5, 10]);
-});
-```
-
-## Security Testing
-
-### Authentication Testing
-```php
-// Testing authentication and authorization
-test('unauthenticated users cannot access protected routes', function () {
- $response = $this->get('/dashboard');
- $response->assertRedirect('/login');
-});
-
-test('users can only access their team resources', function () {
- $user1 = User::factory()->create();
- $user2 = User::factory()->create();
-
- $team1 = Team::factory()->create();
- $team2 = Team::factory()->create();
-
- $user1->teams()->attach($team1);
- $user2->teams()->attach($team2);
-
- $application = Application::factory()->create(['team_id' => $team1->id]);
-
- $response = $this->actingAs($user2)
- ->get("/applications/{$application->id}");
-
- $response->assertStatus(403);
-});
-```
-
-### Input Validation Testing
-```php
-// Testing input validation and sanitization
-test('application creation validates required fields', function () {
- $user = User::factory()->create();
-
- $response = $this->actingAs($user)
- ->postJson('/api/v1/applications', []);
-
- $response->assertStatus(422)
- ->assertJsonValidationErrors(['name', 'git_repository', 'server_id']);
-});
-
-test('malicious input is properly sanitized', function () {
- $user = User::factory()->create();
-
- $response = $this->actingAs($user)
- ->postJson('/api/v1/applications', [
- 'name' => '',
- 'git_repository' => 'javascript:alert("xss")',
- 'server_id' => 'invalid'
- ]);
-
- $response->assertStatus(422);
-});
-```
-
-## Performance Testing
-
-### Load Testing
-```php
-// Testing application performance under load
-test('application list endpoint handles concurrent requests', function () {
- $user = User::factory()->create();
- $applications = Application::factory(100)->create(['team_id' => $user->currentTeam->id]);
-
- $startTime = microtime(true);
-
- $response = $this->actingAs($user)
- ->getJson('/api/v1/applications');
-
- $endTime = microtime(true);
- $responseTime = ($endTime - $startTime) * 1000; // Convert to milliseconds
-
- $response->assertStatus(200);
- expect($responseTime)->toBeLessThan(500); // Should respond within 500ms
-});
-```
-
-### Memory Usage Testing
-```php
-// Testing memory efficiency
-test('deployment process does not exceed memory limits', function () {
- $initialMemory = memory_get_usage();
-
- $application = Application::factory()->create();
- $deployment = $application->deploy();
-
- // Process deployment
- $this->artisan('queue:work --once');
-
- $finalMemory = memory_get_usage();
- $memoryIncrease = $finalMemory - $initialMemory;
-
- expect($memoryIncrease)->toBeLessThan(50 * 1024 * 1024); // Less than 50MB
-});
-```
-
-## Test Utilities and Helpers
-
-### Custom Assertions
-```php
-// Custom test assertions
-expect()->extend('toBeValidDockerCompose', function () {
- $yaml = yaml_parse($this->value);
-
- return $yaml !== false &&
- isset($yaml['version']) &&
- isset($yaml['services']) &&
- is_array($yaml['services']);
-});
-
-expect()->extend('toHaveValidSshConnection', function () {
- $server = $this->value;
-
- try {
- $connection = new SshConnection($server);
- return $connection->test();
- } catch (Exception $e) {
- return false;
- }
-});
-```
-
-### Test Traits
-```php
-// Shared testing functionality
-trait CreatesTestServers
-{
- protected function createTestServer(array $attributes = []): Server
- {
- return Server::factory()->create(array_merge([
- 'name' => 'Test Server',
- 'ip' => '127.0.0.1',
- 'port' => 22,
- 'team_id' => $this->user->currentTeam->id
- ], $attributes));
- }
-}
-
-trait MocksDockerOperations
-{
- protected function mockDockerService(): void
- {
- $this->mock(DockerService::class, function ($mock) {
- $mock->shouldReceive('buildImage')->andReturn('test:latest');
- $mock->shouldReceive('createContainer')->andReturn('container_123');
- $mock->shouldReceive('startContainer')->andReturn(true);
- $mock->shouldReceive('stopContainer')->andReturn(true);
- });
- }
-}
-```
-
-## Continuous Integration Testing
-
-### GitHub Actions Integration
-```yaml
-# .github/workflows/tests.yml
-name: Tests
-on: [push, pull_request]
-jobs:
- test:
- runs-on: ubuntu-latest
- services:
- postgres:
- image: postgres:15
- env:
- POSTGRES_PASSWORD: password
- options: >-
- --health-cmd pg_isready
- --health-interval 10s
- --health-timeout 5s
- --health-retries 5
- steps:
- - uses: actions/checkout@v3
- - name: Setup PHP
- uses: shivammathur/setup-php@v2
- with:
- php-version: 8.4
- - name: Install dependencies
- run: composer install
- - name: Run tests
- run: ./vendor/bin/pest
-```
-
-### Test Coverage
-```php
-// Generate test coverage reports
-test('application has adequate test coverage', function () {
- $coverage = $this->getCoverageData();
-
- expect($coverage['application'])->toBeGreaterThan(80);
- expect($coverage['models'])->toBeGreaterThan(90);
- expect($coverage['actions'])->toBeGreaterThan(85);
-});
-```
diff --git a/.ai/meta/maintaining-docs.md b/.ai/meta/maintaining-docs.md
deleted file mode 100644
index 1a1552399..000000000
--- a/.ai/meta/maintaining-docs.md
+++ /dev/null
@@ -1,172 +0,0 @@
-# Maintaining AI Documentation
-
-Guidelines for creating and maintaining AI documentation to ensure consistency and effectiveness across all AI tools (Claude Code, Cursor IDE, etc.).
-
-## Documentation Structure
-
-All AI documentation lives in the `.ai/` directory with the following structure:
-
-```
-.ai/
-├── README.md # Navigation hub
-├── core/ # Core project information
-├── development/ # Development practices
-├── patterns/ # Code patterns and best practices
-└── meta/ # Documentation maintenance guides
-```
-
-> **Note**: `CLAUDE.md` is in the repository root, not in the `.ai/` directory.
-
-## Required File Structure
-
-When creating new documentation files:
-
-```markdown
-# Title
-
-Brief description of what this document covers.
-
-## Section 1
-
-- **Main Points in Bold**
- - Sub-points with details
- - Examples and explanations
-
-## Section 2
-
-### Subsection
-
-Content with code examples:
-
-```language
-// ✅ DO: Show good examples
-const goodExample = true;
-
-// ❌ DON'T: Show anti-patterns
-const badExample = false;
-```
-```
-
-## File References
-
-- Use relative paths: `See [technology-stack.md](../core/technology-stack.md)`
-- For code references: `` `app/Models/Application.php` ``
-- Keep links working across different tools
-
-## Content Guidelines
-
-### DO:
-- Start with high-level overview
-- Include specific, actionable requirements
-- Show examples of correct implementation
-- Reference existing code when possible
-- Keep documentation DRY by cross-referencing
-- Use bullet points for clarity
-- Include both DO and DON'T examples
-
-### DON'T:
-- Create theoretical examples when real code exists
-- Duplicate content across multiple files
-- Use tool-specific formatting that won't work elsewhere
-- Make assumptions about versions - specify exact versions
-
-## Rule Improvement Triggers
-
-Update documentation when you notice:
-- New code patterns not covered by existing docs
-- Repeated similar implementations across files
-- Common error patterns that could be prevented
-- New libraries or tools being used consistently
-- Emerging best practices in the codebase
-
-## Analysis Process
-
-When updating documentation:
-1. Compare new code with existing rules
-2. Identify patterns that should be standardized
-3. Look for references to external documentation
-4. Check for consistent error handling patterns
-5. Monitor test patterns and coverage
-
-## Rule Updates
-
-### Add New Documentation When:
-- A new technology/pattern is used in 3+ files
-- Common bugs could be prevented by documentation
-- Code reviews repeatedly mention the same feedback
-- New security or performance patterns emerge
-
-### Modify Existing Documentation When:
-- Better examples exist in the codebase
-- Additional edge cases are discovered
-- Related documentation has been updated
-- Implementation details have changed
-
-## Quality Checks
-
-Before committing documentation changes:
-- [ ] Documentation is actionable and specific
-- [ ] Examples come from actual code
-- [ ] References are up to date
-- [ ] Patterns are consistently enforced
-- [ ] Cross-references work correctly
-- [ ] Version numbers are exact and current
-
-## Continuous Improvement
-
-- Monitor code review comments
-- Track common development questions
-- Update docs after major refactors
-- Add links to relevant documentation
-- Cross-reference related docs
-
-## Deprecation
-
-When patterns become outdated:
-1. Mark outdated patterns as deprecated
-2. Remove docs that no longer apply
-3. Update references to deprecated patterns
-4. Document migration paths for old patterns
-
-## Synchronization
-
-### Single Source of Truth
-- Each piece of information should exist in exactly ONE location
-- Other files should reference the source, not duplicate it
-- Example: Version numbers live in `core/technology-stack.md`, other files reference it
-
-### Cross-Tool Compatibility
-- **CLAUDE.md**: Main instructions for Claude Code users (references `.ai/` files)
-- **.cursor/rules/**: Single master file pointing to `.ai/` documentation
-- **Both tools**: Should get same information from `.ai/` directory
-
-### When to Update What
-
-**Version Changes** (Laravel, PHP, packages):
-1. Update `core/technology-stack.md` (single source)
-2. Verify CLAUDE.md references it correctly
-3. No other files should duplicate version numbers
-
-**Workflow Changes** (commands, setup):
-1. Update `development/workflow.md`
-2. Ensure CLAUDE.md quick reference is updated
-3. Verify all cross-references work
-
-**Pattern Changes** (how to write code):
-1. Update appropriate file in `patterns/`
-2. Add/update examples from real codebase
-3. Cross-reference from related docs
-
-## Documentation Files
-
-Keep documentation files only when explicitly needed. Don't create docs that merely describe obvious functionality - the code itself should be clear.
-
-## Breaking Changes
-
-When making breaking changes to documentation structure:
-1. Update this maintaining-docs.md file
-2. Update `.ai/README.md` navigation
-3. Update CLAUDE.md references
-4. Update `.cursor/rules/coolify-ai-docs.mdc`
-5. Test all cross-references still work
-6. Document the changes in sync-guide.md
diff --git a/.ai/meta/sync-guide.md b/.ai/meta/sync-guide.md
deleted file mode 100644
index ab9a45d1a..000000000
--- a/.ai/meta/sync-guide.md
+++ /dev/null
@@ -1,214 +0,0 @@
-# AI Instructions Synchronization Guide
-
-This document explains how AI instructions are organized and synchronized across different AI tools used with Coolify.
-
-## Overview
-
-Coolify maintains AI instructions with a **single source of truth** approach:
-
-1. **CLAUDE.md** - Main entry point for Claude Code (references `.ai/` directory)
-2. **.cursor/rules/coolify-ai-docs.mdc** - Master reference file for Cursor IDE (references `.ai/` directory)
-3. **.ai/** - Single source of truth containing all detailed documentation
-
-All AI tools (Claude Code, Cursor IDE, etc.) reference the same `.ai/` directory to ensure consistency.
-
-## Structure
-
-### CLAUDE.md (Root Directory)
-- **Purpose**: Entry point for Claude Code with quick-reference guide
-- **Format**: Single markdown file
-- **Includes**:
- - Quick-reference development commands
- - High-level architecture overview
- - Essential patterns and guidelines
- - References to detailed `.ai/` documentation
-
-### .cursor/rules/coolify-ai-docs.mdc
-- **Purpose**: Master reference file for Cursor IDE
-- **Format**: Single .mdc file with frontmatter
-- **Content**: Quick decision tree and references to `.ai/` directory
-- **Note**: Replaces all previous topic-specific .mdc files
-
-### .ai/ Directory (Single Source of Truth)
-- **Purpose**: All detailed, topic-specific documentation
-- **Format**: Organized markdown files by category
-- **Structure**:
- ```
- .ai/
- ├── README.md # Navigation hub
- ├── core/ # Project information
- │ ├── technology-stack.md # Version numbers (SINGLE SOURCE OF TRUTH)
- │ ├── project-overview.md
- │ ├── application-architecture.md
- │ └── deployment-architecture.md
- ├── development/ # Development practices
- │ ├── development-workflow.md
- │ ├── testing-patterns.md
- │ └── laravel-boost.md
- ├── patterns/ # Code patterns
- │ ├── database-patterns.md
- │ ├── frontend-patterns.md
- │ ├── security-patterns.md
- │ ├── form-components.md
- │ └── api-and-routing.md
- └── meta/ # Documentation guides
- ├── maintaining-docs.md
- └── sync-guide.md (this file)
- ```
-- **Used by**: All AI tools through CLAUDE.md or coolify-ai-docs.mdc
-
-## Cross-References
-
-All systems reference the `.ai/` directory as the source of truth:
-
-- **CLAUDE.md** → references `.ai/` files for detailed documentation
-- **.cursor/rules/coolify-ai-docs.mdc** → references `.ai/` files for detailed documentation
-- **.ai/README.md** → provides navigation to all documentation
-
-## Maintaining Consistency
-
-### 1. Core Principles (MUST be consistent)
-
-These are defined ONCE in `.ai/core/technology-stack.md`:
-- Laravel version (currently Laravel 12.4.1)
-- PHP version (8.4.7)
-- All package versions (Livewire 3.5.20, Tailwind 4.1.4, etc.)
-
-**Exception**: CLAUDE.md is permitted to show essential version numbers as a quick reference for convenience. These must stay synchronized with `technology-stack.md`. When updating versions, update both locations.
-
-Other critical patterns defined in `.ai/`:
-- Testing execution rules (Docker for Feature tests, mocking for Unit tests)
-- Security patterns and authorization requirements
-- Code style requirements (Pint, PSR-12)
-
-### 2. Where to Make Changes
-
-**For version numbers** (Laravel, PHP, packages):
-1. Update `.ai/core/technology-stack.md` (single source of truth)
-2. Update CLAUDE.md quick reference section (essential versions only)
-3. Verify both files stay synchronized
-4. Never duplicate version numbers in other locations
-
-**For workflow changes** (how to run commands, development setup):
-1. Update `.ai/development/development-workflow.md`
-2. Update quick reference in CLAUDE.md if needed
-3. Verify `.cursor/rules/coolify-ai-docs.mdc` references are correct
-
-**For architectural patterns** (how code should be structured):
-1. Update appropriate file in `.ai/core/`
-2. Add cross-references from related docs
-3. Update CLAUDE.md if it needs to highlight this pattern
-
-**For code patterns** (how to write code):
-1. Update appropriate file in `.ai/patterns/`
-2. Add examples from real codebase
-3. Cross-reference from related docs
-
-**For testing patterns**:
-1. Update `.ai/development/testing-patterns.md`
-2. Ensure CLAUDE.md testing section references it
-
-### 3. Update Checklist
-
-When making significant changes:
-
-- [ ] Identify if change affects core principles (version numbers, critical patterns)
-- [ ] Update primary location in `.ai/` directory
-- [ ] Check if CLAUDE.md needs quick-reference update
-- [ ] Verify `.cursor/rules/coolify-ai-docs.mdc` references are still accurate
-- [ ] Update cross-references in related `.ai/` files
-- [ ] Verify all relative paths work correctly
-- [ ] Test links in markdown files
-- [ ] Run: `./vendor/bin/pint` on modified files (if applicable)
-
-### 4. Common Inconsistencies to Watch
-
-- **Version numbers**: Should ONLY exist in `.ai/core/technology-stack.md`
-- **Testing instructions**: Docker execution requirements must be consistent
-- **File paths**: Ensure relative paths work from their location
-- **Command syntax**: Docker commands, artisan commands must be accurate
-- **Cross-references**: Links must point to current file locations
-
-## File Organization
-
-```
-/
-├── CLAUDE.md # Claude Code entry point
-├── .AI_INSTRUCTIONS_SYNC.md # Redirect to this file
-├── .cursor/
-│ └── rules/
-│ └── coolify-ai-docs.mdc # Cursor IDE master reference
-└── .ai/ # SINGLE SOURCE OF TRUTH
- ├── README.md # Navigation hub
- ├── core/ # Project information
- ├── development/ # Development practices
- ├── patterns/ # Code patterns
- └── meta/ # Documentation guides
-```
-
-## Recent Updates
-
-### 2025-11-18 - Documentation Consolidation
-- ✅ Consolidated all documentation into `.ai/` directory
-- ✅ Created single source of truth for version numbers
-- ✅ Reduced CLAUDE.md from 719 to 319 lines
-- ✅ Replaced 11 .cursor/rules/*.mdc files with single coolify-ai-docs.mdc
-- ✅ Organized by topic: core/, development/, patterns/, meta/
-- ✅ Standardized version numbers (Laravel 12.4.1, PHP 8.4.7, Tailwind 4.1.4)
-- ✅ Created comprehensive navigation with .ai/README.md
-
-### 2025-10-07
-- ✅ Added cross-references between CLAUDE.md and .cursor/rules/
-- ✅ Synchronized Laravel version (12) across all files
-- ✅ Added comprehensive testing execution rules (Docker for Feature tests)
-- ✅ Added test design philosophy (prefer mocking over database)
-- ✅ Fixed inconsistencies in testing documentation
-
-## Maintenance Commands
-
-```bash
-# Check for version inconsistencies (should only be in technology-stack.md)
-# Note: CLAUDE.md is allowed to show quick reference versions
-grep -r "Laravel 12" .ai/ CLAUDE.md .cursor/rules/coolify-ai-docs.mdc
-grep -r "PHP 8.4" .ai/ CLAUDE.md .cursor/rules/coolify-ai-docs.mdc
-
-# Check for broken cross-references to old .mdc files
-grep -r "\.cursor/rules/.*\.mdc" .ai/ CLAUDE.md
-
-# Format all documentation
-./vendor/bin/pint CLAUDE.md .ai/**/*.md
-
-# Search for specific patterns across all docs
-grep -r "pattern_to_check" CLAUDE.md .ai/ .cursor/rules/
-
-# Verify all markdown links work (from repository root)
-find .ai -name "*.md" -exec grep -H "\[.*\](.*)" {} \;
-```
-
-## Contributing
-
-When contributing documentation:
-
-1. **Check `.ai/` directory** for existing documentation
-2. **Update `.ai/` files** - this is the single source of truth
-3. **Use cross-references** - never duplicate content
-4. **Update CLAUDE.md** if adding critical quick-reference information
-5. **Verify `.cursor/rules/coolify-ai-docs.mdc`** still references correctly
-6. **Test all links** work from their respective locations
-7. **Update this sync-guide.md** if changing organizational structure
-8. **Verify consistency** before submitting PR
-
-## Questions?
-
-If unsure about where to document something:
-
-- **Version numbers** → `.ai/core/technology-stack.md` (ONLY location)
-- **Quick reference / commands** → CLAUDE.md + `.ai/development/development-workflow.md`
-- **Detailed patterns / examples** → `.ai/patterns/[topic].md`
-- **Architecture / concepts** → `.ai/core/[topic].md`
-- **Development practices** → `.ai/development/[topic].md`
-- **Documentation guides** → `.ai/meta/[topic].md`
-
-**Golden Rule**: Each piece of information exists in ONE location in `.ai/`, other files reference it.
-
-When in doubt, prefer detailed documentation in `.ai/` and lightweight references in CLAUDE.md and coolify-ai-docs.mdc.
diff --git a/.ai/patterns/api-and-routing.md b/.ai/patterns/api-and-routing.md
deleted file mode 100644
index ceaadaad5..000000000
--- a/.ai/patterns/api-and-routing.md
+++ /dev/null
@@ -1,469 +0,0 @@
-# Coolify API & Routing Architecture
-
-## Routing Structure
-
-Coolify implements **multi-layered routing** with web interfaces, RESTful APIs, webhook endpoints, and real-time communication channels.
-
-## Route Files
-
-### Core Route Definitions
-- **[routes/web.php](mdc:routes/web.php)** - Web application routes (21KB, 362 lines)
-- **[routes/api.php](mdc:routes/api.php)** - RESTful API endpoints (13KB, 185 lines)
-- **[routes/webhooks.php](mdc:routes/webhooks.php)** - Webhook receivers (815B, 22 lines)
-- **[routes/channels.php](mdc:routes/channels.php)** - WebSocket channel definitions (829B, 33 lines)
-- **[routes/console.php](mdc:routes/console.php)** - Artisan command routes (592B, 20 lines)
-
-## Web Application Routing
-
-### Authentication Routes
-```php
-// Laravel Fortify authentication
-Route::middleware('guest')->group(function () {
- Route::get('/login', [AuthController::class, 'login']);
- Route::get('/register', [AuthController::class, 'register']);
- Route::get('/forgot-password', [AuthController::class, 'forgotPassword']);
-});
-```
-
-### Dashboard & Core Features
-```php
-// Main application routes
-Route::middleware(['auth', 'verified'])->group(function () {
- Route::get('/dashboard', Dashboard::class)->name('dashboard');
- Route::get('/projects', ProjectIndex::class)->name('projects');
- Route::get('/servers', ServerIndex::class)->name('servers');
- Route::get('/teams', TeamIndex::class)->name('teams');
-});
-```
-
-### Resource Management Routes
-```php
-// Server management
-Route::prefix('servers')->group(function () {
- Route::get('/{server}', ServerShow::class)->name('server.show');
- Route::get('/{server}/edit', ServerEdit::class)->name('server.edit');
- Route::get('/{server}/logs', ServerLogs::class)->name('server.logs');
-});
-
-// Application management
-Route::prefix('applications')->group(function () {
- Route::get('/{application}', ApplicationShow::class)->name('application.show');
- Route::get('/{application}/deployments', ApplicationDeployments::class);
- Route::get('/{application}/environment-variables', ApplicationEnvironmentVariables::class);
- Route::get('/{application}/logs', ApplicationLogs::class);
-});
-```
-
-## RESTful API Architecture
-
-### API Versioning
-```php
-// API route structure
-Route::prefix('v1')->group(function () {
- // Application endpoints
- Route::apiResource('applications', ApplicationController::class);
- Route::apiResource('servers', ServerController::class);
- Route::apiResource('teams', TeamController::class);
-});
-```
-
-### Authentication & Authorization
-```php
-// Sanctum API authentication
-Route::middleware('auth:sanctum')->group(function () {
- Route::get('/user', function (Request $request) {
- return $request->user();
- });
-
- // Team-scoped resources
- Route::middleware('team.access')->group(function () {
- Route::apiResource('applications', ApplicationController::class);
- });
-});
-```
-
-### Application Management API
-```php
-// Application CRUD operations
-Route::prefix('applications')->group(function () {
- Route::get('/', [ApplicationController::class, 'index']);
- Route::post('/', [ApplicationController::class, 'store']);
- Route::get('/{application}', [ApplicationController::class, 'show']);
- Route::patch('/{application}', [ApplicationController::class, 'update']);
- Route::delete('/{application}', [ApplicationController::class, 'destroy']);
-
- // Deployment operations
- Route::post('/{application}/deploy', [ApplicationController::class, 'deploy']);
- Route::post('/{application}/restart', [ApplicationController::class, 'restart']);
- Route::post('/{application}/stop', [ApplicationController::class, 'stop']);
- Route::get('/{application}/logs', [ApplicationController::class, 'logs']);
-});
-```
-
-### Server Management API
-```php
-// Server operations
-Route::prefix('servers')->group(function () {
- Route::get('/', [ServerController::class, 'index']);
- Route::post('/', [ServerController::class, 'store']);
- Route::get('/{server}', [ServerController::class, 'show']);
- Route::patch('/{server}', [ServerController::class, 'update']);
- Route::delete('/{server}', [ServerController::class, 'destroy']);
-
- // Server actions
- Route::post('/{server}/validate', [ServerController::class, 'validate']);
- Route::get('/{server}/usage', [ServerController::class, 'usage']);
- Route::post('/{server}/cleanup', [ServerController::class, 'cleanup']);
-});
-```
-
-### Database Management API
-```php
-// Database operations
-Route::prefix('databases')->group(function () {
- Route::get('/', [DatabaseController::class, 'index']);
- Route::post('/', [DatabaseController::class, 'store']);
- Route::get('/{database}', [DatabaseController::class, 'show']);
- Route::patch('/{database}', [DatabaseController::class, 'update']);
- Route::delete('/{database}', [DatabaseController::class, 'destroy']);
-
- // Database actions
- Route::post('/{database}/backup', [DatabaseController::class, 'backup']);
- Route::post('/{database}/restore', [DatabaseController::class, 'restore']);
- Route::get('/{database}/logs', [DatabaseController::class, 'logs']);
-});
-```
-
-## Webhook Architecture
-
-### Git Integration Webhooks
-```php
-// GitHub webhook endpoints
-Route::post('/webhooks/github/{application}', [GitHubWebhookController::class, 'handle'])
- ->name('webhooks.github');
-
-// GitLab webhook endpoints
-Route::post('/webhooks/gitlab/{application}', [GitLabWebhookController::class, 'handle'])
- ->name('webhooks.gitlab');
-
-// Generic Git webhooks
-Route::post('/webhooks/git/{application}', [GitWebhookController::class, 'handle'])
- ->name('webhooks.git');
-```
-
-### Deployment Webhooks
-```php
-// Deployment status webhooks
-Route::post('/webhooks/deployment/{deployment}/success', [DeploymentWebhookController::class, 'success']);
-Route::post('/webhooks/deployment/{deployment}/failure', [DeploymentWebhookController::class, 'failure']);
-Route::post('/webhooks/deployment/{deployment}/progress', [DeploymentWebhookController::class, 'progress']);
-```
-
-### Third-Party Integration Webhooks
-```php
-// Monitoring webhooks
-Route::post('/webhooks/monitoring/{server}', [MonitoringWebhookController::class, 'handle']);
-
-// Backup status webhooks
-Route::post('/webhooks/backup/{backup}', [BackupWebhookController::class, 'handle']);
-
-// SSL certificate webhooks
-Route::post('/webhooks/ssl/{certificate}', [SslWebhookController::class, 'handle']);
-```
-
-## WebSocket Channel Definitions
-
-### Real-Time Channels
-```php
-// Private channels for team members
-Broadcast::channel('team.{teamId}', function ($user, $teamId) {
- return $user->teams->contains('id', $teamId);
-});
-
-// Application deployment channels
-Broadcast::channel('application.{applicationId}', function ($user, $applicationId) {
- return $user->hasAccessToApplication($applicationId);
-});
-
-// Server monitoring channels
-Broadcast::channel('server.{serverId}', function ($user, $serverId) {
- return $user->hasAccessToServer($serverId);
-});
-```
-
-### Presence Channels
-```php
-// Team collaboration presence
-Broadcast::channel('team.{teamId}.presence', function ($user, $teamId) {
- if ($user->teams->contains('id', $teamId)) {
- return ['id' => $user->id, 'name' => $user->name];
- }
-});
-```
-
-## API Controllers
-
-### Location: [app/Http/Controllers/Api/](mdc:app/Http/Controllers)
-
-#### Resource Controllers
-```php
-class ApplicationController extends Controller
-{
- public function index(Request $request)
- {
- return ApplicationResource::collection(
- $request->user()->currentTeam->applications()
- ->with(['server', 'environment'])
- ->paginate()
- );
- }
-
- public function store(StoreApplicationRequest $request)
- {
- $application = $request->user()->currentTeam
- ->applications()
- ->create($request->validated());
-
- return new ApplicationResource($application);
- }
-
- public function deploy(Application $application)
- {
- $deployment = $application->deploy();
-
- return response()->json([
- 'message' => 'Deployment started',
- 'deployment_id' => $deployment->id
- ]);
- }
-}
-```
-
-### API Responses & Resources
-```php
-// API Resource classes
-class ApplicationResource extends JsonResource
-{
- public function toArray($request)
- {
- return [
- 'id' => $this->id,
- 'name' => $this->name,
- 'fqdn' => $this->fqdn,
- 'status' => $this->status,
- 'git_repository' => $this->git_repository,
- 'git_branch' => $this->git_branch,
- 'created_at' => $this->created_at,
- 'updated_at' => $this->updated_at,
- 'server' => new ServerResource($this->whenLoaded('server')),
- 'environment' => new EnvironmentResource($this->whenLoaded('environment')),
- ];
- }
-}
-```
-
-## API Authentication
-
-### Sanctum Token Authentication
-```php
-// API token generation
-Route::post('/auth/tokens', function (Request $request) {
- $request->validate([
- 'name' => 'required|string',
- 'abilities' => 'array'
- ]);
-
- $token = $request->user()->createToken(
- $request->name,
- $request->abilities ?? []
- );
-
- return response()->json([
- 'token' => $token->plainTextToken,
- 'abilities' => $token->accessToken->abilities
- ]);
-});
-```
-
-### Team-Based Authorization
-```php
-// Team access middleware
-class EnsureTeamAccess
-{
- public function handle($request, Closure $next)
- {
- $teamId = $request->route('team');
-
- if (!$request->user()->teams->contains('id', $teamId)) {
- abort(403, 'Access denied to team resources');
- }
-
- return $next($request);
- }
-}
-```
-
-## Rate Limiting
-
-### API Rate Limits
-```php
-// API throttling configuration
-RateLimiter::for('api', function (Request $request) {
- return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());
-});
-
-// Deployment rate limiting
-RateLimiter::for('deployments', function (Request $request) {
- return Limit::perMinute(10)->by($request->user()->id);
-});
-```
-
-### Webhook Rate Limiting
-```php
-// Webhook throttling
-RateLimiter::for('webhooks', function (Request $request) {
- return Limit::perMinute(100)->by($request->ip());
-});
-```
-
-## Route Model Binding
-
-### Custom Route Bindings
-```php
-// Custom model binding for applications
-Route::bind('application', function ($value) {
- return Application::where('uuid', $value)
- ->orWhere('id', $value)
- ->firstOrFail();
-});
-
-// Team-scoped model binding
-Route::bind('team_application', function ($value, $route) {
- $teamId = $route->parameter('team');
- return Application::whereHas('environment.project', function ($query) use ($teamId) {
- $query->where('team_id', $teamId);
- })->findOrFail($value);
-});
-```
-
-## API Documentation
-
-### OpenAPI Specification
-- **[openapi.json](mdc:openapi.json)** - API documentation (373KB, 8316 lines)
-- **[openapi.yaml](mdc:openapi.yaml)** - YAML format documentation (184KB, 5579 lines)
-
-### Documentation Generation
-```php
-// Swagger/OpenAPI annotations
-/**
- * @OA\Get(
- * path="/api/v1/applications",
- * summary="List applications",
- * tags={"Applications"},
- * security={{"bearerAuth":{}}},
- * @OA\Response(
- * response=200,
- * description="List of applications",
- * @OA\JsonContent(type="array", @OA\Items(ref="#/components/schemas/Application"))
- * )
- * )
- */
-```
-
-## Error Handling
-
-### API Error Responses
-```php
-// Standardized error response format
-class ApiExceptionHandler
-{
- public function render($request, Throwable $exception)
- {
- if ($request->expectsJson()) {
- return response()->json([
- 'message' => $exception->getMessage(),
- 'error_code' => $this->getErrorCode($exception),
- 'timestamp' => now()->toISOString()
- ], $this->getStatusCode($exception));
- }
-
- return parent::render($request, $exception);
- }
-}
-```
-
-### Validation Error Handling
-```php
-// Form request validation
-class StoreApplicationRequest extends FormRequest
-{
- public function rules()
- {
- return [
- 'name' => 'required|string|max:255',
- 'git_repository' => 'required|url',
- 'git_branch' => 'required|string',
- 'server_id' => 'required|exists:servers,id',
- 'environment_id' => 'required|exists:environments,id'
- ];
- }
-
- public function failedValidation(Validator $validator)
- {
- throw new HttpResponseException(
- response()->json([
- 'message' => 'Validation failed',
- 'errors' => $validator->errors()
- ], 422)
- );
- }
-}
-```
-
-## Real-Time API Integration
-
-### WebSocket Events
-```php
-// Broadcasting deployment events
-class DeploymentStarted implements ShouldBroadcast
-{
- public $application;
- public $deployment;
-
- public function broadcastOn()
- {
- return [
- new PrivateChannel("application.{$this->application->id}"),
- new PrivateChannel("team.{$this->application->team->id}")
- ];
- }
-
- public function broadcastWith()
- {
- return [
- 'deployment_id' => $this->deployment->id,
- 'status' => 'started',
- 'timestamp' => now()
- ];
- }
-}
-```
-
-### API Event Streaming
-```php
-// Server-Sent Events for real-time updates
-Route::get('/api/v1/applications/{application}/events', function (Application $application) {
- return response()->stream(function () use ($application) {
- while (true) {
- $events = $application->getRecentEvents();
- foreach ($events as $event) {
- echo "data: " . json_encode($event) . "\n\n";
- }
- usleep(1000000); // 1 second
- }
- }, 200, [
- 'Content-Type' => 'text/event-stream',
- 'Cache-Control' => 'no-cache',
- ]);
-});
-```
diff --git a/.ai/patterns/database-patterns.md b/.ai/patterns/database-patterns.md
deleted file mode 100644
index 5a9d16f71..000000000
--- a/.ai/patterns/database-patterns.md
+++ /dev/null
@@ -1,377 +0,0 @@
-# Coolify Database Architecture & Patterns
-
-## Database Strategy
-
-Coolify uses **PostgreSQL 15** as the primary database with **Redis 7** for caching and real-time features. The architecture supports managing multiple external databases across different servers.
-
-## Primary Database (PostgreSQL)
-
-### Core Tables & Models
-
-#### User & Team Management
-- **[User.php](mdc:app/Models/User.php)** - User authentication and profiles
-- **[Team.php](mdc:app/Models/Team.php)** - Multi-tenant organization structure
-- **[TeamInvitation.php](mdc:app/Models/TeamInvitation.php)** - Team collaboration invitations
-- **[PersonalAccessToken.php](mdc:app/Models/PersonalAccessToken.php)** - API token management
-
-#### Infrastructure Management
-- **[Server.php](mdc:app/Models/Server.php)** - Physical/virtual server definitions (46KB, complex)
-- **[PrivateKey.php](mdc:app/Models/PrivateKey.php)** - SSH key management
-- **[ServerSetting.php](mdc:app/Models/ServerSetting.php)** - Server-specific configurations
-
-#### Project Organization
-- **[Project.php](mdc:app/Models/Project.php)** - Project containers for applications
-- **[Environment.php](mdc:app/Models/Environment.php)** - Environment isolation (staging, production, etc.)
-- **[ProjectSetting.php](mdc:app/Models/ProjectSetting.php)** - Project-specific settings
-
-#### Application Deployment
-- **[Application.php](mdc:app/Models/Application.php)** - Main application entity (74KB, highly complex)
-- **[ApplicationSetting.php](mdc:app/Models/ApplicationSetting.php)** - Application configurations
-- **[ApplicationDeploymentQueue.php](mdc:app/Models/ApplicationDeploymentQueue.php)** - Deployment orchestration
-- **[ApplicationPreview.php](mdc:app/Models/ApplicationPreview.php)** - Preview environment management
-
-#### Service Management
-- **[Service.php](mdc:app/Models/Service.php)** - Service definitions (58KB, complex)
-- **[ServiceApplication.php](mdc:app/Models/ServiceApplication.php)** - Service components
-- **[ServiceDatabase.php](mdc:app/Models/ServiceDatabase.php)** - Service-attached databases
-
-## Database Type Support
-
-### Standalone Database Models
-Each database type has its own dedicated model with specific configurations:
-
-#### SQL Databases
-- **[StandalonePostgresql.php](mdc:app/Models/StandalonePostgresql.php)** - PostgreSQL instances
-- **[StandaloneMysql.php](mdc:app/Models/StandaloneMysql.php)** - MySQL instances
-- **[StandaloneMariadb.php](mdc:app/Models/StandaloneMariadb.php)** - MariaDB instances
-
-#### NoSQL & Analytics
-- **[StandaloneMongodb.php](mdc:app/Models/StandaloneMongodb.php)** - MongoDB instances
-- **[StandaloneClickhouse.php](mdc:app/Models/StandaloneClickhouse.php)** - ClickHouse analytics
-
-#### Caching & In-Memory
-- **[StandaloneRedis.php](mdc:app/Models/StandaloneRedis.php)** - Redis instances
-- **[StandaloneKeydb.php](mdc:app/Models/StandaloneKeydb.php)** - KeyDB instances
-- **[StandaloneDragonfly.php](mdc:app/Models/StandaloneDragonfly.php)** - Dragonfly instances
-
-## Configuration Management
-
-### Environment Variables
-- **[EnvironmentVariable.php](mdc:app/Models/EnvironmentVariable.php)** - Application-specific environment variables
-- **[SharedEnvironmentVariable.php](mdc:app/Models/SharedEnvironmentVariable.php)** - Shared across applications
-
-### Settings Hierarchy
-- **[InstanceSettings.php](mdc:app/Models/InstanceSettings.php)** - Global Coolify instance settings
-- **[ServerSetting.php](mdc:app/Models/ServerSetting.php)** - Server-specific settings
-- **[ProjectSetting.php](mdc:app/Models/ProjectSetting.php)** - Project-level settings
-- **[ApplicationSetting.php](mdc:app/Models/ApplicationSetting.php)** - Application settings
-
-## Storage & Backup Systems
-
-### Storage Management
-- **[S3Storage.php](mdc:app/Models/S3Storage.php)** - S3-compatible storage configurations
-- **[LocalFileVolume.php](mdc:app/Models/LocalFileVolume.php)** - Local filesystem volumes
-- **[LocalPersistentVolume.php](mdc:app/Models/LocalPersistentVolume.php)** - Persistent volume management
-
-### Backup Infrastructure
-- **[ScheduledDatabaseBackup.php](mdc:app/Models/ScheduledDatabaseBackup.php)** - Automated backup scheduling
-- **[ScheduledDatabaseBackupExecution.php](mdc:app/Models/ScheduledDatabaseBackupExecution.php)** - Backup execution tracking
-
-### Task Scheduling
-- **[ScheduledTask.php](mdc:app/Models/ScheduledTask.php)** - Cron job management
-- **[ScheduledTaskExecution.php](mdc:app/Models/ScheduledTaskExecution.php)** - Task execution history
-
-## Notification & Integration Models
-
-### Notification Channels
-- **[EmailNotificationSettings.php](mdc:app/Models/EmailNotificationSettings.php)** - Email notifications
-- **[DiscordNotificationSettings.php](mdc:app/Models/DiscordNotificationSettings.php)** - Discord integration
-- **[SlackNotificationSettings.php](mdc:app/Models/SlackNotificationSettings.php)** - Slack integration
-- **[TelegramNotificationSettings.php](mdc:app/Models/TelegramNotificationSettings.php)** - Telegram bot
-- **[PushoverNotificationSettings.php](mdc:app/Models/PushoverNotificationSettings.php)** - Pushover notifications
-
-### Source Control Integration
-- **[GithubApp.php](mdc:app/Models/GithubApp.php)** - GitHub App integration
-- **[GitlabApp.php](mdc:app/Models/GitlabApp.php)** - GitLab integration
-
-### OAuth & Authentication
-- **[OauthSetting.php](mdc:app/Models/OauthSetting.php)** - OAuth provider configurations
-
-## Docker & Container Management
-
-### Container Orchestration
-- **[StandaloneDocker.php](mdc:app/Models/StandaloneDocker.php)** - Standalone Docker containers
-- **[SwarmDocker.php](mdc:app/Models/SwarmDocker.php)** - Docker Swarm management
-
-### SSL & Security
-- **[SslCertificate.php](mdc:app/Models/SslCertificate.php)** - SSL certificate management
-
-## Database Migration Strategy
-
-### Migration Location: [database/migrations/](mdc:database/migrations)
-
-#### Migration Patterns
-```php
-// Typical Coolify migration structure
-Schema::create('applications', function (Blueprint $table) {
- $table->id();
- $table->string('name');
- $table->string('fqdn')->nullable();
- $table->json('environment_variables')->nullable();
- $table->foreignId('destination_id');
- $table->foreignId('source_id');
- $table->timestamps();
-});
-```
-
-### Schema Versioning
-- **Incremental migrations** for database evolution
-- **Data migrations** for complex transformations
-- **Rollback support** for deployment safety
-
-## Eloquent Model Patterns
-
-### Base Model Structure
-- **[BaseModel.php](mdc:app/Models/BaseModel.php)** - Common model functionality
-- **UUID primary keys** for distributed systems
-- **Soft deletes** for audit trails
-- **Activity logging** with Spatie package
-
-### **CRITICAL: Mass Assignment Protection**
-**When adding new database columns, you MUST update the model's `$fillable` array.** Without this, Laravel will silently ignore mass assignment operations like `Model::create()` or `$model->update()`.
-
-**Checklist for new columns:**
-1. ✅ Create migration file
-2. ✅ Run migration
-3. ✅ **Add column to model's `$fillable` array**
-4. ✅ Update any Livewire components that sync this property
-5. ✅ Test that the column can be read and written
-
-**Example:**
-```php
-class Server extends BaseModel
-{
- protected $fillable = [
- 'name',
- 'ip',
- 'port',
- 'is_validating', // ← MUST add new columns here
- ];
-}
-```
-
-### Relationship Patterns
-```php
-// Typical relationship structure in Application model
-class Application extends Model
-{
- public function server()
- {
- return $this->belongsTo(Server::class);
- }
-
- public function environment()
- {
- return $this->belongsTo(Environment::class);
- }
-
- public function deployments()
- {
- return $this->hasMany(ApplicationDeploymentQueue::class);
- }
-
- public function environmentVariables()
- {
- return $this->hasMany(EnvironmentVariable::class);
- }
-}
-```
-
-### Model Traits
-```php
-// Common traits used across models
-use SoftDeletes;
-use LogsActivity;
-use HasFactory;
-use HasUuids;
-```
-
-## Caching Strategy (Redis)
-
-### Cache Usage Patterns
-- **Session storage** - User authentication sessions
-- **Queue backend** - Background job processing
-- **Model caching** - Expensive query results
-- **Real-time data** - WebSocket state management
-
-### Cache Keys Structure
-```
-coolify:session:{session_id}
-coolify:server:{server_id}:status
-coolify:deployment:{deployment_id}:logs
-coolify:user:{user_id}:teams
-```
-
-## Query Optimization Patterns
-
-### Eager Loading
-```php
-// Optimized queries with relationships
-$applications = Application::with([
- 'server',
- 'environment.project',
- 'environmentVariables',
- 'deployments' => function ($query) {
- $query->latest()->limit(5);
- }
-])->get();
-```
-
-### Chunking for Large Datasets
-```php
-// Processing large datasets efficiently
-Server::chunk(100, function ($servers) {
- foreach ($servers as $server) {
- // Process server monitoring
- }
-});
-```
-
-### Database Indexes
-- **Primary keys** on all tables
-- **Foreign key indexes** for relationships
-- **Composite indexes** for common queries
-- **Unique constraints** for business rules
-
-### Request-Level Caching with ownedByCurrentTeamCached()
-
-Many models have both `ownedByCurrentTeam()` (returns query builder) and `ownedByCurrentTeamCached()` (returns cached collection). **Always prefer the cached version** to avoid duplicate database queries within the same request.
-
-**Models with cached methods available:**
-- `Server`, `PrivateKey`, `Project`
-- `Application`
-- `StandalonePostgresql`, `StandaloneMysql`, `StandaloneRedis`, `StandaloneMariadb`, `StandaloneMongodb`, `StandaloneKeydb`, `StandaloneDragonfly`, `StandaloneClickhouse`
-- `Service`, `ServiceApplication`, `ServiceDatabase`
-
-**Usage patterns:**
-```php
-// ✅ CORRECT - Uses request-level cache (via Laravel's once() helper)
-$servers = Server::ownedByCurrentTeamCached();
-
-// ❌ AVOID - Makes a new database query each time
-$servers = Server::ownedByCurrentTeam()->get();
-
-// ✅ CORRECT - Filter cached collection in memory
-$activeServers = Server::ownedByCurrentTeamCached()->where('is_active', true);
-$server = Server::ownedByCurrentTeamCached()->firstWhere('id', $serverId);
-$serverIds = Server::ownedByCurrentTeamCached()->pluck('id');
-
-// ❌ AVOID - Making filtered database queries when data is already cached
-$activeServers = Server::ownedByCurrentTeam()->where('is_active', true)->get();
-```
-
-**When to use which:**
-- `ownedByCurrentTeamCached()` - **Default choice** for reading team data
-- `ownedByCurrentTeam()` - Only when you need to chain query builder methods that can't be done on collections (like `with()` for eager loading), or when you explicitly need a fresh database query
-
-**Implementation pattern for new models:**
-```php
-/**
- * Get query builder for resources owned by current team.
- * If you need all resources without further query chaining, use ownedByCurrentTeamCached() instead.
- */
-public static function ownedByCurrentTeam()
-{
- return self::whereTeamId(currentTeam()->id);
-}
-
-/**
- * Get all resources owned by current team (cached for request duration).
- */
-public static function ownedByCurrentTeamCached()
-{
- return once(function () {
- return self::ownedByCurrentTeam()->get();
- });
-}
-```
-
-## Data Consistency Patterns
-
-### Database Transactions
-```php
-// Atomic operations for deployment
-DB::transaction(function () {
- $application = Application::create($data);
- $application->environmentVariables()->createMany($envVars);
- $application->deployments()->create(['status' => 'queued']);
-});
-```
-
-### Model Events
-```php
-// Automatic cleanup on model deletion
-class Application extends Model
-{
- protected static function booted()
- {
- static::deleting(function ($application) {
- $application->environmentVariables()->delete();
- $application->deployments()->delete();
- });
- }
-}
-```
-
-## Backup & Recovery
-
-### Database Backup Strategy
-- **Automated PostgreSQL backups** via scheduled tasks
-- **Point-in-time recovery** capability
-- **Cross-region backup** replication
-- **Backup verification** and testing
-
-### Data Export/Import
-- **Application configurations** export/import
-- **Environment variable** bulk operations
-- **Server configurations** backup and restore
-
-## Performance Monitoring
-
-### Query Performance
-- **Laravel Telescope** for development debugging
-- **Slow query logging** in production
-- **Database connection** pooling
-- **Read replica** support for scaling
-
-### Metrics Collection
-- **Database size** monitoring
-- **Connection count** tracking
-- **Query execution time** analysis
-- **Cache hit rates** monitoring
-
-## Multi-Tenancy Pattern
-
-### Team-Based Isolation
-```php
-// Global scope for team-based filtering
-class Application extends Model
-{
- protected static function booted()
- {
- static::addGlobalScope('team', function (Builder $builder) {
- if (auth()->user()) {
- $builder->whereHas('environment.project', function ($query) {
- $query->where('team_id', auth()->user()->currentTeam->id);
- });
- }
- });
- }
-}
-```
-
-### Data Separation
-- **Team-scoped queries** by default
-- **Cross-team access** controls
-- **Admin access** patterns
-- **Data isolation** guarantees
diff --git a/.ai/patterns/form-components.md b/.ai/patterns/form-components.md
deleted file mode 100644
index 3ff1d0f81..000000000
--- a/.ai/patterns/form-components.md
+++ /dev/null
@@ -1,447 +0,0 @@
-
-# Enhanced Form Components with Authorization
-
-## Overview
-
-Coolify's form components now feature **built-in authorization** that automatically handles permission-based UI control, dramatically reducing code duplication and improving security consistency.
-
-## Enhanced Components
-
-All form components now support the `canGate` authorization system:
-
-- **[Input.php](mdc:app/View/Components/Forms/Input.php)** - Text, password, and other input fields
-- **[Select.php](mdc:app/View/Components/Forms/Select.php)** - Dropdown selection components
-- **[Textarea.php](mdc:app/View/Components/Forms/Textarea.php)** - Multi-line text areas
-- **[Checkbox.php](mdc:app/View/Components/Forms/Checkbox.php)** - Boolean toggle components
-- **[Button.php](mdc:app/View/Components/Forms/Button.php)** - Action buttons
-
-## Authorization Parameters
-
-### Core Parameters
-```php
-public ?string $canGate = null; // Gate name: 'update', 'view', 'deploy', 'delete'
-public mixed $canResource = null; // Resource model instance to check against
-public bool $autoDisable = true; // Automatically disable if no permission
-```
-
-### How It Works
-```php
-// Automatic authorization logic in each component
-if ($this->canGate && $this->canResource && $this->autoDisable) {
- $hasPermission = Gate::allows($this->canGate, $this->canResource);
-
- if (! $hasPermission) {
- $this->disabled = true;
- // For Checkbox: also sets $this->instantSave = false;
- }
-}
-```
-
-## Usage Patterns
-
-### ✅ Recommended: Single Line Pattern
-
-**Before (Verbose, 6+ lines per element):**
-```html
-@can('update', $application)
-
-
- Save
-@else
-
-
-@endcan
-```
-
-**After (Clean, 1 line per element):**
-```html
-
-
-Save
-```
-
-**Result: 90% code reduction!**
-
-### Component-Specific Examples
-
-#### Input Fields
-```html
-
-
-
-
-
-
-
-
-```
-
-#### Select Dropdowns
-```html
-
-
-
-
-
-
-
-
-
- @foreach($servers as $server)
-
- @endforeach
-
-```
-
-#### Checkboxes with InstantSave
-```html
-
-
-
-
-
-
-
-
-```
-
-#### Textareas
-```html
-
-
-
-
-
-```
-
-#### Buttons
-```html
-
-
- Save Configuration
-
-
-
-
- Deploy Application
-
-
-
-
- Delete Application
-
-```
-
-## Advanced Usage
-
-### Custom Authorization Logic
-```html
-
-
-```
-
-### Multiple Permission Checks
-```html
-
-
-```
-
-### Conditional Resources
-```html
-
-
- {{ $isEditing ? 'Save Changes' : 'View Details' }}
-
-```
-
-## Supported Gates
-
-### Resource-Level Gates
-- `view` - Read access to resource details
-- `update` - Modify resource configuration and settings
-- `deploy` - Deploy, restart, or manage resource state
-- `delete` - Remove or destroy resource
-- `clone` - Duplicate resource to another location
-
-### Global Gates
-- `createAnyResource` - Create new resources of any type
-- `manageTeam` - Team administration permissions
-- `accessServer` - Server-level access permissions
-
-## Supported Resources
-
-### Primary Resources
-- `$application` - Application instances and configurations
-- `$service` - Docker Compose services and components
-- `$database` - Database instances (PostgreSQL, MySQL, etc.)
-- `$server` - Physical or virtual server instances
-
-### Container Resources
-- `$project` - Project containers and environments
-- `$environment` - Environment-specific configurations
-- `$team` - Team and organization contexts
-
-### Infrastructure Resources
-- `$privateKey` - SSH private keys and certificates
-- `$source` - Git sources and repositories
-- `$destination` - Deployment destinations and targets
-
-## Component Behavior
-
-### Input Components (Input, Select, Textarea)
-When authorization fails:
-- **disabled = true** - Field becomes non-editable
-- **Visual styling** - Opacity reduction and disabled cursor
-- **Form submission** - Values are ignored in forms
-- **User feedback** - Clear visual indication of restricted access
-
-### Checkbox Components
-When authorization fails:
-- **disabled = true** - Checkbox becomes non-clickable
-- **instantSave = false** - Automatic saving is disabled
-- **State preservation** - Current value is maintained but read-only
-- **Visual styling** - Disabled appearance with reduced opacity
-
-### Button Components
-When authorization fails:
-- **disabled = true** - Button becomes non-clickable
-- **Event blocking** - Click handlers are ignored
-- **Visual styling** - Disabled appearance and cursor
-- **Loading states** - Loading indicators are disabled
-
-## Migration Guide
-
-### Converting Existing Forms
-
-**Old Pattern:**
-```html
-
-```
-
-**New Pattern:**
-```html
-
-```
-
-### Gradual Migration Strategy
-
-1. **Start with new forms** - Use the new pattern for all new components
-2. **Convert high-traffic areas** - Migrate frequently used forms first
-3. **Batch convert similar forms** - Group similar authorization patterns
-4. **Test thoroughly** - Verify authorization behavior matches expectations
-5. **Remove old patterns** - Clean up legacy @can/@else blocks
-
-## Testing Patterns
-
-### Component Authorization Tests
-```php
-// Test authorization integration in components
-test('input component respects authorization', function () {
- $user = User::factory()->member()->create();
- $application = Application::factory()->create();
-
- // Member should see disabled input
- $component = Livewire::actingAs($user)
- ->test(TestComponent::class, [
- 'canGate' => 'update',
- 'canResource' => $application
- ]);
-
- expect($component->get('disabled'))->toBeTrue();
-});
-
-test('checkbox disables instantSave for unauthorized users', function () {
- $user = User::factory()->member()->create();
- $application = Application::factory()->create();
-
- $component = Livewire::actingAs($user)
- ->test(CheckboxComponent::class, [
- 'instantSave' => true,
- 'canGate' => 'update',
- 'canResource' => $application
- ]);
-
- expect($component->get('disabled'))->toBeTrue();
- expect($component->get('instantSave'))->toBeFalse();
-});
-```
-
-### Integration Tests
-```php
-// Test full form authorization behavior
-test('application form respects member permissions', function () {
- $member = User::factory()->member()->create();
- $application = Application::factory()->create();
-
- $this->actingAs($member)
- ->get(route('application.edit', $application))
- ->assertSee('disabled')
- ->assertDontSee('Save Configuration');
-});
-```
-
-## Best Practices
-
-### Consistent Gate Usage
-- Use `update` for configuration changes
-- Use `deploy` for operational actions
-- Use `view` for read-only access
-- Use `delete` for destructive actions
-
-### Resource Context
-- Always pass the specific resource being acted upon
-- Use team context for creation permissions
-- Consider nested resource relationships
-
-### Error Handling
-- Provide clear feedback for disabled components
-- Use helper text to explain permission requirements
-- Consider tooltips for disabled buttons
-
-### Performance
-- Authorization checks are cached per request
-- Use eager loading for resource relationships
-- Consider query optimization for complex permissions
-
-## Common Patterns
-
-### Application Configuration Forms
-```html
-
-
-...
-
-Save
-```
-
-### Service Configuration Forms
-```html
-
-
-
-
-Save
-
-
-
-
-
-@can('update', $service)
-
-@endcan
-```
-
-### Server Management Forms
-```html
-
-
-...
-Delete Server
-```
-
-### Resource Creation Forms
-```html
-
-
-...
-Create Application
-```
\ No newline at end of file
diff --git a/.ai/patterns/frontend-patterns.md b/.ai/patterns/frontend-patterns.md
deleted file mode 100644
index 675881608..000000000
--- a/.ai/patterns/frontend-patterns.md
+++ /dev/null
@@ -1,696 +0,0 @@
-# Coolify Frontend Architecture & Patterns
-
-## Frontend Philosophy
-
-Coolify uses a **server-side first** approach with minimal JavaScript, leveraging Livewire for reactivity and Alpine.js for lightweight client-side interactions.
-
-## Core Frontend Stack
-
-### Livewire 3.5+ (Primary Framework)
-- **Server-side rendering** with reactive components
-- **Real-time updates** without page refreshes
-- **State management** handled on the server
-- **WebSocket integration** for live updates
-
-### Alpine.js (Client-Side Interactivity)
-- **Lightweight JavaScript** for DOM manipulation
-- **Declarative directives** in HTML
-- **Component-like behavior** without build steps
-- **Perfect companion** to Livewire
-
-### Tailwind CSS 4.1+ (Styling)
-- **Utility-first** CSS framework
-- **Custom design system** for deployment platform
-- **Responsive design** built-in
-- **Dark mode support**
-
-## Livewire Component Structure
-
-### Location: [app/Livewire/](mdc:app/Livewire)
-
-#### Core Application Components
-- **[Dashboard.php](mdc:app/Livewire/Dashboard.php)** - Main dashboard interface
-- **[ActivityMonitor.php](mdc:app/Livewire/ActivityMonitor.php)** - Real-time activity tracking
-- **[MonacoEditor.php](mdc:app/Livewire/MonacoEditor.php)** - Code editor component
-
-#### Server Management
-- **Server/** directory - Server configuration and monitoring
-- Real-time server status updates
-- SSH connection management
-- Resource monitoring
-
-#### Project & Application Management
-- **Project/** directory - Project organization
-- Application deployment interfaces
-- Environment variable management
-- Service configuration
-
-#### Settings & Configuration
-- **Settings/** directory - System configuration
-- **[SettingsEmail.php](mdc:app/Livewire/SettingsEmail.php)** - Email notification setup
-- **[SettingsOauth.php](mdc:app/Livewire/SettingsOauth.php)** - OAuth provider configuration
-- **[SettingsBackup.php](mdc:app/Livewire/SettingsBackup.php)** - Backup configuration
-
-#### User & Team Management
-- **Team/** directory - Team collaboration features
-- **Profile/** directory - User profile management
-- **Security/** directory - Security settings
-
-## Blade Template Organization
-
-### Location: [resources/views/](mdc:resources/views)
-
-#### Layout Structure
-- **layouts/** - Base layout templates
-- **components/** - Reusable UI components
-- **livewire/** - Livewire component views
-
-#### Feature-Specific Views
-- **server/** - Server management interfaces
-- **auth/** - Authentication pages
-- **emails/** - Email templates
-- **errors/** - Error pages
-
-## Interactive Components
-
-### Monaco Editor Integration
-- **Code editing** for configuration files
-- **Syntax highlighting** for multiple languages
-- **Live validation** and error detection
-- **Integration** with deployment process
-
-### Terminal Emulation (XTerm.js)
-- **Real-time terminal** access to servers
-- **WebSocket-based** communication
-- **Multi-session** support
-- **Secure connection** through SSH
-
-### Real-Time Updates
-- **WebSocket connections** via Laravel Echo
-- **Live deployment logs** streaming
-- **Server monitoring** with live metrics
-- **Activity notifications** in real-time
-
-## Alpine.js Patterns
-
-### Common Directives Used
-```html
-
-