|
| 1 | +# AGENTS.md |
| 2 | + |
| 3 | +This file provides guidance to agentic coding tools when working with code in this repository. |
| 4 | + |
| 5 | +## Project Overview |
| 6 | + |
| 7 | +Coolify is an open-source, self-hostable PaaS (alternative to Heroku/Netlify/Vercel). It manages servers, applications, databases, and services via SSH. Built with Laravel 12 (using Laravel 10 file structure), Livewire 3, and Tailwind CSS v4. |
| 8 | + |
1 | 9 | ## Design Reference |
2 | 10 |
|
3 | 11 | For UI/UX design specifications, principles, and visual standards, consult `DESIGN.md` in the [coollabsio/architecture](https://github.com/coollabsio/architecture) repo. |
4 | 12 |
|
| 13 | +## Development Environment |
| 14 | + |
| 15 | +Docker Compose-based dev setup with services: coolify (app), postgres, redis, soketi (WebSockets), vite, testing-host, mailpit, minio. |
| 16 | + |
| 17 | +```bash |
| 18 | +# Start dev environment (uses docker-compose.dev.yml) |
| 19 | +spin up # or: docker compose -f docker-compose.dev.yml up -d |
| 20 | +spin down # stop services |
| 21 | +``` |
| 22 | + |
| 23 | +The app runs at `localhost:8000` by default. Vite dev server on port 5173. |
| 24 | + |
| 25 | +## Common Commands |
| 26 | + |
| 27 | +```bash |
| 28 | +# Tests (Pest 4) |
| 29 | +php artisan test --compact # all tests |
| 30 | +php artisan test --compact --filter=testName # single test |
| 31 | +php artisan test --compact tests/Feature/SomeTest.php # specific file |
| 32 | + |
| 33 | +# Code formatting (Pint, Laravel preset) |
| 34 | +vendor/bin/pint --dirty --format agent # format changed files |
| 35 | + |
| 36 | +# Frontend |
| 37 | +npm run dev # vite dev server |
| 38 | +npm run build # production build |
| 39 | +``` |
| 40 | + |
| 41 | +## Browser Tests (Pest Browser Plugin) |
| 42 | + |
| 43 | +Uses `pestphp/pest-plugin-browser` with Laravel Dusk 8. New browser tests go in `tests/v4/Browser/`. |
| 44 | + |
| 45 | +```bash |
| 46 | +# Run all browser tests |
| 47 | +php artisan test --compact tests/v4/Browser/ |
| 48 | + |
| 49 | +# Run a specific browser test file |
| 50 | +php artisan test --compact tests/v4/Browser/LoginTest.php |
| 51 | + |
| 52 | +# Run a specific test by name |
| 53 | +php artisan test --compact --filter='can login with valid credentials' |
| 54 | +``` |
| 55 | + |
| 56 | +### Writing Browser Tests |
| 57 | + |
| 58 | +- Place new tests in `tests/v4/Browser/` — legacy Dusk tests in `tests/Browser/` should not be used as reference. |
| 59 | +- Use `RefreshDatabase` and seed required data (at minimum `InstanceSettings::create(['id' => 0])`) in `beforeEach`. |
| 60 | +- Key API: `visit()`, `fill(field, value)`, `click(text)`, `assertSee()`, `assertDontSee()`, `assertPathIs()`, `screenshot()`. |
| 61 | +- Always call `screenshot()` at the end of each test for debugging. |
| 62 | +- For authenticated tests, create a helper function that logs in via the UI: |
| 63 | + |
| 64 | +```php |
| 65 | +function loginAsRoot(): mixed |
| 66 | +{ |
| 67 | + return visit('/login') |
| 68 | + ->fill('email', 'test@example.com') |
| 69 | + ->fill('password', 'password') |
| 70 | + ->click('Login'); |
| 71 | +} |
| 72 | +``` |
| 73 | + |
| 74 | +- See `tests/v4/Browser/LoginTest.php`, `tests/v4/Browser/DashboardTest.php`, and `tests/v4/Browser/RegistrationTest.php` for conventions. |
| 75 | +- Chrome driver runs on `localhost:4444`, app on `localhost:8000` (configured in `tests/DuskTestCase.php`). |
| 76 | +- Legacy Dusk macros in `app/Providers/DuskServiceProvider.php` use the old `type()`/`press()` API — do not mix with Pest Browser Plugin's `fill()`/`click()` API. |
| 77 | + |
| 78 | +## Architecture |
| 79 | + |
| 80 | +### Backend Structure (app/) |
| 81 | +- **Actions/** — Domain actions organized by area (Application, Database, Docker, Proxy, Server, Service, Shared, Stripe, User, CoolifyTask, Fortify). Uses `lorisleiva/laravel-actions` with `AsAction` trait — actions can be called as objects, dispatched as jobs, or used as controllers. |
| 82 | +- **Livewire/** — All UI components (Livewire 3). Pages organized by domain: Server, Project, Settings, Security, Notifications, Terminal, Subscription, SharedVariables. This is the primary UI layer — no traditional Blade controllers. Components listen to private team channels for real-time status updates via Soketi. |
| 83 | +- **Jobs/** — Queue jobs for deployments (`ApplicationDeploymentJob`), backups, Docker cleanup, server management, proxy configuration. Uses Redis queue with Horizon for monitoring. |
| 84 | +- **Models/** — Eloquent models extending `BaseModel` which provides auto-CUID2 UUID generation. Key models: `Server`, `Application`, `Service`, `Project`, `Environment`, `Team`, plus standalone database models (`StandalonePostgresql`, `StandaloneMysql`, etc.). Common traits: `HasConfiguration`, `HasMetrics`, `HasSafeStringAttribute`, `ClearsGlobalSearchCache`. |
| 85 | +- **Services/** — Business logic services (ConfigurationGenerator, DockerImageParser, ContainerStatusAggregator, HetznerService, etc.). Use Services for complex orchestration; use Actions for single-purpose domain operations. |
| 86 | +- **Helpers/** — Global helpers loaded via `bootstrap/includeHelpers.php` from `bootstrap/helpers/` — organized into `shared.php`, `constants.php`, `versions.php`, `subscriptions.php`, `domains.php`, `docker.php`, `services.php`, `github.php`, `proxy.php`, `notifications.php`. |
| 87 | +- **Data/** — Spatie Laravel Data DTOs (e.g., `ServerMetadata`). |
| 88 | +- **Enums/** — PHP enums (TitleCase keys). Key enums: `ProcessStatus`, `Role` (MEMBER/ADMIN/OWNER with rank comparison), `BuildPackTypes`, `ProxyTypes`, `ContainerStatusTypes`. |
| 89 | +- **Rules/** — Custom validation rules (`ValidGitRepositoryUrl`, `ValidServerIp`, `ValidHostname`, `DockerImageFormat`, etc.). |
| 90 | + |
| 91 | +### API Layer |
| 92 | +- REST API at `/api/v1/` with OpenAPI 3.0 attributes (`use OpenApi\Attributes as OA`) for auto-generated docs |
| 93 | +- Authentication via Laravel Sanctum with custom `ApiAbility` middleware for token abilities (read, write, deploy) |
| 94 | +- `ApiSensitiveData` middleware masks sensitive fields (IDs, credentials) in responses |
| 95 | +- API controllers in `app/Http/Controllers/Api/` use inline `Validator` (not Form Request classes) |
| 96 | +- Response serialization via `serializeApiResponse()` helper |
| 97 | + |
| 98 | +### Authorization |
| 99 | +- Policy-based authorization with ~15 model-to-policy mappings in `AuthServiceProvider` |
| 100 | +- Custom gates: `createAnyResource`, `canAccessTerminal` |
| 101 | +- Role hierarchy: `Role::MEMBER` (1) < `Role::ADMIN` (2) < `Role::OWNER` (3) with `lt()`/`gt()` comparison methods |
| 102 | +- Multi-tenancy via Teams — team auto-initializes notification settings on creation |
| 103 | + |
| 104 | +### Event Broadcasting |
| 105 | +- Soketi WebSocket server for real-time updates (ports 6001-6002 in dev) |
| 106 | +- Status change events: `ApplicationStatusChanged`, `ServiceStatusChanged`, `DatabaseStatusChanged`, `ProxyStatusChanged` |
| 107 | +- Livewire components subscribe to private team channels via `getListeners()` |
| 108 | + |
| 109 | +### Key Domain Concepts |
| 110 | +- **Server** — A managed host connected via SSH. Has settings, proxy config, and destinations. |
| 111 | +- **Application** — A deployed app (from Git or Docker image) with environment variables, previews, deployment queue. |
| 112 | +- **Service** — A pre-configured service stack from templates (`templates/service-templates-latest.json`). |
| 113 | +- **Standalone Databases** — Individual database instances (Postgres, MySQL, MariaDB, MongoDB, Redis, Clickhouse, KeyDB, Dragonfly). |
| 114 | +- **Project/Environment** — Organizational hierarchy: Team → Project → Environment → Resources. |
| 115 | +- **Proxy** — Traefik reverse proxy managed per server. |
| 116 | + |
| 117 | +### Frontend |
| 118 | +- Livewire 3 components with Alpine.js for client-side interactivity |
| 119 | +- Blade templates in `resources/views/livewire/` |
| 120 | +- Tailwind CSS v4 with `@tailwindcss/forms` and `@tailwindcss/typography` |
| 121 | +- Vite for asset bundling |
| 122 | + |
| 123 | +### Laravel 10 Structure (NOT Laravel 11+ slim structure) |
| 124 | +- Middleware in `app/Http/Middleware/` — custom middleware includes `CheckForcePasswordReset`, `DecideWhatToDoWithUser`, `ApiAbility`, `ApiSensitiveData` |
| 125 | +- Kernels: `app/Http/Kernel.php`, `app/Console/Kernel.php` |
| 126 | +- Exception handler: `app/Exceptions/Handler.php` |
| 127 | +- Service providers in `app/Providers/` |
| 128 | + |
| 129 | +## Key Conventions |
| 130 | + |
| 131 | +- Use `php artisan make:*` commands with `--no-interaction` to create files |
| 132 | +- Use Eloquent relationships, avoid `DB::` facade — prefer `Model::query()` |
| 133 | +- PHP 8.5: constructor property promotion, explicit return types, type hints |
| 134 | +- Validation uses inline `Validator` facade in controllers/Livewire components and custom rules in `app/Rules/` — not Form Request classes |
| 135 | +- Run `vendor/bin/pint --dirty --format agent` before finalizing changes |
| 136 | +- Every change must have tests — write or update tests, then run them. For bug fixes, follow TDD: write a failing test first, then fix the bug (see Test Enforcement below) |
| 137 | +- Check sibling files for conventions before creating new files |
| 138 | + |
| 139 | +## Git Workflow |
| 140 | + |
| 141 | +- Main branch: `v4.x` |
| 142 | +- Development branch: `next` |
| 143 | +- PRs should target `v4.x` |
| 144 | + |
5 | 145 | <laravel-boost-guidelines> |
6 | 146 | === foundation rules === |
7 | 147 |
|
|
0 commit comments