Skip to content
377 linesCodeBlameRaw
1// Everything g1t deploys to Cloudflare, in one place. Read by
2// scripts/deploy.mjs (plan, deploy), deploy/self-host/configs.mjs (what a
3// self-hosted installation runs) and the tests in scripts/deploy/.
4// docs.g1t.sh/guides/deploy-to-cloudflare/ explains each field and how to
5// add a unit.
6//
7// What is written here is what the Wrangler configs cannot say. The rest is
8// read from each unit's wrangler.jsonc, never copied: its D1 databases and
9// migrations, the services it binds to, its KV, R2, queues and routes. The
10// shared crates and packages a unit is built from are read from Cargo's and
11// npm's workspace metadata. `worker` and `d1` are written here too, so the
12// file reads as an inventory, and a test checks they match the configs.
13{
14 // Deployed in this order. A stage starts only when the one before it
15 // succeeded. A unit binds only to units in its own stage or an earlier
16 // one (a test checks it), so new code never calls a service that has
17 // not shipped yet. Within a stage, units go out in parallel.
18 //
19 // migrations: every pending D1 migration, before any code.
20 // core: the services, reached through service bindings.
21 // edge: public endpoints other than the site: API, MCP, g1t.page, status
22 // (models is public too, but in core: agents binds to it).
23 // front: the site, sudo and the docs.
24 "stages": ["migrations", "core", "edge", "front"],
25
26 // Names for the resources the configs refer to by id, for setup
27 // commands and the docs. A test checks every KV id in a config is here.
28 "resources": {
29 "kv": {
30 "16a4232cb746418db53782aa068be693": "g1t-actions-blobs",
31 "e627b571f07047e187c03e1fc2b3bbdd": "g1t-avatars",
32 "14bc5c233d4c46a5bbf23b5367cce5fd": "g1t-domains",
33 "be765052d0124c2a935b3db4dff99f1f": "g1t-repos-git-cache"
34 }
35 },
36
37 // Each deployable unit, by short name (`--only events,web`).
38 //
39 // kind: rust-worker (worker-build), ts-worker (Wrangler bundles it),
40 // react-router (vite build first), astro (astro build first).
41 // secrets: names only; set with `npx wrangler secret put NAME` in its folder.
42 // setup: one-time steps no config can say, for a first deploy.
43 // self_host: what deploy/self-host does with it: "run" (in the one
44 // workerd), "off" (bound to the off Worker), "separate" (a
45 // process of its own), or "none".
46 // inputs: files outside its folder it is built from that no workspace
47 // metadata names (a test finds such imports).
48 // image: a Containers image (the guide's "The runner's images"):
49 // dockerfile the image a deploy ships: the base plus the binary
50 // crate the crate that binary is built from (and what it uses)
51 // base { context: the base's folder, lock: the file that
52 // records the base that was pushed }; the base is
53 // rebuilt only when its folder changes
54 // repository where both are pushed in Cloudflare's registry
55 "units": {
56 "events": {
57 "path": "services/events",
58 "kind": "rust-worker",
59 "worker": "g1t-events",
60 "d1": { "database": "g1t-events", "migrations": "migrations" },
61 "stage": "core",
62 "secrets": [],
63 "setup": [
64 "The dead-letter queue every queue consumer sends what it gave up on to, before any unit that names it deploys: npx wrangler queues create g1t-events-dlq"
65 ],
66 "self_host": "run"
67 },
68 "identity": {
69 "path": "services/identity",
70 "kind": "rust-worker",
71 "worker": "g1t-identity",
72 "d1": { "database": "g1t", "migrations": "migrations" },
73 "stage": "core",
74 "secrets": ["GITHUB_APP_CLIENT_SECRET", "IDENTITY_KEY"],
75 "setup": ["Email Sending on g1t.sh (the EMAIL binding)"],
76 "self_host": "run"
77 },
78 "repos": {
79 "path": "services/repos",
80 "kind": "rust-worker",
81 "worker": "g1t-repos",
82 "d1": { "database": "g1t-repos", "migrations": "migrations" },
83 "stage": "core",
84 "secrets": ["REPOS_KEY"],
85 "setup": [
86 "The Artifacts namespace `g1t` (the ARTIFACTS binding)",
87 "The R2 bucket `g1t-git-packs` (GIT_PACKS) with its lifecycle rule: `npx wrangler r2 bucket create g1t-git-packs`, then `npx wrangler r2 bucket lifecycle add g1t-git-packs expire-packs packs/ --expire-days 7 --abort-multipart-days 1`",
88 "The R2 bucket for nightly backups: npx wrangler r2 bucket create g1t-backups"
89 ],
90 "self_host": "run"
91 },
92 "work": {
93 "path": "services/work",
94 "kind": "rust-worker",
95 "worker": "g1t-work",
96 "d1": { "database": "g1t-work", "migrations": "migrations" },
97 "stage": "core",
98 "secrets": [],
99 "self_host": "run"
100 },
101 "search": {
102 "path": "services/search",
103 "kind": "rust-worker",
104 "worker": "g1t-search",
105 "d1": { "database": "g1t-search", "migrations": "migrations" },
106 "stage": "core",
107 "secrets": [],
108 "self_host": "run"
109 },
110 "projects": {
111 "path": "services/projects",
112 "kind": "ts-worker",
113 "worker": "g1t-projects",
114 "d1": { "database": "g1t-projects", "migrations": "migrations" },
115 "stage": "core",
116 "secrets": [],
117 "self_host": "run"
118 },
119 "chat": {
120 "path": "services/chat",
121 "kind": "ts-worker",
122 "worker": "g1t-chat",
123 "d1": { "database": "g1t-chat", "migrations": "migrations" },
124 "stage": "core",
125 "secrets": [],
126 "setup": [
127 "The D1 database, before the first deploy: npx wrangler d1 create g1t-chat, then put its id in services/chat/wrangler.jsonc"
128 ],
129 "self_host": "run"
130 },
131 // Docs mode's service, which also hosts artifacts (folios). Its
132 // Worker is g1t-docs-service: g1t-docs is the documentation site
133 // (apps/docs).
134 "docs-service": {
135 "path": "services/docs",
136 "kind": "ts-worker",
137 "worker": "g1t-docs-service",
138 "d1": { "database": "g1t-docs", "migrations": "migrations" },
139 "stage": "core",
140 "secrets": [],
141 "setup": [
142 "The D1 database, before the first deploy: npx wrangler d1 create g1t-docs, then put its id in services/docs/wrangler.jsonc",
143 "The R2 bucket for files in pages: npx wrangler r2 bucket create g1t-docs-files",
144 "The queue the events service sends it merges and pushes on (pages whose cited code changed, projects' docs): npx wrangler queues create g1t-events-docs. The service also sends its own backfill jobs to it (JOBS)",
145 "The Vectorize index agents recall Docs from: npx wrangler vectorize create g1t-docs --dimensions=768 --metric=cosine, with string metadata indexes on workspace_id and space_id (npx wrangler vectorize create-metadata-index g1t-docs --property-name=<name> --type=string)",
146 "The Vectorize index for artifacts (folios), before the first deploy with FOLIO_VECTORS: npx wrangler vectorize create g1t-folios --dimensions=768 --metric=cosine, with string metadata indexes on workspace_id, scope and kind (npx wrangler vectorize create-metadata-index g1t-folios --property-name=<name> --type=string). Without it, artifacts are searched and recalled by words"
147 ],
148 "self_host": "run"
149 },
150 "notify": {
151 "path": "services/notify",
152 "kind": "ts-worker",
153 "worker": "g1t-notify",
154 // No D1: each person's feed keeps its own state in its Durable
155 // Object's SQLite storage.
156 "stage": "core",
157 // The private half of the VAPID key pair browser pushes are signed
158 // with; without it, notifications are live in open tabs only.
159 "secrets": ["VAPID_PRIVATE_KEY"],
160 "setup": [
161 "The VAPID key pair for browser push: `node scripts/ops/vapid-keys.mjs` prints both halves and stores nothing. Put the public half in VAPID_PUBLIC_KEY in services/notify/wrangler.jsonc, and the private half with `npx wrangler secret put VAPID_PRIVATE_KEY` in services/notify"
162 ],
163 "self_host": "run"
164 },
165 "agents": {
166 "path": "services/agents",
167 "kind": "ts-worker",
168 "worker": "g1t-agents",
169 "d1": { "database": "g1t-agents", "migrations": "migrations" },
170 "stage": "core",
171 "secrets": [],
172 "setup": [
173 "The D1 database, before the first deploy: npx wrangler d1 create g1t-agents, then put its id in services/agents/wrangler.jsonc"
174 ],
175 // Replies route and gate model work exactly as runs do, with the
176 // runner's own modules: its routing policy and who may use hosted models.
177 "inputs": ["services/runner/src/model-env.ts", "services/runner/src/hosted.ts"],
178 "self_host": "run"
179 },
180 "billing": {
181 "path": "services/billing",
182 "kind": "rust-worker",
183 "worker": "g1t-billing",
184 "d1": { "database": "g1t-billing", "migrations": "migrations" },
185 "stage": "core",
186 "secrets": ["STRIPE_SECRET_KEY", "STRIPE_WEBHOOK_SECRET", "CLOUDFLARE_USAGE_TOKEN"],
187 "self_host": "run"
188 },
189 "integrations": {
190 "path": "services/integrations",
191 "kind": "rust-worker",
192 "worker": "g1t-integrations",
193 "d1": { "database": "g1t-integrations", "migrations": "migrations" },
194 "stage": "core",
195 "secrets": ["INTEGRATIONS_KEY", "GITHUB_APP_PRIVATE_KEY", "GITHUB_APP_WEBHOOK_SECRET"],
196 "self_host": "run"
197 },
198 "webhooks": {
199 "path": "services/webhooks",
200 "kind": "rust-worker",
201 "worker": "g1t-webhooks",
202 "d1": { "database": "g1t-webhooks", "migrations": "migrations" },
203 "stage": "core",
204 "secrets": ["WEBHOOKS_KEY"],
205 "self_host": "run"
206 },
207 "actions": {
208 "path": "services/actions",
209 "kind": "rust-worker",
210 "worker": "g1t-actions",
211 "d1": { "database": "g1t-actions", "migrations": "migrations" },
212 "stage": "core",
213 "secrets": ["ACTIONS_KEY"],
214 "self_host": "run"
215 },
216 "packages": {
217 "path": "services/packages",
218 "kind": "rust-worker",
219 "worker": "g1t-packages",
220 "d1": { "database": "g1t-packages", "migrations": "migrations" },
221 "stage": "core",
222 "secrets": ["PACKAGES_TOKEN_SECRET", "R2_ACCESS_KEY_ID", "R2_SECRET_ACCESS_KEY"],
223 "setup": [
224 "The D1 database: npx wrangler d1 create g1t-packages, its id in services/packages/wrangler.jsonc",
225 "The R2 bucket for packages' files: npx wrangler r2 bucket create g1t-packages",
226 "The events queue: npx wrangler queues create g1t-events-packages",
227 "For signed downloads: an R2 API token with read access to g1t-packages, as R2_ACCESS_KEY_ID and R2_SECRET_ACCESS_KEY"
228 ],
229 "self_host": "run"
230 },
231 "security": {
232 "path": "services/security",
233 "kind": "rust-worker",
234 "worker": "g1t-security",
235 "d1": { "database": "g1t-security", "migrations": "migrations" },
236 "stage": "core",
237 "secrets": [],
238 "self_host": "run"
239 },
240 "deployments": {
241 "path": "services/deployments",
242 "kind": "ts-worker",
243 "worker": "g1t-deployments",
244 "d1": { "database": "g1t-deployments", "migrations": "migrations" },
245 "stage": "core",
246 "secrets": ["CLOUDFLARE_API_TOKEN"],
247 "setup": [
248 "Workers for Platforms, and the dispatch namespace: scripts/setup-deployments.sh",
249 "Custom domains (Cloudflare for SaaS on g1t.page): scripts/setup-custom-domains.sh"
250 ],
251 "self_host": "run"
252 },
253 "runner": {
254 "path": "services/runner",
255 "kind": "ts-worker",
256 "worker": "g1t-runner",
257 "stage": "core",
258 "secrets": ["AI_GATEWAY_TOKEN"],
259 "setup": [
260 "Containers on the account; Docker on the machine that builds a new image",
261 "The base image, once: node scripts/deploy.mjs build-base"
262 ],
263 "image": {
264 "dockerfile": "services/runner/Dockerfile",
265 // The binary the image adds to its base (scripts/build-runner.mjs).
266 "crate": "g1t-runner",
267 "base": { "context": "services/runner/base", "lock": "services/runner/base.json" },
268 "repository": "g1t-runner"
269 },
270 "self_host": "off"
271 },
272 "context": {
273 "path": "services/context",
274 "kind": "ts-worker",
275 "worker": "g1t-context",
276 "d1": { "database": "g1t-context", "migrations": "migrations" },
277 "stage": "core",
278 "secrets": [],
279 "setup": [
280 "The Vectorize index: npx wrangler vectorize create g1t-context --dimensions=768 --metric=cosine, with metadata indexes on workspace, kind, project and private"
281 ],
282 "self_host": "off"
283 },
284 "og": {
285 "path": "services/og",
286 "kind": "ts-worker",
287 "worker": "g1t-og",
288 "stage": "core",
289 "secrets": [],
290 "setup": ["Browser Rendering on the account (the BROWSER binding)"],
291 // The roadmap cards read the site's roadmap.
292 "inputs": ["apps/web/app/lib/roadmap.ts"],
293 "self_host": "none"
294 },
295 "api": {
296 "path": "apps/api",
297 "kind": "rust-worker",
298 "worker": "g1t-api",
299 "stage": "edge",
300 // ACTIONS_OIDC_KEY signs workflow jobs' OIDC tokens; without it the
301 // issuer answers 404 and jobs are not offered tokens.
302 // ACTIONS_OIDC_KEY_PREVIOUS only while rotating (the guide's "OIDC
303 // tokens for workflow jobs").
304 "secrets": ["ACTIONS_OIDC_KEY"],
305 "setup": [
306 "The OIDC signing key for workflow jobs: an RSA key made with `openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048`, stored with `npx wrangler secret put ACTIONS_OIDC_KEY` (docs.g1t.sh/guides/deploy-to-cloudflare/#oidc-tokens-for-workflow-jobs)",
307 "The actions cache bucket's lifecycle rule limited to `c/`, so artifacts under `a/` are kept their retention-days (docs.g1t.sh/guides/deploy-to-cloudflare/#a-first-deploy-to-a-new-account)"
308 ],
309 "self_host": "separate"
310 },
311 "models": {
312 "path": "services/models",
313 "kind": "ts-worker",
314 "worker": "g1t-models",
315 // Core, though it is public at models.g1t.sh: the agents service
316 // reaches it by service binding for chat replies. It binds only to
317 // core services itself.
318 "stage": "core",
319 "secrets": ["AI_GATEWAY_TOKEN"],
320 "setup": ["The AI Gateway `g1t`"],
321 // Not run self-hosted, but bound to the off Worker: agents binds to it.
322 "self_host": "off"
323 },
324 "pages": {
325 "path": "services/pages",
326 "kind": "ts-worker",
327 "worker": "g1t-pages",
328 "stage": "edge",
329 "secrets": [],
330 "setup": ["A proxied wildcard DNS record on g1t.page (`*`, AAAA 100::): scripts/setup-deployments.sh"],
331 "self_host": "none"
332 },
333 "status": {
334 "path": "apps/status",
335 "kind": "ts-worker",
336 "worker": "g1t-status",
337 "d1": { "database": "g1t-status", "migrations": "migrations" },
338 "stage": "edge",
339 "secrets": ["STATUS_SECRET"],
340 "setup": ["Email Sending on g1t.sh (the EMAIL binding)"],
341 "self_host": "separate"
342 },
343 "web": {
344 "path": "apps/web",
345 "kind": "react-router",
346 "worker": "g1t",
347 "stage": "front",
348 // USERCONTENT_KEY signs the short-lived addresses of private
349 // repositories' files on g1tusercontent.com; without it they are
350 // served from g1t.sh instead.
351 "secrets": ["USERCONTENT_KEY"],
352 "setup": [
353 "The R2 bucket g1t-downloads, for the self-hosted runner's releases: npx wrangler r2 bucket create g1t-downloads",
354 "The zone g1tusercontent.com on the account; the Worker's custom domain on it is made by the deploy",
355 "The key for private files' addresses: `node -e \"console.log(require('crypto').randomBytes(32).toString('hex'))\" | npx wrangler secret put USERCONTENT_KEY` in apps/web"
356 ],
357 "self_host": "run"
358 },
359 "sudo": {
360 "path": "apps/sudo",
361 "kind": "react-router",
362 "worker": "g1t-sudo",
363 "stage": "front",
364 "secrets": [],
365 "setup": ["A Cloudflare Access application on sudo.g1t.sh; its AUD tag is ACCESS_AUD"],
366 "self_host": "none"
367 },
368 "docs": {
369 "path": "apps/docs",
370 "kind": "astro",
371 "worker": "g1t-docs",
372 "stage": "front",
373 "secrets": [],
374 "self_host": "none"
375 }
376 }
377}