opencastle 0.33.9 → 0.34.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/init.d.ts.map +1 -1
- package/dist/cli/init.js +39 -17
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/stack-config.d.ts.map +1 -1
- package/dist/cli/stack-config.js +5 -0
- package/dist/cli/stack-config.js.map +1 -1
- package/dist/cli/types.d.ts +1 -1
- package/dist/cli/types.d.ts.map +1 -1
- package/dist/orchestrator/plugins/cloudflare/config.d.ts +3 -0
- package/dist/orchestrator/plugins/cloudflare/config.d.ts.map +1 -0
- package/dist/orchestrator/plugins/cloudflare/config.js +23 -0
- package/dist/orchestrator/plugins/cloudflare/config.js.map +1 -0
- package/dist/orchestrator/plugins/coolify/config.d.ts +3 -0
- package/dist/orchestrator/plugins/coolify/config.d.ts.map +1 -0
- package/dist/orchestrator/plugins/coolify/config.js +28 -0
- package/dist/orchestrator/plugins/coolify/config.js.map +1 -0
- package/dist/orchestrator/plugins/drizzle/config.d.ts +3 -0
- package/dist/orchestrator/plugins/drizzle/config.d.ts.map +1 -0
- package/dist/orchestrator/plugins/drizzle/config.js +15 -0
- package/dist/orchestrator/plugins/drizzle/config.js.map +1 -0
- package/dist/orchestrator/plugins/expo/config.d.ts +3 -0
- package/dist/orchestrator/plugins/expo/config.d.ts.map +1 -0
- package/dist/orchestrator/plugins/expo/config.js +23 -0
- package/dist/orchestrator/plugins/expo/config.js.map +1 -0
- package/dist/orchestrator/plugins/index.d.ts.map +1 -1
- package/dist/orchestrator/plugins/index.js +12 -0
- package/dist/orchestrator/plugins/index.js.map +1 -1
- package/dist/orchestrator/plugins/sentry/config.d.ts +3 -0
- package/dist/orchestrator/plugins/sentry/config.d.ts.map +1 -0
- package/dist/orchestrator/plugins/sentry/config.js +28 -0
- package/dist/orchestrator/plugins/sentry/config.js.map +1 -0
- package/dist/orchestrator/plugins/stripe/config.d.ts +3 -0
- package/dist/orchestrator/plugins/stripe/config.d.ts.map +1 -0
- package/dist/orchestrator/plugins/stripe/config.js +42 -0
- package/dist/orchestrator/plugins/stripe/config.js.map +1 -0
- package/dist/orchestrator/plugins/types.d.ts +1 -1
- package/dist/orchestrator/plugins/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/cli/init.ts +43 -22
- package/src/cli/stack-config.ts +5 -0
- package/src/cli/types.ts +1 -1
- package/src/dashboard/dist/data/convoys/demo-api-v2.json +3 -3
- package/src/dashboard/dist/data/convoys/demo-auth-revamp.json +4 -4
- package/src/dashboard/dist/data/convoys/demo-dashboard-ui.json +12 -12
- package/src/dashboard/dist/data/convoys/demo-data-pipeline.json +3 -3
- package/src/dashboard/dist/data/convoys/demo-deploy-ci.json +1 -1
- package/src/dashboard/dist/data/convoys/demo-docs-update.json +3 -3
- package/src/dashboard/dist/data/convoys/demo-perf-opt.json +4 -4
- package/src/dashboard/node_modules/.vite/deps/_metadata.json +6 -6
- package/src/dashboard/public/data/convoys/demo-api-v2.json +3 -3
- package/src/dashboard/public/data/convoys/demo-auth-revamp.json +4 -4
- package/src/dashboard/public/data/convoys/demo-dashboard-ui.json +12 -12
- package/src/dashboard/public/data/convoys/demo-data-pipeline.json +3 -3
- package/src/dashboard/public/data/convoys/demo-deploy-ci.json +1 -1
- package/src/dashboard/public/data/convoys/demo-docs-update.json +3 -3
- package/src/dashboard/public/data/convoys/demo-perf-opt.json +4 -4
- package/src/orchestrator/customizations/agents/skill-matrix.json +24 -4
- package/src/orchestrator/customizations/agents/skill-matrix.md +5 -0
- package/src/orchestrator/plugins/cloudflare/SKILL.md +111 -0
- package/src/orchestrator/plugins/cloudflare/config.ts +24 -0
- package/src/orchestrator/plugins/cloudflare/references/deployment.md +147 -0
- package/src/orchestrator/plugins/cloudflare/references/storage.md +118 -0
- package/src/orchestrator/plugins/cloudflare/references/workers.md +135 -0
- package/src/orchestrator/plugins/convex/SKILL.md +62 -20
- package/src/orchestrator/plugins/convex/references/auth-auth0.md +116 -0
- package/src/orchestrator/plugins/convex/references/auth-clerk.md +113 -0
- package/src/orchestrator/plugins/convex/references/auth-convex-auth.md +143 -0
- package/src/orchestrator/plugins/convex/references/auth-setup.md +87 -0
- package/src/orchestrator/plugins/convex/references/auth-workos.md +114 -0
- package/src/orchestrator/plugins/convex/references/components-advanced.md +134 -0
- package/src/orchestrator/plugins/convex/references/components.md +171 -0
- package/src/orchestrator/plugins/convex/references/function-budget.md +232 -0
- package/src/orchestrator/plugins/convex/references/hot-path-rules.md +371 -0
- package/src/orchestrator/plugins/convex/references/migrations-component.md +170 -0
- package/src/orchestrator/plugins/convex/references/migrations.md +259 -0
- package/src/orchestrator/plugins/convex/references/occ-conflicts.md +126 -0
- package/src/orchestrator/plugins/convex/references/performance-audit.md +80 -0
- package/src/orchestrator/plugins/convex/references/quickstart.md +176 -0
- package/src/orchestrator/plugins/convex/references/subscription-cost.md +252 -0
- package/src/orchestrator/plugins/coolify/SKILL.md +134 -0
- package/src/orchestrator/plugins/coolify/config.ts +29 -0
- package/src/orchestrator/plugins/coolify/references/applications.md +65 -0
- package/src/orchestrator/plugins/coolify/references/ci-cd-webhooks.md +73 -0
- package/src/orchestrator/plugins/coolify/references/databases-services.md +57 -0
- package/src/orchestrator/plugins/coolify/references/docker-compose.md +121 -0
- package/src/orchestrator/plugins/coolify/references/infrastructure.md +77 -0
- package/src/orchestrator/plugins/drizzle/SKILL.md +123 -0
- package/src/orchestrator/plugins/drizzle/config.ts +16 -0
- package/src/orchestrator/plugins/drizzle/references/migrations.md +112 -0
- package/src/orchestrator/plugins/drizzle/references/query-patterns.md +127 -0
- package/src/orchestrator/plugins/drizzle/references/schema-patterns.md +105 -0
- package/src/orchestrator/plugins/expo/SKILL.md +114 -0
- package/src/orchestrator/plugins/expo/config.ts +24 -0
- package/src/orchestrator/plugins/expo/references/eas-build.md +73 -0
- package/src/orchestrator/plugins/expo/references/native-modules.md +71 -0
- package/src/orchestrator/plugins/expo/references/routing.md +83 -0
- package/src/orchestrator/plugins/index.ts +12 -0
- package/src/orchestrator/plugins/linear/SKILL.md +21 -3
- package/src/orchestrator/plugins/sentry/SKILL.md +94 -0
- package/src/orchestrator/plugins/sentry/config.ts +29 -0
- package/src/orchestrator/plugins/sentry/references/error-patterns.md +112 -0
- package/src/orchestrator/plugins/sentry/references/performance.md +66 -0
- package/src/orchestrator/plugins/sentry/references/sdk-setup.md +108 -0
- package/src/orchestrator/plugins/stripe/SKILL.md +138 -0
- package/src/orchestrator/plugins/stripe/config.ts +43 -0
- package/src/orchestrator/plugins/stripe/references/api-patterns.md +57 -0
- package/src/orchestrator/plugins/stripe/references/projects-setup.md +30 -0
- package/src/orchestrator/plugins/stripe/references/upgrade-guide.md +105 -0
- package/src/orchestrator/plugins/types.ts +1 -1
- package/src/orchestrator/skills/backbone-scaffolding/EXAMPLES.md +1 -1
- package/src/orchestrator/skills/backbone-scaffolding/SKILL.md +32 -16
- package/src/orchestrator/plugins/convex/REFERENCE.md +0 -9
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
# Subscription Cost
|
|
2
|
+
|
|
3
|
+
Use these rules when the problem is too many reactive subscriptions, queries invalidating too frequently, or React components re-rendering excessively due to Convex state changes.
|
|
4
|
+
|
|
5
|
+
## Core Principle
|
|
6
|
+
|
|
7
|
+
Every `useQuery` and `usePaginatedQuery` call creates a live subscription. The server tracks the query's read set and re-executes the query whenever any document in that read set changes. Subscription cost scales with:
|
|
8
|
+
|
|
9
|
+
`subscriptions x invalidation_frequency x query_cost`
|
|
10
|
+
|
|
11
|
+
Subscriptions are not inherently bad. Convex reactivity is often the right default. The goal is to reduce unnecessary invalidation work, not to eliminate subscriptions on principle.
|
|
12
|
+
|
|
13
|
+
## Symptoms
|
|
14
|
+
|
|
15
|
+
- Dashboard shows high active subscription count
|
|
16
|
+
- UI feels sluggish or laggy despite fast individual queries
|
|
17
|
+
- React profiling shows frequent re-renders from Convex state
|
|
18
|
+
- Pages with many components each running their own `useQuery`
|
|
19
|
+
- Paginated lists where every loaded page stays subscribed
|
|
20
|
+
|
|
21
|
+
## Common Causes
|
|
22
|
+
|
|
23
|
+
### Reactive queries on low-freshness flows
|
|
24
|
+
|
|
25
|
+
Some user flows are read-heavy and do not need live updates every time the underlying data changes. In those cases, ongoing subscriptions may cost more than they are worth.
|
|
26
|
+
|
|
27
|
+
### Overly broad queries
|
|
28
|
+
|
|
29
|
+
A query that returns a large result set invalidates whenever any document in that set changes. The broader the query, the more frequent the invalidation.
|
|
30
|
+
|
|
31
|
+
### Too many subscriptions per page
|
|
32
|
+
|
|
33
|
+
A page with 20 list items, each running its own `useQuery` to fetch related data, creates 20+ subscriptions per visitor.
|
|
34
|
+
|
|
35
|
+
### Paginated queries keeping all pages live
|
|
36
|
+
|
|
37
|
+
`usePaginatedQuery` with `loadMore` keeps every loaded page subscribed. On a page where a user has scrolled through 10 pages, all 10 stay reactive.
|
|
38
|
+
|
|
39
|
+
### Frequently-updated fields on widely-read documents
|
|
40
|
+
|
|
41
|
+
A document that many queries touch gets a frequently-updated field (like `lastSeen`, `lastActiveAt`, or a counter). Every write to that field invalidates every subscription that reads the document, even if those subscriptions never use the field. This is different from OCC conflicts (see `occ-conflicts.md`), which are write-vs-write contention. This is write-vs-subscription: the write succeeds fine, but it forces hundreds of queries to re-run for no reason.
|
|
42
|
+
|
|
43
|
+
## Fix Order
|
|
44
|
+
|
|
45
|
+
### 1. Use point-in-time reads when live updates are not valuable
|
|
46
|
+
|
|
47
|
+
Keep `useQuery` and `usePaginatedQuery` by default when the product benefits from fresh live data.
|
|
48
|
+
|
|
49
|
+
Consider a point-in-time read instead when all of these are true:
|
|
50
|
+
|
|
51
|
+
- the flow is high-read
|
|
52
|
+
- the underlying data changes less often than users need to see
|
|
53
|
+
- explicit refresh, periodic refresh, or a fresh read on navigation is acceptable
|
|
54
|
+
|
|
55
|
+
Possible implementations depend on environment:
|
|
56
|
+
|
|
57
|
+
- a server-rendered fetch
|
|
58
|
+
- a framework helper like `fetchQuery`
|
|
59
|
+
- a point-in-time client read such as `ConvexHttpClient.query()`
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
// Reactive by default when fresh live data matters
|
|
63
|
+
function TeamPresence() {
|
|
64
|
+
const presence = useQuery(api.teams.livePresence, { teamId });
|
|
65
|
+
return <PresenceList users={presence} />;
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
// Point-in-time read when explicit refresh is acceptable
|
|
71
|
+
import { ConvexHttpClient } from "convex/browser";
|
|
72
|
+
|
|
73
|
+
const client = new ConvexHttpClient(import.meta.env.VITE_CONVEX_URL);
|
|
74
|
+
|
|
75
|
+
function SnapshotView() {
|
|
76
|
+
const [items, setItems] = useState<Item[]>([]);
|
|
77
|
+
|
|
78
|
+
useEffect(() => {
|
|
79
|
+
client.query(api.items.snapshot).then(setItems);
|
|
80
|
+
}, []);
|
|
81
|
+
|
|
82
|
+
return <ItemGrid items={items} />;
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Good candidates for point-in-time reads:
|
|
87
|
+
|
|
88
|
+
- aggregate snapshots
|
|
89
|
+
- reports
|
|
90
|
+
- low-churn listings
|
|
91
|
+
- flows where explicit refresh is already acceptable
|
|
92
|
+
|
|
93
|
+
Keep reactive for:
|
|
94
|
+
|
|
95
|
+
- collaborative editing
|
|
96
|
+
- live dashboards
|
|
97
|
+
- presence-heavy views
|
|
98
|
+
- any surface where users expect fresh changes to appear automatically
|
|
99
|
+
|
|
100
|
+
### 2. Batch related data into fewer queries
|
|
101
|
+
|
|
102
|
+
Instead of N components each fetching their own related data, fetch it in a single query.
|
|
103
|
+
|
|
104
|
+
```ts
|
|
105
|
+
// Bad: each card fetches its own author
|
|
106
|
+
function ProjectCard({ project }: { project: Project }) {
|
|
107
|
+
const author = useQuery(api.users.get, { id: project.authorId });
|
|
108
|
+
return <Card title={project.name} author={author?.name} />;
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
```ts
|
|
113
|
+
// Good: parent query returns projects with author names included
|
|
114
|
+
function ProjectList() {
|
|
115
|
+
const projects = useQuery(api.projects.listWithAuthors);
|
|
116
|
+
return projects?.map((p) => (
|
|
117
|
+
<Card key={p._id} title={p.name} author={p.authorName} />
|
|
118
|
+
));
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
This can use denormalized fields or server-side joins in the query handler. Either way, it is one subscription instead of N.
|
|
123
|
+
|
|
124
|
+
This is not automatically better. If the combined query becomes much broader and invalidates much more often, several narrower subscriptions may be the better tradeoff. Optimize for total invalidation cost, not raw subscription count.
|
|
125
|
+
|
|
126
|
+
### 3. Use skip to avoid unnecessary subscriptions
|
|
127
|
+
|
|
128
|
+
The `"skip"` value prevents a subscription from being created when the arguments are not ready.
|
|
129
|
+
|
|
130
|
+
```ts
|
|
131
|
+
// Bad: subscribes with undefined args, wastes a subscription slot
|
|
132
|
+
const profile = useQuery(api.users.getProfile, { userId: selectedId! });
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
```ts
|
|
136
|
+
// Good: skip when there is nothing to fetch
|
|
137
|
+
const profile = useQuery(
|
|
138
|
+
api.users.getProfile,
|
|
139
|
+
selectedId ? { userId: selectedId } : "skip",
|
|
140
|
+
);
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### 4. Isolate frequently-updated fields into separate documents
|
|
144
|
+
|
|
145
|
+
If a document is widely read but has a field that changes often, move that field to a separate document. Queries that do not need the field will no longer be invalidated by its writes.
|
|
146
|
+
|
|
147
|
+
```ts
|
|
148
|
+
// Bad: lastSeen lives on the user doc, every heartbeat invalidates
|
|
149
|
+
// every query that reads this user
|
|
150
|
+
const users = defineTable({
|
|
151
|
+
name: v.string(),
|
|
152
|
+
email: v.string(),
|
|
153
|
+
lastSeen: v.number(),
|
|
154
|
+
});
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
```ts
|
|
158
|
+
// Good: lastSeen lives in a separate heartbeat doc
|
|
159
|
+
const users = defineTable({
|
|
160
|
+
name: v.string(),
|
|
161
|
+
email: v.string(),
|
|
162
|
+
heartbeatId: v.id("heartbeats"),
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
const heartbeats = defineTable({
|
|
166
|
+
lastSeen: v.number(),
|
|
167
|
+
});
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Queries that only need `name` and `email` no longer re-run on every heartbeat. Queries that actually need online status fetch the heartbeat document explicitly.
|
|
171
|
+
|
|
172
|
+
For an even further optimization, if you only need a coarse online/offline boolean rather than the exact `lastSeen` timestamp, add a separate presence document with an `isOnline` flag. Update it immediately when a user comes online, and use a cron to batch-mark users offline when their heartbeat goes stale. This way the presence query only invalidates when online status actually changes, not on every heartbeat.
|
|
173
|
+
|
|
174
|
+
### 5. Use the aggregate component for counts and sums
|
|
175
|
+
|
|
176
|
+
Reactive global counts (`SELECT COUNT(*)` equivalent) invalidate on every insert or delete to the table. The [`@convex-dev/aggregate`](https://www.npmjs.com/package/@convex-dev/aggregate) component maintains denormalized COUNT, SUM, and MAX values efficiently so you do not need a reactive query scanning the full table.
|
|
177
|
+
|
|
178
|
+
Use it for leaderboards, totals, "X items" badges, or any stat that would otherwise require scanning many rows reactively.
|
|
179
|
+
|
|
180
|
+
If the aggregate component is not appropriate, prefer point-in-time reads for global stats, or precomputed summary rows updated by a cron or trigger, over reactive queries that scan large tables.
|
|
181
|
+
|
|
182
|
+
### 6. Narrow query read sets
|
|
183
|
+
|
|
184
|
+
Queries that return less data and touch fewer documents invalidate less often.
|
|
185
|
+
|
|
186
|
+
```ts
|
|
187
|
+
// Bad: returns all fields, invalidates on any field change
|
|
188
|
+
export const list = query({
|
|
189
|
+
handler: async (ctx) => {
|
|
190
|
+
return await ctx.db.query("projects").collect();
|
|
191
|
+
},
|
|
192
|
+
});
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
```ts
|
|
196
|
+
// Good: use a digest table with only the fields the list needs
|
|
197
|
+
export const listDigests = query({
|
|
198
|
+
handler: async (ctx) => {
|
|
199
|
+
return await ctx.db.query("projectDigests").collect();
|
|
200
|
+
},
|
|
201
|
+
});
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
Writes to fields not in the digest table do not invalidate the digest query.
|
|
205
|
+
|
|
206
|
+
### 7. Remove `Date.now()` from queries
|
|
207
|
+
|
|
208
|
+
Using `Date.now()` inside a query defeats Convex's query cache. The cache is invalidated frequently to avoid showing stale time-dependent results, which increases database work even when the underlying data has not changed.
|
|
209
|
+
|
|
210
|
+
```ts
|
|
211
|
+
// Bad: Date.now() defeats query caching and causes frequent re-evaluation
|
|
212
|
+
const releasedPosts = await ctx.db
|
|
213
|
+
.query("posts")
|
|
214
|
+
.withIndex("by_released_at", (q) => q.lte("releasedAt", Date.now()))
|
|
215
|
+
.take(100);
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
```ts
|
|
219
|
+
// Good: use a boolean field updated by a scheduled function
|
|
220
|
+
const releasedPosts = await ctx.db
|
|
221
|
+
.query("posts")
|
|
222
|
+
.withIndex("by_is_released", (q) => q.eq("isReleased", true))
|
|
223
|
+
.take(100);
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
If the query must compare against a time value, pass it as an explicit argument from the client and round it to a coarse interval (e.g. the most recent minute) so requests within that window share the same cache entry.
|
|
227
|
+
|
|
228
|
+
### 8. Consider pagination strategy
|
|
229
|
+
|
|
230
|
+
For long lists where users scroll through many pages:
|
|
231
|
+
|
|
232
|
+
- If the data does not need live updates, use point-in-time fetching with manual "load more"
|
|
233
|
+
- If it does need live updates, accept the subscription cost but limit the number of loaded pages
|
|
234
|
+
- Consider whether older pages can be unloaded as the user scrolls forward
|
|
235
|
+
|
|
236
|
+
### 9. Separate backend cost from UI churn
|
|
237
|
+
|
|
238
|
+
If the main problem is loading flash or UI churn when query arguments change, stabilizing the reactive UI behavior may be better than replacing reactivity altogether.
|
|
239
|
+
|
|
240
|
+
Treat this as a UX problem first when:
|
|
241
|
+
|
|
242
|
+
- the underlying query is already reasonably cheap
|
|
243
|
+
- the complaint is flicker, loading flashes, or re-render churn
|
|
244
|
+
- live updates are still desirable once fresh data arrives
|
|
245
|
+
|
|
246
|
+
## Verification
|
|
247
|
+
|
|
248
|
+
1. Subscription count in dashboard is lower for the affected pages
|
|
249
|
+
2. UI responsiveness has improved
|
|
250
|
+
3. React profiling shows fewer unnecessary re-renders
|
|
251
|
+
4. Surfaces that do not need live updates are not paying for persistent subscriptions unnecessarily
|
|
252
|
+
5. Sibling pages with similar patterns were updated consistently
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: coolify-deployment
|
|
3
|
+
description: "Deploys applications, databases, and services on self-hosted Coolify instances, manages environments and env vars, runs infrastructure diagnostics, and performs batch operations. Use when deploying apps to Coolify, managing Coolify servers, debugging deployment issues, setting up databases, or managing Coolify infrastructure."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Coolify Deployment
|
|
7
|
+
|
|
8
|
+
## Topic Routing
|
|
9
|
+
|
|
10
|
+
Read the matching reference before working on any of these topics:
|
|
11
|
+
|
|
12
|
+
| Topic | Reference |
|
|
13
|
+
|-------|-----------|
|
|
14
|
+
| Application deployment | `references/applications.md` |
|
|
15
|
+
| Database & service management | `references/databases-services.md` |
|
|
16
|
+
| Infrastructure & diagnostics | `references/infrastructure.md` |
|
|
17
|
+
| Docker Compose templates | `references/docker-compose.md` |
|
|
18
|
+
| CI/CD & Docker image deploys | `references/ci-cd-webhooks.md` |
|
|
19
|
+
|
|
20
|
+
## Critical Rules
|
|
21
|
+
|
|
22
|
+
**Project Structure**
|
|
23
|
+
- Coolify organizes resources as: Server → Project → Environment → Resources (apps, databases, services)
|
|
24
|
+
- Always start with `get_infrastructure_overview` to understand the current state before making changes
|
|
25
|
+
- Use `projects` tool with action params (`list`, `get`, `create`) — one tool, multiple operations
|
|
26
|
+
|
|
27
|
+
**Application Deployment**
|
|
28
|
+
- Deploy from public repos, private GitHub repos (via GitHub App), SSH keys, or Docker images
|
|
29
|
+
- Configure health checks (path, interval, retries) during `application` create to avoid unhealthy deployments
|
|
30
|
+
- Use `deploy` with `force_rebuild: true` only when cache issues are suspected — normal deploys are faster
|
|
31
|
+
- Check deployment status with `deployment` action `list_for_app` after triggering a deploy
|
|
32
|
+
|
|
33
|
+
**Diagnostics**
|
|
34
|
+
- `diagnose_app` accepts name or domain (e.g., `"my-app"` or `"example.com"`) — not just UUIDs
|
|
35
|
+
- `diagnose_server` accepts name or IP address — use for connectivity and resource issues
|
|
36
|
+
- `find_issues` scans all infrastructure — use as a health check before and after changes
|
|
37
|
+
|
|
38
|
+
**Environment Variables**
|
|
39
|
+
- Use `env_vars` tool with `resource: "application"` or `resource: "service"` — supports `list`, `create`, `update`, `delete`
|
|
40
|
+
- `bulk_env_update` updates a variable across multiple apps at once — use for shared config like API URLs
|
|
41
|
+
- Env vars require app restart to take effect — use `control` with `action: "restart"` after updates
|
|
42
|
+
|
|
43
|
+
**Database Management**
|
|
44
|
+
- Supports 8 database types: PostgreSQL, MySQL, MariaDB, MongoDB, Redis, KeyDB, ClickHouse, Dragonfly
|
|
45
|
+
- Configure backup schedules via `database_backups` with `action: "create"` — set frequency and retention
|
|
46
|
+
- Backups can target S3-compatible storage — configure via backup schedule creation
|
|
47
|
+
|
|
48
|
+
**Security**
|
|
49
|
+
- Store `COOLIFY_ACCESS_TOKEN` in `.env` — never commit to source control
|
|
50
|
+
- Private keys for SSH deployments: manage via `private_keys` tool, never hardcode
|
|
51
|
+
- Use `validate_server` to verify SSH connectivity before deploying
|
|
52
|
+
|
|
53
|
+
**Docker Compose**
|
|
54
|
+
- Coolify supports two compose modes: Raw (paste YAML, no `build:`) and Repository (git URL, full features)
|
|
55
|
+
- Use magic variables (`SERVICE_PASSWORD_*`, `SERVICE_URL_*`) for auto-generated credentials and proxy URLs — never hardcode
|
|
56
|
+
- Remove `ports:` for proxied services — Traefik handles routing via `SERVICE_URL_NAME_PORT`
|
|
57
|
+
|
|
58
|
+
**CI/CD Integration**
|
|
59
|
+
- Trigger deploys via webhook with tag or UUID — use for automated pipelines
|
|
60
|
+
- Docker image deployments (`create_dockerimage`) skip the build step — use for pre-built images from registries
|
|
61
|
+
|
|
62
|
+
## Workflow: Deploy Application
|
|
63
|
+
|
|
64
|
+
1. **Survey** — `get_infrastructure_overview` → identify target server and project
|
|
65
|
+
2. **Create project** (if needed) — `projects` action `create` with name and description
|
|
66
|
+
3. **Create environment** — `environments` action `create` in the target project
|
|
67
|
+
4. **Deploy** — `application` action `create_public` (or `create_github` / `create_dockerimage`) with health check config
|
|
68
|
+
5. **Verify** — `deployment` action `list_for_app` → wait for `finished` status; if `failed` → `application_logs` → fix → redeploy
|
|
69
|
+
6. **Set env vars** — `env_vars` resource `application` action `create` → `control` action `restart`
|
|
70
|
+
7. **Validate** — `diagnose_app` by name → confirm status `running` and health check passing
|
|
71
|
+
|
|
72
|
+
## Workflow: Diagnose & Fix
|
|
73
|
+
|
|
74
|
+
1. **Scan** — `find_issues` → list all unhealthy resources
|
|
75
|
+
2. **Investigate** — `diagnose_app` or `diagnose_server` with name/domain/IP
|
|
76
|
+
3. **Check logs** — `application_logs` for the affected app
|
|
77
|
+
4. **Fix** — update config, env vars, or redeploy as needed
|
|
78
|
+
5. **Restart** — `control` resource `application` action `restart`
|
|
79
|
+
6. **Verify** — `diagnose_app` again → confirm status `running`
|
|
80
|
+
|
|
81
|
+
## Deployment Failure Recovery
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
Deploy failed → check status
|
|
85
|
+
├── Health check failing → verify health check path returns 200 → fix app → redeploy
|
|
86
|
+
├── Build error → application_logs → fix Dockerfile/build config → deploy with force_rebuild
|
|
87
|
+
├── Port conflict → check server_resources → update port mapping → redeploy
|
|
88
|
+
├── SSH key invalid → validate_server → private_keys action update → retry
|
|
89
|
+
└── Out of resources → server diagnostics → scale server or stop unused apps
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Deploy Public Repo — Full Example
|
|
93
|
+
|
|
94
|
+
```
|
|
95
|
+
# 1. Create app from public repo
|
|
96
|
+
application(
|
|
97
|
+
action="create_public",
|
|
98
|
+
project_uuid="proj-abc",
|
|
99
|
+
environment_name="production",
|
|
100
|
+
server_uuid="srv-xyz",
|
|
101
|
+
repository_url="https://github.com/org/my-app",
|
|
102
|
+
branch="main",
|
|
103
|
+
build_pack="dockerfile",
|
|
104
|
+
health_check_path="/health",
|
|
105
|
+
health_check_interval=30,
|
|
106
|
+
health_check_retries=3
|
|
107
|
+
)
|
|
108
|
+
# → returns { uuid: "app-123", status: "deploying" }
|
|
109
|
+
|
|
110
|
+
# 2. Set env vars
|
|
111
|
+
env_vars(resource="application", action="create", uuid="app-123", key="DATABASE_URL", value="postgres://...", is_preview=false)
|
|
112
|
+
|
|
113
|
+
# 3. Restart to pick up env vars
|
|
114
|
+
control(resource="application", action="restart", uuid="app-123")
|
|
115
|
+
|
|
116
|
+
# 4. Verify
|
|
117
|
+
diagnose_app(identifier="my-app")
|
|
118
|
+
# → status: "running", health_check: "healthy"
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## MCP Config (VS Code)
|
|
122
|
+
|
|
123
|
+
```json
|
|
124
|
+
{
|
|
125
|
+
"servers": {
|
|
126
|
+
"Coolify": {
|
|
127
|
+
"type": "stdio",
|
|
128
|
+
"command": "npx",
|
|
129
|
+
"args": ["-y", "@masonator/coolify-mcp"],
|
|
130
|
+
"envFile": "${workspaceFolder}/.env"
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
```
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { PluginConfig } from '../types.js';
|
|
2
|
+
|
|
3
|
+
export const config: PluginConfig = {
|
|
4
|
+
id: 'coolify',
|
|
5
|
+
name: 'Coolify',
|
|
6
|
+
category: 'tech',
|
|
7
|
+
subCategory: 'deployment',
|
|
8
|
+
label: 'Coolify',
|
|
9
|
+
hint: 'Self-hosted PaaS — deploy apps, databases, and services',
|
|
10
|
+
skillName: 'coolify-deployment',
|
|
11
|
+
mcpServerKey: 'Coolify',
|
|
12
|
+
mcpConfig: {
|
|
13
|
+
type: 'stdio',
|
|
14
|
+
command: 'npx',
|
|
15
|
+
args: ['-y', '@masonator/coolify-mcp'],
|
|
16
|
+
},
|
|
17
|
+
authType: 'env-token',
|
|
18
|
+
envVars: [
|
|
19
|
+
{ name: 'COOLIFY_ACCESS_TOKEN', hint: 'Generate in Coolify Settings → API' },
|
|
20
|
+
{ name: 'COOLIFY_BASE_URL', hint: 'Your Coolify instance URL (e.g. https://coolify.example.com)' },
|
|
21
|
+
],
|
|
22
|
+
agentToolMap: {
|
|
23
|
+
'developer': ['list_applications', 'get_application', 'application_logs', 'deploy'],
|
|
24
|
+
'devops-expert': ['get_infrastructure_overview', 'diagnose_app', 'diagnose_server', 'find_issues', 'control', 'deploy', 'restart_project_apps'],
|
|
25
|
+
},
|
|
26
|
+
docsUrl: null,
|
|
27
|
+
officialDocs: 'https://coolify.io/docs',
|
|
28
|
+
mcpPackage: '@masonator/coolify-mcp',
|
|
29
|
+
};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Application Deployment
|
|
2
|
+
|
|
3
|
+
## Deployment Sources
|
|
4
|
+
|
|
5
|
+
| Source | Action | Key Args |
|
|
6
|
+
|--------|--------|----------|
|
|
7
|
+
| Public Git repo | `create_public` | `repository_url`, `branch` |
|
|
8
|
+
| GitHub App | `create_github` | `github_app_uuid`, `repository_url`, `branch` |
|
|
9
|
+
| SSH key | `create_key` | `private_key_uuid`, `repository_url`, `branch` |
|
|
10
|
+
| Docker image | `create_dockerimage` | `docker_image`, `docker_registry` |
|
|
11
|
+
|
|
12
|
+
## Create Application
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
application action="create_public"
|
|
16
|
+
project_uuid="<uuid>"
|
|
17
|
+
environment_name="production"
|
|
18
|
+
server_uuid="<uuid>"
|
|
19
|
+
repository_url="https://github.com/org/repo"
|
|
20
|
+
branch="main"
|
|
21
|
+
build_pack="dockerfile"
|
|
22
|
+
health_check_path="/health"
|
|
23
|
+
health_check_interval=30
|
|
24
|
+
health_check_retries=3
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Application Lifecycle
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
# List all apps (summaries)
|
|
31
|
+
list_applications
|
|
32
|
+
|
|
33
|
+
# Full details for one app
|
|
34
|
+
get_application uuid="<uuid>"
|
|
35
|
+
|
|
36
|
+
# View logs
|
|
37
|
+
application_logs uuid="<uuid>" lines=100
|
|
38
|
+
|
|
39
|
+
# Deploy (with optional force rebuild)
|
|
40
|
+
deploy uuid="<uuid>" force_rebuild=true
|
|
41
|
+
|
|
42
|
+
# Restart/stop/start
|
|
43
|
+
control resource="application" action="restart" uuid="<uuid>"
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Environment Variables
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
# List env vars
|
|
50
|
+
env_vars resource="application" action="list" uuid="<uuid>"
|
|
51
|
+
|
|
52
|
+
# Create env var
|
|
53
|
+
env_vars resource="application" action="create" uuid="<uuid>" key="DATABASE_URL" value="postgres://..." is_preview=false
|
|
54
|
+
|
|
55
|
+
# Bulk update across apps
|
|
56
|
+
bulk_env_update key="API_URL" value="https://api.example.com" uuids=["<uuid1>","<uuid2>"]
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Health Check Configuration
|
|
60
|
+
|
|
61
|
+
Always configure health checks to prevent deploying broken apps:
|
|
62
|
+
|
|
63
|
+
- `health_check_path` — endpoint that returns 200 (e.g., `/health`, `/api/health`)
|
|
64
|
+
- `health_check_interval` — seconds between checks (default: 30)
|
|
65
|
+
- `health_check_retries` — failures before marking unhealthy (default: 3)
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# CI/CD & Docker Image Deployments
|
|
2
|
+
|
|
3
|
+
## Webhook-Triggered Deployments
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
# Trigger deploy by image tag
|
|
7
|
+
deploy_webhook tag="v1.2.3"
|
|
8
|
+
|
|
9
|
+
# Trigger deploy by application UUID
|
|
10
|
+
deploy_webhook uuid="<app-uuid>"
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
REST API equivalent (outside MCP):
|
|
14
|
+
```bash
|
|
15
|
+
# By tag
|
|
16
|
+
curl -H "Authorization: Bearer $COOLIFY_TOKEN" \
|
|
17
|
+
"$COOLIFY_URL/api/v1/deployments/deploy?tag=v1.2.3"
|
|
18
|
+
|
|
19
|
+
# By UUID
|
|
20
|
+
curl -H "Authorization: Bearer $COOLIFY_TOKEN" \
|
|
21
|
+
"$COOLIFY_URL/api/v1/deployments/deploy?uuid=<app-uuid>"
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Docker Image Deployment (no git repo)
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
application action="create_dockerimage"
|
|
28
|
+
project_uuid="<uuid>"
|
|
29
|
+
environment_name="production"
|
|
30
|
+
server_uuid="<uuid>"
|
|
31
|
+
docker_image="nginx:latest"
|
|
32
|
+
docker_registry="docker.io"
|
|
33
|
+
health_check_path="/"
|
|
34
|
+
health_check_interval=30
|
|
35
|
+
health_check_retries=3
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
**Private registry** (credentials configured in Coolify dashboard):
|
|
39
|
+
```
|
|
40
|
+
application action="create_dockerimage"
|
|
41
|
+
project_uuid="<uuid>"
|
|
42
|
+
environment_name="production"
|
|
43
|
+
server_uuid="<uuid>"
|
|
44
|
+
docker_image="ghcr.io/org/app:latest"
|
|
45
|
+
docker_registry="ghcr.io"
|
|
46
|
+
health_check_path="/health"
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## GitHub Actions Integration
|
|
50
|
+
|
|
51
|
+
```yaml
|
|
52
|
+
# .github/workflows/deploy.yml
|
|
53
|
+
jobs:
|
|
54
|
+
deploy:
|
|
55
|
+
runs-on: ubuntu-latest
|
|
56
|
+
steps:
|
|
57
|
+
- name: Trigger Coolify Deploy
|
|
58
|
+
run: |
|
|
59
|
+
curl -sf -H "Authorization: Bearer ${{ secrets.COOLIFY_TOKEN }}" \
|
|
60
|
+
"${{ secrets.COOLIFY_URL }}/api/v1/deployments/deploy?uuid=${{ secrets.APP_UUID }}"
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Required GitHub secrets: `COOLIFY_TOKEN`, `COOLIFY_URL`, `APP_UUID`
|
|
64
|
+
|
|
65
|
+
## Deployment Strategy Reference
|
|
66
|
+
|
|
67
|
+
| Strategy | When to Use |
|
|
68
|
+
|----------|-------------|
|
|
69
|
+
| Git push + auto-deploy | Default for most apps |
|
|
70
|
+
| Webhook by tag | Version-pinned releases, staging promotion |
|
|
71
|
+
| Webhook by UUID | CI/CD pipeline integration (specific app) |
|
|
72
|
+
| Docker image (`create_dockerimage`) | Pre-built images, registry-first workflow |
|
|
73
|
+
| `force_rebuild: true` | Cache corruption or dependency issues |
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Database & Service Management
|
|
2
|
+
|
|
3
|
+
## Supported Database Types
|
|
4
|
+
|
|
5
|
+
PostgreSQL, MySQL, MariaDB, MongoDB, Redis, KeyDB, ClickHouse, Dragonfly.
|
|
6
|
+
|
|
7
|
+
## Create Database
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
database action="create"
|
|
11
|
+
type="postgresql"
|
|
12
|
+
project_uuid="<uuid>"
|
|
13
|
+
environment_name="production"
|
|
14
|
+
server_uuid="<uuid>"
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Backup Management
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
# List backup schedules
|
|
21
|
+
database_backups action="list_schedules" database_uuid="<uuid>"
|
|
22
|
+
|
|
23
|
+
# Create schedule
|
|
24
|
+
database_backups action="create" database_uuid="<uuid>"
|
|
25
|
+
frequency="0 2 * * *" # Daily at 2 AM
|
|
26
|
+
retention=7
|
|
27
|
+
|
|
28
|
+
# View executions
|
|
29
|
+
database_backups action="list_executions" database_uuid="<uuid>"
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Services
|
|
33
|
+
|
|
34
|
+
One-click service deployments (e.g., Plausible, Gitea, Uptime Kuma):
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
# List available services
|
|
38
|
+
list_services
|
|
39
|
+
|
|
40
|
+
# Create service
|
|
41
|
+
service action="create"
|
|
42
|
+
project_uuid="<uuid>"
|
|
43
|
+
environment_name="production"
|
|
44
|
+
server_uuid="<uuid>"
|
|
45
|
+
type="plausible"
|
|
46
|
+
|
|
47
|
+
# Manage env vars
|
|
48
|
+
env_vars resource="service" action="list" uuid="<uuid>"
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Database Lifecycle
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
control resource="database" action="start" uuid="<uuid>"
|
|
55
|
+
control resource="database" action="stop" uuid="<uuid>"
|
|
56
|
+
control resource="database" action="restart" uuid="<uuid>"
|
|
57
|
+
```
|