|
| 1 | +--- |
| 2 | +name: forge |
| 3 | +description: Manage Laravel Forge servers, sites, deployments, environment variables, SSL, daemons, scheduled jobs, and nginx config via the Forge REST API. Use when the user wants to deploy, check deployment status, manage .env, or perform any Forge server/site operation. |
| 4 | +origin: custom |
| 5 | +--- |
| 6 | + |
| 7 | +# Laravel Forge |
| 8 | + |
| 9 | +Manage Laravel Forge infrastructure via the REST API (`curl`) or `forge-cli`. |
| 10 | + |
| 11 | +## When to Activate |
| 12 | + |
| 13 | +- User wants to deploy a site or check deployment status/logs |
| 14 | +- User wants to view/edit `.env` variables on a Forge-managed server |
| 15 | +- User wants to manage SSL certificates, daemons, cron jobs, nginx config |
| 16 | +- User mentions "forge" in the context of server management |
| 17 | +- User shares a `forge.laravel.com` URL |
| 18 | + |
| 19 | +## Configuration |
| 20 | + |
| 21 | +### API Token |
| 22 | + |
| 23 | +The skill requires a Forge API token. Check for it in this order: |
| 24 | + |
| 25 | +1. Environment variable: `FORGE_API_TOKEN` |
| 26 | +2. Ask the user to provide it (generated at Forge Dashboard > Account > API Tokens) |
| 27 | + |
| 28 | +Store the base URL and headers as variables for reuse: |
| 29 | + |
| 30 | +```bash |
| 31 | +FORGE_API="https://forge.laravel.com/api/v1" |
| 32 | +FORGE_TOKEN="${FORGE_API_TOKEN}" |
| 33 | +``` |
| 34 | + |
| 35 | +Every request MUST include: |
| 36 | +``` |
| 37 | +Authorization: Bearer $FORGE_TOKEN |
| 38 | +Accept: application/json |
| 39 | +Content-Type: application/json |
| 40 | +``` |
| 41 | + |
| 42 | +### Parsing Forge URLs |
| 43 | + |
| 44 | +When the user provides a Forge URL like: |
| 45 | +``` |
| 46 | +https://forge.laravel.com/{username}/{server-name}/{serverId}/deployments |
| 47 | +``` |
| 48 | + |
| 49 | +Extract the `serverId` from the URL path (the numeric segment). You still need the `siteId` — list sites on that server to find it. |
| 50 | + |
| 51 | +## API Reference |
| 52 | + |
| 53 | +### Servers |
| 54 | + |
| 55 | +```bash |
| 56 | +# List all servers |
| 57 | +curl -s "$FORGE_API/servers" -H "Authorization: Bearer $FORGE_TOKEN" -H "Accept: application/json" | jq '.servers[] | {id, name, ip_address, type, provider}' |
| 58 | + |
| 59 | +# Get server details |
| 60 | +curl -s "$FORGE_API/servers/$SERVER_ID" -H "Authorization: Bearer $FORGE_TOKEN" -H "Accept: application/json" | jq '.server' |
| 61 | + |
| 62 | +# Reboot server (DANGEROUS - confirm with user first) |
| 63 | +curl -s -X POST "$FORGE_API/servers/$SERVER_ID/reboot" -H "Authorization: Bearer $FORGE_TOKEN" -H "Accept: application/json" |
| 64 | +``` |
| 65 | + |
| 66 | +### Sites |
| 67 | + |
| 68 | +```bash |
| 69 | +# List sites on a server |
| 70 | +curl -s "$FORGE_API/servers/$SERVER_ID/sites" -H "Authorization: Bearer $FORGE_TOKEN" -H "Accept: application/json" | jq '.sites[] | {id, name, directory, repository, deployment_status}' |
| 71 | + |
| 72 | +# Get site details |
| 73 | +curl -s "$FORGE_API/servers/$SERVER_ID/sites/$SITE_ID" -H "Authorization: Bearer $FORGE_TOKEN" -H "Accept: application/json" | jq '.site' |
| 74 | +``` |
| 75 | + |
| 76 | +### Deployments |
| 77 | + |
| 78 | +```bash |
| 79 | +# Trigger deployment |
| 80 | +curl -s -X POST "$FORGE_API/servers/$SERVER_ID/sites/$SITE_ID/deployment/deploy" \ |
| 81 | + -H "Authorization: Bearer $FORGE_TOKEN" \ |
| 82 | + -H "Accept: application/json" |
| 83 | + |
| 84 | +# Get latest deployment log |
| 85 | +curl -s "$FORGE_API/servers/$SERVER_ID/sites/$SITE_ID/deployment/log" \ |
| 86 | + -H "Authorization: Bearer $FORGE_TOKEN" \ |
| 87 | + -H "Accept: application/json" |
| 88 | + |
| 89 | +# List deployment history |
| 90 | +curl -s "$FORGE_API/servers/$SERVER_ID/sites/$SITE_ID/deployments" \ |
| 91 | + -H "Authorization: Bearer $FORGE_TOKEN" \ |
| 92 | + -H "Accept: application/json" | jq '.deployments[] | {id, status, commit_hash, commit_author, started_at, ended_at}' |
| 93 | + |
| 94 | +# Get specific deployment output |
| 95 | +curl -s "$FORGE_API/servers/$SERVER_ID/sites/$SITE_ID/deployments/$DEPLOYMENT_ID/output" \ |
| 96 | + -H "Authorization: Bearer $FORGE_TOKEN" \ |
| 97 | + -H "Accept: application/json" |
| 98 | + |
| 99 | +# Get deployment script |
| 100 | +curl -s "$FORGE_API/servers/$SERVER_ID/sites/$SITE_ID/deployment/script" \ |
| 101 | + -H "Authorization: Bearer $FORGE_TOKEN" \ |
| 102 | + -H "Accept: application/json" |
| 103 | + |
| 104 | +# Update deployment script |
| 105 | +curl -s -X PUT "$FORGE_API/servers/$SERVER_ID/sites/$SITE_ID/deployment/script" \ |
| 106 | + -H "Authorization: Bearer $FORGE_TOKEN" \ |
| 107 | + -H "Accept: application/json" \ |
| 108 | + -H "Content-Type: application/json" \ |
| 109 | + -d '{"content": "cd /home/forge/site\ngit pull origin main\ncomposer install --no-dev\nphp artisan migrate --force\nphp artisan config:cache\nphp artisan route:cache\nphp artisan view:cache"}' |
| 110 | + |
| 111 | +# Enable quick deploy (auto-deploy on git push) |
| 112 | +curl -s -X POST "$FORGE_API/servers/$SERVER_ID/sites/$SITE_ID/deployment" \ |
| 113 | + -H "Authorization: Bearer $FORGE_TOKEN" \ |
| 114 | + -H "Accept: application/json" |
| 115 | + |
| 116 | +# Disable quick deploy |
| 117 | +curl -s -X DELETE "$FORGE_API/servers/$SERVER_ID/sites/$SITE_ID/deployment" \ |
| 118 | + -H "Authorization: Bearer $FORGE_TOKEN" \ |
| 119 | + -H "Accept: application/json" |
| 120 | +``` |
| 121 | + |
| 122 | +### Environment Variables (.env) |
| 123 | + |
| 124 | +```bash |
| 125 | +# Get .env content |
| 126 | +curl -s "$FORGE_API/servers/$SERVER_ID/sites/$SITE_ID/env" \ |
| 127 | + -H "Authorization: Bearer $FORGE_TOKEN" \ |
| 128 | + -H "Accept: application/json" |
| 129 | + |
| 130 | +# Update .env (replaces the ENTIRE file - always GET first, modify, then PUT) |
| 131 | +curl -s -X PUT "$FORGE_API/servers/$SERVER_ID/sites/$SITE_ID/env" \ |
| 132 | + -H "Authorization: Bearer $FORGE_TOKEN" \ |
| 133 | + -H "Accept: application/json" \ |
| 134 | + -H "Content-Type: application/json" \ |
| 135 | + -d '{"content": "APP_NAME=MyApp\nAPP_ENV=production\n..."}' |
| 136 | +``` |
| 137 | + |
| 138 | +**WARNING:** The .env PUT replaces the entire file. Always read the current .env first, modify the specific values, then write back the full content. |
| 139 | + |
| 140 | +### SSL Certificates |
| 141 | + |
| 142 | +```bash |
| 143 | +# List certificates |
| 144 | +curl -s "$FORGE_API/servers/$SERVER_ID/sites/$SITE_ID/certificates" \ |
| 145 | + -H "Authorization: Bearer $FORGE_TOKEN" \ |
| 146 | + -H "Accept: application/json" |
| 147 | + |
| 148 | +# Request Let's Encrypt certificate |
| 149 | +curl -s -X POST "$FORGE_API/servers/$SERVER_ID/sites/$SITE_ID/certificates/letsencrypt" \ |
| 150 | + -H "Authorization: Bearer $FORGE_TOKEN" \ |
| 151 | + -H "Accept: application/json" \ |
| 152 | + -H "Content-Type: application/json" \ |
| 153 | + -d '{"domains": ["example.com", "www.example.com"]}' |
| 154 | + |
| 155 | +# Activate certificate |
| 156 | +curl -s -X POST "$FORGE_API/servers/$SERVER_ID/sites/$SITE_ID/certificates/$CERT_ID/activate" \ |
| 157 | + -H "Authorization: Bearer $FORGE_TOKEN" \ |
| 158 | + -H "Accept: application/json" |
| 159 | + |
| 160 | +# Delete certificate |
| 161 | +curl -s -X DELETE "$FORGE_API/servers/$SERVER_ID/sites/$SITE_ID/certificates/$CERT_ID" \ |
| 162 | + -H "Authorization: Bearer $FORGE_TOKEN" \ |
| 163 | + -H "Accept: application/json" |
| 164 | +``` |
| 165 | + |
| 166 | +### Databases |
| 167 | + |
| 168 | +```bash |
| 169 | +# List databases |
| 170 | +curl -s "$FORGE_API/servers/$SERVER_ID/databases" \ |
| 171 | + -H "Authorization: Bearer $FORGE_TOKEN" \ |
| 172 | + -H "Accept: application/json" |
| 173 | + |
| 174 | +# Create database |
| 175 | +curl -s -X POST "$FORGE_API/servers/$SERVER_ID/databases" \ |
| 176 | + -H "Authorization: Bearer $FORGE_TOKEN" \ |
| 177 | + -H "Accept: application/json" \ |
| 178 | + -H "Content-Type: application/json" \ |
| 179 | + -d '{"name": "my_database", "user": "my_user", "password": "secret"}' |
| 180 | + |
| 181 | +# Delete database (DANGEROUS - confirm with user) |
| 182 | +curl -s -X DELETE "$FORGE_API/servers/$SERVER_ID/databases/$DB_ID" \ |
| 183 | + -H "Authorization: Bearer $FORGE_TOKEN" \ |
| 184 | + -H "Accept: application/json" |
| 185 | +``` |
| 186 | + |
| 187 | +### Daemons (Supervisor) |
| 188 | + |
| 189 | +```bash |
| 190 | +# List daemons |
| 191 | +curl -s "$FORGE_API/servers/$SERVER_ID/daemons" \ |
| 192 | + -H "Authorization: Bearer $FORGE_TOKEN" \ |
| 193 | + -H "Accept: application/json" |
| 194 | + |
| 195 | +# Create daemon |
| 196 | +curl -s -X POST "$FORGE_API/servers/$SERVER_ID/daemons" \ |
| 197 | + -H "Authorization: Bearer $FORGE_TOKEN" \ |
| 198 | + -H "Accept: application/json" \ |
| 199 | + -H "Content-Type: application/json" \ |
| 200 | + -d '{"command": "php /home/forge/site/artisan queue:work --sleep=3 --tries=3", "user": "forge", "directory": "/home/forge/site", "processes": 1, "startsecs": 1}' |
| 201 | + |
| 202 | +# Restart daemon |
| 203 | +curl -s -X POST "$FORGE_API/servers/$SERVER_ID/daemons/$DAEMON_ID/restart" \ |
| 204 | + -H "Authorization: Bearer $FORGE_TOKEN" \ |
| 205 | + -H "Accept: application/json" |
| 206 | + |
| 207 | +# Delete daemon |
| 208 | +curl -s -X DELETE "$FORGE_API/servers/$SERVER_ID/daemons/$DAEMON_ID" \ |
| 209 | + -H "Authorization: Bearer $FORGE_TOKEN" \ |
| 210 | + -H "Accept: application/json" |
| 211 | +``` |
| 212 | + |
| 213 | +### Scheduled Jobs (Cron) |
| 214 | + |
| 215 | +```bash |
| 216 | +# List jobs |
| 217 | +curl -s "$FORGE_API/servers/$SERVER_ID/jobs" \ |
| 218 | + -H "Authorization: Bearer $FORGE_TOKEN" \ |
| 219 | + -H "Accept: application/json" |
| 220 | + |
| 221 | +# Create job |
| 222 | +curl -s -X POST "$FORGE_API/servers/$SERVER_ID/jobs" \ |
| 223 | + -H "Authorization: Bearer $FORGE_TOKEN" \ |
| 224 | + -H "Accept: application/json" \ |
| 225 | + -H "Content-Type: application/json" \ |
| 226 | + -d '{"command": "php /home/forge/site/artisan schedule:run", "frequency": "minutely", "user": "forge"}' |
| 227 | + |
| 228 | +# Delete job |
| 229 | +curl -s -X DELETE "$FORGE_API/servers/$SERVER_ID/jobs/$JOB_ID" \ |
| 230 | + -H "Authorization: Bearer $FORGE_TOKEN" \ |
| 231 | + -H "Accept: application/json" |
| 232 | +``` |
| 233 | + |
| 234 | +Frequency values: `minutely`, `hourly`, `nightly`, `weekly`, `monthly`, `reboot`, `custom`. |
| 235 | + |
| 236 | +### Nginx Configuration |
| 237 | + |
| 238 | +```bash |
| 239 | +# Get nginx config |
| 240 | +curl -s "$FORGE_API/servers/$SERVER_ID/sites/$SITE_ID/nginx" \ |
| 241 | + -H "Authorization: Bearer $FORGE_TOKEN" \ |
| 242 | + -H "Accept: application/json" |
| 243 | + |
| 244 | +# Update nginx config (replaces entire config - GET first, modify, PUT back) |
| 245 | +curl -s -X PUT "$FORGE_API/servers/$SERVER_ID/sites/$SITE_ID/nginx" \ |
| 246 | + -H "Authorization: Bearer $FORGE_TOKEN" \ |
| 247 | + -H "Accept: application/json" \ |
| 248 | + -H "Content-Type: application/json" \ |
| 249 | + -d '{"content": "server { ... }"}' |
| 250 | +``` |
| 251 | + |
| 252 | +## Workflow Patterns |
| 253 | + |
| 254 | +### Deploy and Watch |
| 255 | + |
| 256 | +```bash |
| 257 | +# 1. Trigger deployment |
| 258 | +curl -s -X POST "$FORGE_API/servers/$SERVER_ID/sites/$SITE_ID/deployment/deploy" \ |
| 259 | + -H "Authorization: Bearer $FORGE_TOKEN" -H "Accept: application/json" |
| 260 | + |
| 261 | +# 2. Check site status for deployment_status |
| 262 | +curl -s "$FORGE_API/servers/$SERVER_ID/sites/$SITE_ID" \ |
| 263 | + -H "Authorization: Bearer $FORGE_TOKEN" -H "Accept: application/json" | jq '.site.deployment_status' |
| 264 | +# null = idle, "deploying" = in progress |
| 265 | + |
| 266 | +# 3. Once done, check deployment log |
| 267 | +curl -s "$FORGE_API/servers/$SERVER_ID/sites/$SITE_ID/deployment/log" \ |
| 268 | + -H "Authorization: Bearer $FORGE_TOKEN" -H "Accept: application/json" |
| 269 | +``` |
| 270 | + |
| 271 | +### Safe .env Update |
| 272 | + |
| 273 | +```bash |
| 274 | +# 1. Read current .env |
| 275 | +CURRENT_ENV=$(curl -s "$FORGE_API/servers/$SERVER_ID/sites/$SITE_ID/env" \ |
| 276 | + -H "Authorization: Bearer $FORGE_TOKEN" -H "Accept: application/json") |
| 277 | + |
| 278 | +# 2. Show user current values, confirm changes |
| 279 | + |
| 280 | +# 3. Write updated .env (full content) |
| 281 | +curl -s -X PUT "$FORGE_API/servers/$SERVER_ID/sites/$SITE_ID/env" \ |
| 282 | + -H "Authorization: Bearer $FORGE_TOKEN" -H "Accept: application/json" \ |
| 283 | + -H "Content-Type: application/json" \ |
| 284 | + -d "{\"content\": \"$UPDATED_ENV\"}" |
| 285 | +``` |
| 286 | + |
| 287 | +## Safety Rules |
| 288 | + |
| 289 | +- **Server reboot**: ALWAYS confirm with user before executing |
| 290 | +- **Database delete**: ALWAYS confirm with user before executing |
| 291 | +- **Site delete**: ALWAYS confirm with user before executing |
| 292 | +- **.env update**: ALWAYS read current .env first, show diff to user, then write |
| 293 | +- **Nginx update**: ALWAYS read current config first, show diff to user, then write |
| 294 | +- **Deployment script update**: ALWAYS show current script and proposed changes before writing |
| 295 | +- **Deploy**: Safe to execute when user explicitly requests it |
| 296 | + |
| 297 | +## Rate Limiting |
| 298 | + |
| 299 | +Forge allows ~30 requests/minute per token. Headers: `X-RateLimit-Limit`, `X-RateLimit-Remaining`. If you get `HTTP 429`, wait before retrying. |
| 300 | + |
| 301 | +## Forge CLI Alternative |
| 302 | + |
| 303 | +If `forge-cli` is installed (`composer global require laravel/forge-cli`): |
| 304 | + |
| 305 | +```bash |
| 306 | +forge login # Auth with API token |
| 307 | +forge server:list # List servers |
| 308 | +forge site:list {server} # List sites |
| 309 | +forge deploy {server} {site} # Deploy |
| 310 | +forge deploy:log {server} {site} # Deployment log |
| 311 | +forge env:get {server} {site} # Get .env |
| 312 | +forge env:set {server} {site} # Set .env |
| 313 | +forge nginx:get {server} {site} # Get nginx config |
| 314 | +forge daemon:list {server} # List daemons |
| 315 | +forge job:list {server} # List cron jobs |
| 316 | +forge certificate:list {server} {site} # List SSL certs |
| 317 | +``` |
0 commit comments