@cloudgrid-io/mcp 0.11.0 → 0.12.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/package.json +3 -1
- package/src/corpus/capability-map.md +46 -28
- package/src/corpus/cloudgrid-yaml.md +569 -0
- package/src/corpus/examples/ai-app/index.md +237 -0
- package/src/corpus/examples/api-service/index.md +190 -0
- package/src/corpus/examples/app-with-data/index.md +24 -19
- package/src/corpus/templates/ai-app/README.md +95 -0
- package/src/corpus/templates/ai-app/cloudgrid.yaml +20 -0
- package/src/corpus/templates/ai-app/index.md +269 -0
- package/src/corpus/templates/ai-app/services/web/app/api/chat/route.js +51 -0
- package/src/corpus/templates/ai-app/services/web/app/chat.js +60 -0
- package/src/corpus/templates/ai-app/services/web/app/layout.js +36 -0
- package/src/corpus/templates/ai-app/services/web/app/page.js +25 -0
- package/src/corpus/templates/ai-app/services/web/lib/db.js +32 -0
- package/src/corpus/templates/ai-app/services/web/package.json +17 -0
- package/src/corpus/templates/api-service/README.md +87 -0
- package/src/corpus/templates/api-service/cloudgrid.yaml +18 -0
- package/src/corpus/templates/api-service/index.md +225 -0
- package/src/corpus/templates/api-service/services/api/package.json +13 -0
- package/src/corpus/templates/api-service/services/api/src/index.js +132 -0
- package/src/corpus/templates/app-with-data/README.md +22 -19
- package/src/corpus/templates/app-with-data/cloudgrid.yaml +9 -13
- package/src/corpus/templates/app-with-data/index.md +37 -38
- package/src/corpus/templates/app-with-data/services/web/lib/db.js +10 -9
- package/src/corpus/troubleshooting/persistent-apps.md +6 -6
- package/src/corpus/workflows/ai-app.md +136 -0
- package/src/corpus/workflows/api-service.md +118 -0
- package/src/corpus/workflows/app-with-data.md +21 -18
- package/src/tools.js +1 -0
|
@@ -0,0 +1,569 @@
|
|
|
1
|
+
# cloudgrid.yaml reference — the full manifest schema for agents
|
|
2
|
+
|
|
3
|
+
> **Canonical source:** `cloudgrid-yaml-reference.md` in the CloudGrid platform
|
|
4
|
+
> repo. This is the agent-facing distribution
|
|
5
|
+
> of that reference — practically complete, not a verbatim dump. **Keep in sync**
|
|
6
|
+
> with the upstream: when the platform reference changes, update this doc.
|
|
7
|
+
>
|
|
8
|
+
> Fetch this doc any time with `gridctl_fetch("doc", "cloudgrid-yaml")`.
|
|
9
|
+
> For matching an intent to a template, see the companion
|
|
10
|
+
> `gridctl_fetch("doc", "capability-map")`.
|
|
11
|
+
|
|
12
|
+
`cloudgrid.yaml` is the manifest at the **root of your project** that defines your
|
|
13
|
+
entity (an app or an agent). It declares the services your entity runs, the
|
|
14
|
+
infrastructure it needs, and how it behaves. `grid init` creates it; `grid plug`
|
|
15
|
+
reads it to build and deploy.
|
|
16
|
+
|
|
17
|
+
**Wiring a database or cache? Use `needs:`** — `needs: {database: true}` is
|
|
18
|
+
canonical and the deployer injects the connection env vars from it. See §7 for the
|
|
19
|
+
`needs:`-vs-`requires:` note. `requires:` is the deprecated v1 alias; don't author
|
|
20
|
+
new yaml with it.
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## 1. Minimal, real, deployable examples
|
|
25
|
+
|
|
26
|
+
These are the shapes verified against live deploys. Copy one and adapt it.
|
|
27
|
+
|
|
28
|
+
### Static site → deploys as an inspiration (instant)
|
|
29
|
+
|
|
30
|
+
A single-file / static bundle. No `needs:`. Publishes instantly via
|
|
31
|
+
`gridctl_drop`, works on any edition (hosted or local).
|
|
32
|
+
|
|
33
|
+
```yaml
|
|
34
|
+
name: landing-page
|
|
35
|
+
services:
|
|
36
|
+
web:
|
|
37
|
+
type: static
|
|
38
|
+
path: /
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Node.js app
|
|
42
|
+
|
|
43
|
+
Code lives under `services/web/` (`src/index.js` + `package.json`). Runtime deploy
|
|
44
|
+
(async, local edition only).
|
|
45
|
+
|
|
46
|
+
```yaml
|
|
47
|
+
name: my-api
|
|
48
|
+
services:
|
|
49
|
+
web:
|
|
50
|
+
type: node
|
|
51
|
+
path: /
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Next.js app
|
|
55
|
+
|
|
56
|
+
```yaml
|
|
57
|
+
name: my-dashboard
|
|
58
|
+
services:
|
|
59
|
+
web:
|
|
60
|
+
type: nextjs
|
|
61
|
+
path: /
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Next.js + database — the canonical shape
|
|
65
|
+
|
|
66
|
+
Declare the datastore with `needs: {database: true}`. The deployer provisions
|
|
67
|
+
Mongo and injects `DATABASE_MONGODB_URL` (plus the legacy `MONGODB_URL` alias) at
|
|
68
|
+
dev-time and runtime. `requires:` is the deprecated v1 alias — don't author new
|
|
69
|
+
yaml with it, and never set `needs:` and `requires:` together (the validator
|
|
70
|
+
rejects the combination). See §7 for the details.
|
|
71
|
+
|
|
72
|
+
This mirrors the live-verified `app-with-data` template exactly: app code under
|
|
73
|
+
`services/web/`, and the app reads `process.env.DATABASE_MONGODB_URL` (falling
|
|
74
|
+
back to the legacy `MONGODB_URL`) **lazily** (inside a getter, never at module top
|
|
75
|
+
level — a top-level read fails `next build`).
|
|
76
|
+
|
|
77
|
+
```yaml
|
|
78
|
+
# Rename this app. The grid injects the DB connection string (and a Redis URL if
|
|
79
|
+
# you add a cache) as environment variables at runtime — do NOT set them
|
|
80
|
+
# yourself, and never commit a connection string or secret.
|
|
81
|
+
#
|
|
82
|
+
# `needs: { database: true }` is canonical: the deployer provisions Mongo and
|
|
83
|
+
# injects DATABASE_MONGODB_URL (plus the legacy MONGODB_URL alias). `requires:` is
|
|
84
|
+
# the deprecated v1 alias — don't mix the two (the validator rejects it).
|
|
85
|
+
name: my-app
|
|
86
|
+
services:
|
|
87
|
+
web:
|
|
88
|
+
type: nextjs
|
|
89
|
+
path: /
|
|
90
|
+
needs:
|
|
91
|
+
database: true
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Agent — a cron job
|
|
95
|
+
|
|
96
|
+
An agent is an app with `kind: agent` and an `agent:` block. Cron is a **service
|
|
97
|
+
type**, not a need. `type: cron` requires a `schedule` and cannot have `path` or
|
|
98
|
+
`port`.
|
|
99
|
+
|
|
100
|
+
```yaml
|
|
101
|
+
name: nightly-sync
|
|
102
|
+
kind: agent
|
|
103
|
+
agent:
|
|
104
|
+
purpose: Sync data from an external API every night
|
|
105
|
+
services:
|
|
106
|
+
sync:
|
|
107
|
+
type: cron
|
|
108
|
+
schedule: "0 9 * * *" # 9am daily
|
|
109
|
+
timezone: UTC
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
> A cron/agent that also needs a database declares it the canonical way:
|
|
113
|
+
> `needs: {database: true}` (not the deprecated `requires:`).
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## 2. Full annotated example (the kitchen sink)
|
|
118
|
+
|
|
119
|
+
Every supported field, commented. **No real project uses all of these at once** —
|
|
120
|
+
this is a reference, not a template. (The `needs:` block here is the canonical
|
|
121
|
+
schema — the deployer injects the connection env vars from it; see §7.)
|
|
122
|
+
|
|
123
|
+
```yaml
|
|
124
|
+
# ─── Identity ───────────────────────────────────────────────────────
|
|
125
|
+
name: my-app # Entity slug (required). 2-42 chars, lowercase + hyphens.
|
|
126
|
+
description: Order processing API # One-line summary (max 280 chars). Optional.
|
|
127
|
+
icon: "📦" # Single emoji or short text (max 64 chars). Optional.
|
|
128
|
+
kind: app # 'app' (default) or 'agent'. Optional.
|
|
129
|
+
|
|
130
|
+
# ─── Networking ─────────────────────────────────────────────────────
|
|
131
|
+
expose: true # Serve public HTTP traffic (default: true).
|
|
132
|
+
# Set to false for internal-only entities.
|
|
133
|
+
custom_domains: # Additional FQDNs the entity serves on. Optional.
|
|
134
|
+
- orders.example.com
|
|
135
|
+
|
|
136
|
+
# ─── Health probe (entity-level override) ───────────────────────────
|
|
137
|
+
probe: # HTTP health probe override. Optional.
|
|
138
|
+
path: /healthz # Probe path (default: /).
|
|
139
|
+
expected: "<500" # Expected status: '<500' (default), '200', '2xx'.
|
|
140
|
+
|
|
141
|
+
# ─── Deploy notifications ──────────────────────────────────────────
|
|
142
|
+
notify: true # Slack notification on deploy failure (default: true).
|
|
143
|
+
|
|
144
|
+
# ─── Scaling ────────────────────────────────────────────────────────
|
|
145
|
+
scale: auto # 'auto' (default), 'alert', or integer 1-10.
|
|
146
|
+
|
|
147
|
+
# ─── Infrastructure needs (v2, CANONICAL — the deployer injects from these) ─
|
|
148
|
+
needs:
|
|
149
|
+
database: true # Mongo (org-tier). Injects DATABASE_MONGODB_URL.
|
|
150
|
+
cache: true # Redis with LRU eviction. Injects CACHE_REDIS_URL.
|
|
151
|
+
kv: true # Redis, no eviction. Injects KV_REDIS_URL.
|
|
152
|
+
queue: true # Redis for BullMQ etc. Injects QUEUE_REDIS_URL.
|
|
153
|
+
pubsub: true # Redis pub/sub. Injects PUBSUB_REDIS_URL.
|
|
154
|
+
vector: pgvector # pgvector. Injects VECTOR_PGVECTOR_URL.
|
|
155
|
+
object_storage: true # GCS bucket. Injects OBJECT_STORAGE_GCS_BUCKET.
|
|
156
|
+
disk: true # Persistent filesystem at /data. Injects DISK_PATH.
|
|
157
|
+
ai: true # AI Gateway access. Injects AI_GATEWAY_URL.
|
|
158
|
+
|
|
159
|
+
# ─── Inter-entity communication ────────────────────────────────────
|
|
160
|
+
calls: # Entities this app calls (outbound grants).
|
|
161
|
+
- org:atomic/billing-api
|
|
162
|
+
- org:atomic/content-service
|
|
163
|
+
|
|
164
|
+
callers: # Inbound caller policy.
|
|
165
|
+
policy: auto # 'auto' or 'manual' (default: 'manual').
|
|
166
|
+
scope: org # 'org', 'space', or 'explicit' (default: 'org').
|
|
167
|
+
fallback: deny # 'deny' or 'warn' (default: 'deny').
|
|
168
|
+
|
|
169
|
+
# ─── Vault ──────────────────────────────────────────────────────────
|
|
170
|
+
vault: # Map of ENV_VAR: vault_item_key. Optional.
|
|
171
|
+
STRIPE_KEY: stripe-live-key
|
|
172
|
+
SENDGRID_API_KEY: sendgrid-key
|
|
173
|
+
|
|
174
|
+
# ─── Connectors and hooks ──────────────────────────────────────────
|
|
175
|
+
connectors:
|
|
176
|
+
- id: team-slack
|
|
177
|
+
target: org # 'system' or 'org'
|
|
178
|
+
channel: "#deploys"
|
|
179
|
+
|
|
180
|
+
hooks: # Outbound events. on ∈ release.published |
|
|
181
|
+
- on: deploy.failed # deploy.failed | feedback.received | entity.created
|
|
182
|
+
via: team-slack # Must match a connector id above.
|
|
183
|
+
template: "Deploy failed for {entity_name}: {error}"
|
|
184
|
+
|
|
185
|
+
triggers: # Inbound events routed into your app.
|
|
186
|
+
- from: team-slack
|
|
187
|
+
event: message
|
|
188
|
+
channel: "#support"
|
|
189
|
+
route: /webhooks/slack # Path in your app that receives the POST.
|
|
190
|
+
|
|
191
|
+
# ─── Local development ─────────────────────────────────────────────
|
|
192
|
+
dev:
|
|
193
|
+
snapshot:
|
|
194
|
+
mask: # Fields to redact in dev snapshots (collection.field).
|
|
195
|
+
- users.email
|
|
196
|
+
- users.phone
|
|
197
|
+
|
|
198
|
+
# ─── Services ──────────────────────────────────────────────────────
|
|
199
|
+
services:
|
|
200
|
+
web:
|
|
201
|
+
type: nextjs # 'node', 'nextjs', 'python', 'static', or 'cron'.
|
|
202
|
+
path: / # HTTP route prefix. false = internal-only.
|
|
203
|
+
port: 3000 # Container port (default: 8080).
|
|
204
|
+
base_image: distroless # 'alpine' or 'distroless' (nextjs only).
|
|
205
|
+
health: # Health probe config.
|
|
206
|
+
path: /api/health
|
|
207
|
+
expected: "200"
|
|
208
|
+
env: # Static env vars (not secrets).
|
|
209
|
+
LOG_LEVEL: info
|
|
210
|
+
depends_on: # Wait for these before starting.
|
|
211
|
+
- worker
|
|
212
|
+
|
|
213
|
+
worker:
|
|
214
|
+
type: node
|
|
215
|
+
path: false # No public URL — internal service.
|
|
216
|
+
source:
|
|
217
|
+
path: src/worker # Custom source location (instead of services/worker/).
|
|
218
|
+
needs: # Per-service needs override (merged per-key).
|
|
219
|
+
queue: true # Adds queue (inherits database, cache from app-level).
|
|
220
|
+
persist: # Service-level persistence.
|
|
221
|
+
- name: scratch
|
|
222
|
+
path: /tmp/scratch
|
|
223
|
+
size: 500Mi
|
|
224
|
+
|
|
225
|
+
reports:
|
|
226
|
+
type: cron
|
|
227
|
+
schedule: "0 9 * * 1" # Every Monday at 9am.
|
|
228
|
+
timezone: UTC # 'UTC', 'EST', or 'PST'.
|
|
229
|
+
run: job # 'job' (run code) or an HTTP URL.
|
|
230
|
+
|
|
231
|
+
frontend:
|
|
232
|
+
type: static
|
|
233
|
+
path: /app
|
|
234
|
+
build: # Build step for static sites.
|
|
235
|
+
command: npm run build
|
|
236
|
+
output: dist
|
|
237
|
+
env:
|
|
238
|
+
VITE_API_URL: https://api.example.com
|
|
239
|
+
node_version: "22" # Node version for build: '18', '20', '22', '24'. STRING, not int.
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
---
|
|
243
|
+
|
|
244
|
+
## 3. Top-level fields
|
|
245
|
+
|
|
246
|
+
### Required
|
|
247
|
+
|
|
248
|
+
| Field | Type | Description |
|
|
249
|
+
|-------|------|-------------|
|
|
250
|
+
| `name` | string | Entity slug. 2-42 chars, lowercase alphanumeric + hyphens, starts/ends with a letter or digit. Becomes part of the public URL. |
|
|
251
|
+
| `services` | map | One or more named service configs. See §4. |
|
|
252
|
+
|
|
253
|
+
### Optional
|
|
254
|
+
|
|
255
|
+
| Field | Type | Default | Description |
|
|
256
|
+
|-------|------|---------|-------------|
|
|
257
|
+
| `description` | string | — | One-line summary, max 280 chars. Auto-generated by Grid Brain if unset. |
|
|
258
|
+
| `icon` | string | — | Single emoji or short text, max 64 chars. |
|
|
259
|
+
| `kind` | string | `app` | `app` or `agent` (an agent is an app with an `agent:` block). |
|
|
260
|
+
| `expose` | boolean | `true` | Whether the entity serves public HTTP. `false` = internal-only. |
|
|
261
|
+
| `custom_domains` | string[] | — | Additional FQDNs served. |
|
|
262
|
+
| `probe` | object | — | Entity-level health probe override: `{ path, expected }`. |
|
|
263
|
+
| `notify` | boolean | `true` | Slack notification on deploy failure. |
|
|
264
|
+
| `scale` | string \| int | `auto` | `auto`, `alert`, or integer 1-10. |
|
|
265
|
+
| `needs` | object | — | Infrastructure capabilities (canonical). See §5 + caveat box. |
|
|
266
|
+
| `calls` | string[] | — | Entities this app calls outbound. |
|
|
267
|
+
| `callers` | object | — | Inbound caller policy. |
|
|
268
|
+
| `vault` | map | — | `ENV_VAR: vault_item_key` mappings. |
|
|
269
|
+
| `connectors` / `hooks` / `triggers` | array | — | Connector refs, outbound event hooks, inbound trigger webhooks. |
|
|
270
|
+
| `dev` | object | — | Local development config (`grid dev`). |
|
|
271
|
+
| `agent` | object | — | Agent metadata (loosely typed: `purpose`, `schedule`, `trigger`). |
|
|
272
|
+
| `auto_depends_on_private_requires` | boolean | `true` | Auto-inject private resources as service deps. |
|
|
273
|
+
|
|
274
|
+
### Deprecated (still accepted)
|
|
275
|
+
|
|
276
|
+
| Field | Replacement | Notes |
|
|
277
|
+
|-------|-------------|-------|
|
|
278
|
+
| `domain` | `custom_domains` | Single custom domain string. |
|
|
279
|
+
| `network` | `expose` | `{ public: bool }`. |
|
|
280
|
+
| `requires` | `needs` | v1 infra requirements. Deprecated — author `needs:` instead. **Cannot be mixed with `needs`** (the validator rejects the combination). |
|
|
281
|
+
| `persist` (top-level) | `needs.disk` | v1 entity-level filesystem persistence. |
|
|
282
|
+
|
|
283
|
+
---
|
|
284
|
+
|
|
285
|
+
## 4. Services
|
|
286
|
+
|
|
287
|
+
Each service is a named entry under `services:`. The name is the service identifier
|
|
288
|
+
(same slug rules as the entity name). **By default, a service's code lives at
|
|
289
|
+
`services/<service-name>/`** — `path:` is the URL mount, NOT the filesystem path. A
|
|
290
|
+
service named `web` → the CLI looks for `services/web/`. (Override with `source.path`.)
|
|
291
|
+
|
|
292
|
+
### Service types
|
|
293
|
+
|
|
294
|
+
| Type | What it runs | Required files | Default port | `path`? |
|
|
295
|
+
|------|-------------|----------------|--------------|---------|
|
|
296
|
+
| `node` | Node.js server | `src/index.js` (or `.ts`), `package.json` | 8080 | yes |
|
|
297
|
+
| `nextjs` | Next.js app (standalone output) | `next.config.*`, `package.json` | 8080 | yes |
|
|
298
|
+
| `python` | Python server | `src/main.py`, `requirements.txt` | 8080 | yes |
|
|
299
|
+
| `static` | Static files served by nginx (optional `build`) | `index.html` or build output | 8080 | yes |
|
|
300
|
+
| `cron` | Scheduled job | `src/job.js` or `src/main.py` | none | **no** — needs `schedule`, no `path`/`port` |
|
|
301
|
+
|
|
302
|
+
### Service fields
|
|
303
|
+
|
|
304
|
+
| Field | Type | Required | Default | Applies to |
|
|
305
|
+
|-------|------|----------|---------|-----------|
|
|
306
|
+
| `type` | string | Yes | — | All |
|
|
307
|
+
| `path` | string \| `false` | No | — | All except cron |
|
|
308
|
+
| `port` | integer (1-65535) | No | 8080 | All except cron |
|
|
309
|
+
| `lang` | string | No | — | node only (`javascript` \| `typescript`) |
|
|
310
|
+
| `base_image` | string | No | `distroless` | nextjs only (`alpine` \| `distroless`) |
|
|
311
|
+
| `source` | object | No | — | All — `{ path }`, default `services/<name>/`, `.` = project root |
|
|
312
|
+
| `build` | object | No | — | static only — `{ command, output, env }` |
|
|
313
|
+
| `node_version` | string | No | `"22"` | static build (`"18"`/`"20"`/`"22"`/`"24"` — STRING) |
|
|
314
|
+
| `health` | bool \| string \| object | No | TCP | All except cron |
|
|
315
|
+
| `env` | map (string values) | No | — | All (reserved names forbidden) |
|
|
316
|
+
| `schedule` | string (cron) | Required for cron | — | cron only |
|
|
317
|
+
| `run` | string | No | `job` | cron only (`job` or an HTTP URL) |
|
|
318
|
+
| `timezone` | string | No | `UTC` | cron only (`UTC`/`EST`/`PST`) |
|
|
319
|
+
| `depends_on` | string[] | No | — | All except cron (no self/circular, no cron) |
|
|
320
|
+
| `persist` | bool \| array | No | — | All except cron |
|
|
321
|
+
| `needs` | object | No | — | All (overrides/extends app-level per-key) |
|
|
322
|
+
|
|
323
|
+
### `path` — HTTP routing
|
|
324
|
+
|
|
325
|
+
`"/"` serves at root; `"/api"` under a prefix; `false` = internal only (no public
|
|
326
|
+
URL); omitted = treated as `false` with a soft warning. **Only one service can
|
|
327
|
+
claim `"/"`.**
|
|
328
|
+
|
|
329
|
+
### `source` — custom source location
|
|
330
|
+
|
|
331
|
+
```yaml
|
|
332
|
+
source:
|
|
333
|
+
path: src/worker # relative to project root; no leading /, no .., no globs
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
Use `.` for a flat layout where the service code lives at the project root:
|
|
337
|
+
|
|
338
|
+
```yaml
|
|
339
|
+
services:
|
|
340
|
+
web:
|
|
341
|
+
type: node
|
|
342
|
+
path: /
|
|
343
|
+
source:
|
|
344
|
+
path: .
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
### `build` — static site build step (static only)
|
|
348
|
+
|
|
349
|
+
```yaml
|
|
350
|
+
build:
|
|
351
|
+
command: npm run build # required
|
|
352
|
+
output: dist # built output dir (default: dist)
|
|
353
|
+
env:
|
|
354
|
+
VITE_API_URL: https://api.example.com
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
### `health` — probe config
|
|
358
|
+
|
|
359
|
+
Omitted / `false` → TCP probe. `true` → HTTP at `/health`. `"/healthz"` → HTTP at
|
|
360
|
+
that path. `{ path, expected }` → HTTP with expected status
|
|
361
|
+
(`healthy` (default, 2xx/3xx), `<500`, `200`, `2xx`).
|
|
362
|
+
|
|
363
|
+
### `env` — static environment variables
|
|
364
|
+
|
|
365
|
+
String key-values injected at runtime (not build). **Reserved names (rejected):**
|
|
366
|
+
`PORT`, `APP_NAME`, `SERVICE_NAME`, `NODE_ENV`, `MONGODB_URL`, `REDIS_URL`,
|
|
367
|
+
`AI_GATEWAY_URL`, `N8N_WEBHOOK_URL`, and any key starting with `CLOUDGRID_`. Use
|
|
368
|
+
`grid secrets set` for sensitive values.
|
|
369
|
+
|
|
370
|
+
### Per-service `needs` override
|
|
371
|
+
|
|
372
|
+
Merge is per-key — service values override app-level for that key; absent keys
|
|
373
|
+
inherit:
|
|
374
|
+
|
|
375
|
+
```yaml
|
|
376
|
+
needs: # App-level
|
|
377
|
+
database: true
|
|
378
|
+
cache: true
|
|
379
|
+
pubsub: true
|
|
380
|
+
services:
|
|
381
|
+
worker:
|
|
382
|
+
type: node
|
|
383
|
+
path: false
|
|
384
|
+
needs:
|
|
385
|
+
queue: true # Added
|
|
386
|
+
pubsub: false # Overridden (opted out)
|
|
387
|
+
# Effective: database: true, cache: true, queue: true, pubsub: false
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
---
|
|
391
|
+
|
|
392
|
+
## 5. Needs (infrastructure) — the nine needs
|
|
393
|
+
|
|
394
|
+
The `needs:` block declares infrastructure capabilities; the platform provisions
|
|
395
|
+
them and injects connection details as env vars. **Cron is NOT a need — it is a
|
|
396
|
+
service type.**
|
|
397
|
+
|
|
398
|
+
| Need | Provides | Injected env var(s) | Backing |
|
|
399
|
+
|------|----------|---------------------|---------|
|
|
400
|
+
| `database` | Primary datastore | `DATABASE_MONGODB_URL` (+legacy `MONGODB_URL`) | MongoDB |
|
|
401
|
+
| `cache` | Key-value store, LRU eviction | `CACHE_REDIS_URL` (+legacy `REDIS_URL`) | Redis |
|
|
402
|
+
| `kv` | Key-value store, no eviction | `KV_REDIS_URL` | Redis |
|
|
403
|
+
| `queue` | Durable job queue substrate | `QUEUE_REDIS_URL` | Redis |
|
|
404
|
+
| `pubsub` | Broadcast pub/sub | `PUBSUB_REDIS_URL` | Redis |
|
|
405
|
+
| `vector` | Vector embeddings DB | `VECTOR_PGVECTOR_URL` (+legacy `PGVECTOR_URL`) | pgvector (Postgres) |
|
|
406
|
+
| `object_storage` | Object/blob bucket | `OBJECT_STORAGE_GCS_BUCKET`, `OBJECT_STORAGE_GCS_REGION` | GCS |
|
|
407
|
+
| `disk` | Persistent filesystem mount | `DISK_PATH` | PVC |
|
|
408
|
+
| `ai` | AI Gateway access | `AI_GATEWAY_URL` | CloudGrid AI Gateway |
|
|
409
|
+
|
|
410
|
+
`cache`, `kv`, `queue`, `pubsub` all share one physical Redis but each gets a
|
|
411
|
+
distinct env var. `cache` runs `allkeys-lru` eviction (derived data); the other
|
|
412
|
+
three run `noeviction` (durable data).
|
|
413
|
+
|
|
414
|
+
### Usage forms
|
|
415
|
+
|
|
416
|
+
```yaml
|
|
417
|
+
needs:
|
|
418
|
+
database: true # Grid decides tier
|
|
419
|
+
database: pool # Shared Mongo (free)
|
|
420
|
+
database: dedicated # Entity-private Mongo
|
|
421
|
+
database: { tier: external, secret: MY_DB } # Bring your own (value from vault)
|
|
422
|
+
|
|
423
|
+
cache: true # Platform Redis
|
|
424
|
+
cache: { engine: external, secret: MY_REDIS } # Bring your own (same forms for kv/queue/pubsub)
|
|
425
|
+
|
|
426
|
+
vector: true # Platform default engine
|
|
427
|
+
vector: pgvector # pgvector explicitly
|
|
428
|
+
vector: { engine: pgvector, dim: 1536 } # With dimension hint
|
|
429
|
+
|
|
430
|
+
object_storage: true # Default size (100MB)
|
|
431
|
+
object_storage: { size: 10Gi } # Custom size
|
|
432
|
+
|
|
433
|
+
disk: true # 50MB at /data
|
|
434
|
+
disk: { size: 1Gi, mount: /var/data } # Custom (forbidden mounts: /proc, /sys, /dev)
|
|
435
|
+
|
|
436
|
+
ai: true # Enable AI Gateway (ai: false = disabled, default)
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
### Injected environment variables
|
|
440
|
+
|
|
441
|
+
**Always injected** (do not set these yourself):
|
|
442
|
+
|
|
443
|
+
| Variable | Description |
|
|
444
|
+
|----------|-------------|
|
|
445
|
+
| `PORT` | Port your service should listen on (your `port:` or 8080). |
|
|
446
|
+
| `APP_NAME` | Entity slug. |
|
|
447
|
+
| `SERVICE_NAME` | Service name (the key under `services:`). |
|
|
448
|
+
| `NODE_ENV` | `production` in deployed environments. |
|
|
449
|
+
|
|
450
|
+
**Per `needs:` declaration:** as in the table above. Legacy aliases
|
|
451
|
+
(`MONGODB_URL`, `REDIS_URL`, `PGVECTOR_URL`) are injected alongside the canonical
|
|
452
|
+
names during the transition; new apps should read the canonical names
|
|
453
|
+
(`DATABASE_MONGODB_URL`, etc.) with the legacy name as a fallback.
|
|
454
|
+
|
|
455
|
+
### Persistence (disk)
|
|
456
|
+
|
|
457
|
+
Two ways to declare filesystem persistence — use `needs.disk` (recommended) or
|
|
458
|
+
service-level `persist:` (fine-grained). Do not mix the entity-level flag form and
|
|
459
|
+
the service-level verbose array form.
|
|
460
|
+
|
|
461
|
+
```yaml
|
|
462
|
+
# Recommended:
|
|
463
|
+
needs:
|
|
464
|
+
disk: { size: 1Gi, mount: /var/sqlite }
|
|
465
|
+
|
|
466
|
+
# Fine-grained, per-service:
|
|
467
|
+
services:
|
|
468
|
+
web:
|
|
469
|
+
type: node
|
|
470
|
+
path: /
|
|
471
|
+
persist:
|
|
472
|
+
- name: db
|
|
473
|
+
path: /data/sqlite
|
|
474
|
+
size: 1Gi
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
Persistence pins the service to 1 replica with a `Recreate` rollout strategy
|
|
478
|
+
(single-writer constraint).
|
|
479
|
+
|
|
480
|
+
---
|
|
481
|
+
|
|
482
|
+
## 6. Validation rules (the ones agents trip on)
|
|
483
|
+
|
|
484
|
+
The platform validates `cloudgrid.yaml` at plug time — strictly.
|
|
485
|
+
|
|
486
|
+
1. **No `Dockerfile`.** Images are generated from `services.*.type`; a Dockerfile
|
|
487
|
+
is silently ignored.
|
|
488
|
+
2. **No `org:` or `entity_id:` in the YAML.** Identity lives in
|
|
489
|
+
`.cloudgrid/link.json` (gitignored), not the manifest.
|
|
490
|
+
3. **No mixing `requires:` and `needs:`.** Use one or the other — the validator
|
|
491
|
+
rejects the combination. Author `needs:` (canonical); `requires:` is the
|
|
492
|
+
deprecated v1 alias.
|
|
493
|
+
4. **No mixed persistence forms.** Entity-level flag form and service-level
|
|
494
|
+
verbose array form cannot coexist.
|
|
495
|
+
5. **Unknown fields are rejected** with a "did you mean?" suggestion.
|
|
496
|
+
6. **Reserved env var names cannot be set** in `services.*.env` (see §4 `env`).
|
|
497
|
+
7. **`port` must be an integer 1-65535** (not a float). Forbidden on `type: cron`.
|
|
498
|
+
8. **`depends_on`:** no self-references, no circular dependencies, no references to
|
|
499
|
+
cron services.
|
|
500
|
+
9. **`node_version` must be a string** (`"22"`, not `22`).
|
|
501
|
+
10. **Cron services require `schedule`** and cannot have `path` or `port`.
|
|
502
|
+
11. **Entity names** must match `^[a-z][a-z0-9-]*[a-z0-9]$` (2-42 chars) — no
|
|
503
|
+
leading/trailing hyphens.
|
|
504
|
+
|
|
505
|
+
### File layout
|
|
506
|
+
|
|
507
|
+
```
|
|
508
|
+
my-app/
|
|
509
|
+
├── cloudgrid.yaml
|
|
510
|
+
├── .cloudgrid/link.json # gitignored; local binding to the server-side entity
|
|
511
|
+
├── services/
|
|
512
|
+
│ ├── web/ # service "web" → services/web/
|
|
513
|
+
│ │ ├── package.json
|
|
514
|
+
│ │ └── src/index.js
|
|
515
|
+
│ └── worker/
|
|
516
|
+
│ ├── package.json
|
|
517
|
+
│ └── src/index.js
|
|
518
|
+
└── .gitignore # includes .cloudgrid/
|
|
519
|
+
```
|
|
520
|
+
|
|
521
|
+
Use `source.path: .` for a flat project where the code lives at the root.
|
|
522
|
+
|
|
523
|
+
---
|
|
524
|
+
|
|
525
|
+
## 7. `needs:` vs `requires:` — and the live deploy mechanics (READ THIS)
|
|
526
|
+
|
|
527
|
+
Bake these into any yaml you author:
|
|
528
|
+
|
|
529
|
+
- **`needs:` is canonical and it injects.** `needs: {database: true}` provisions
|
|
530
|
+
Mongo and injects the connection env vars — `DATABASE_MONGODB_URL` plus the
|
|
531
|
+
legacy `MONGODB_URL` alias. Likewise `cache: true` → `CACHE_REDIS_URL` (+legacy
|
|
532
|
+
`REDIS_URL`), `vector: pgvector` → `VECTOR_PGVECTOR_URL` (+legacy
|
|
533
|
+
`PGVECTOR_URL`), and so on across the nine needs (see §5). An app reading the
|
|
534
|
+
legacy names keeps working because the aliases are injected alongside.
|
|
535
|
+
(*Historical note: the deployer once did not inject from `needs:` — platform
|
|
536
|
+
issue #1527, now fixed and verified live. The old `requires:` workaround is
|
|
537
|
+
obsolete.*)
|
|
538
|
+
- **`requires:` is the deprecated v1 alias** — `requires: [mongodb]` /
|
|
539
|
+
`requires: [redis]`. The CLI warns it's deprecated. **Don't author new yaml with
|
|
540
|
+
it**; use `needs:` instead.
|
|
541
|
+
- **`needs:` and `requires:` together are HARD-REJECTED** by the validator ("use
|
|
542
|
+
one or the other"). So author **`needs:`-only** — never set both.
|
|
543
|
+
- **Static single-file pages deploy as inspirations** via `gridctl_drop` — instant,
|
|
544
|
+
synchronous, works on the hosted edition and any client.
|
|
545
|
+
- **A `cloudgrid.yaml` with `services:` makes it a runtime** — async build,
|
|
546
|
+
**local edition only** (Claude Desktop/Code or the CLI, not hosted). `plug`
|
|
547
|
+
returns `status: building`; poll status until live, then use the returned live
|
|
548
|
+
URL. Re-plug the same entity to update the same URL.
|
|
549
|
+
- **Read injected env vars lazily** (inside a getter/handler), never at module top
|
|
550
|
+
level — a top-level read of `DATABASE_MONGODB_URL` fails `next build`, which
|
|
551
|
+
imports the module for route analysis before the grid injects the var.
|
|
552
|
+
|
|
553
|
+
The canonical DB shape (mirrors the `app-with-data` template):
|
|
554
|
+
|
|
555
|
+
```yaml
|
|
556
|
+
name: my-app
|
|
557
|
+
services:
|
|
558
|
+
web:
|
|
559
|
+
type: nextjs
|
|
560
|
+
path: /
|
|
561
|
+
needs:
|
|
562
|
+
database: true
|
|
563
|
+
```
|
|
564
|
+
|
|
565
|
+
---
|
|
566
|
+
|
|
567
|
+
*This doc is the agent-facing distribution of the platform's canonical
|
|
568
|
+
`cloudgrid-yaml-reference.md`. See also `gridctl_fetch("doc", "capability-map")`
|
|
569
|
+
for matching a user intent to a template + the `needs:` injection status.*
|