Skip to content

Where people and agents ship software together. The open-source git platform for the whole job: issues, agents, checks and deploys to the edge.

538 linesCodeBlameRaw
1/**
2 * The Architecture section's diagrams (content/docs/architecture). Each one
3 * shows code that exists on main: when the code changes, change the
4 * diagram in the same pull request, as its page says.
5 */
6import type { Graph, Sequence, Spec } from "./engine.ts";
7
8// ── Overview ───────────────────────────────────────────────────────────────
9
10const CORE_X = [20, 206, 392, 578, 764];
11const CORE_Y = [312, 364, 416, 468];
12const core = (id: string, title: string, sub: string, col: number, row: number, tone?: "accent") => ({
13 id,
14 title,
15 sub,
16 x: CORE_X[col],
17 y: CORE_Y[row],
18 w: 172,
19 h: 44,
20 tone,
21});
22
23const platformMap: Graph = {
24 kind: "graph",
25 caption: "g1t at a glance: clients reach four public Workers, which reach the core services through service bindings",
26 width: 960,
27 height: 664,
28 groups: [
29 { x: 4, y: 4, w: 952, h: 92, label: "Clients", tone: "default" },
30 { x: 4, y: 140, w: 952, h: 96, label: "Edge: the public Workers", tone: "accent" },
31 { id: "core", x: 4, y: 280, w: 952, h: 244, label: "Core services: reached through service bindings" },
32 { id: "platform", x: 4, y: 566, w: 952, h: 94, label: "Platform: Cloudflare’s primitives, each behind a port" },
33 ],
34 nodes: [
35 { id: "browser", x: 16, y: 34, w: 168, h: 50, title: "Browsers and apps", sub: "site, desktop, phone", tone: "external" },
36 { id: "git", x: 206, y: 34, w: 168, h: 50, title: "git", sub: "clone, fetch, push", tone: "external" },
37 { id: "agents", x: 396, y: 34, w: 168, h: 50, title: "Coding agents, CLI", sub: "REST, MCP, OAuth", tone: "external" },
38 { id: "runners", x: 586, y: 34, w: 168, h: 50, title: "Self-hosted runners", sub: "outbound HTTPS only", tone: "external" },
39 { id: "visitors", x: 776, y: 34, w: 168, h: 50, title: "Visitors to your apps", sub: "*.g1t.page, your domains", tone: "external" },
40
41 { id: "web", x: 111, y: 170, w: 168, h: 50, title: "Site", sub: "apps/web · g1t.sh", tone: "accent" },
42 { id: "status", x: 301, y: 170, w: 168, h: 50, title: "Status", sub: "apps/status", tone: "accent" },
43 { id: "api", x: 491, y: 170, w: 168, h: 50, title: "API and MCP", sub: "apps/api", tone: "accent" },
44 { id: "pages", x: 776, y: 170, w: 168, h: 50, title: "Pages", sub: "services/pages", tone: "accent" },
45
46 core("identity", "identity", "Rust", 0, 0),
47 core("repos", "repos", "Rust", 1, 0),
48 core("work", "work", "Rust", 2, 0),
49 core("events", "events", "Rust · the bus", 3, 0, "accent"),
50 core("actions", "actions", "Rust", 4, 0),
51 core("runner", "runner", "TypeScript · sandboxes", 0, 1),
52 core("deployments", "deployments", "TypeScript", 1, 1),
53 core("packages", "packages", "Rust", 2, 1),
54 core("security", "security", "Rust", 3, 1),
55 core("search", "search", "Rust", 4, 1),
56 core("projects", "projects", "TypeScript", 0, 2),
57 core("chat", "chat", "TypeScript", 1, 2),
58 core("artifacts", "artifacts", "TypeScript", 2, 2),
59 core("notify", "notify", "TypeScript", 3, 2),
60 core("telemetry", "telemetry", "TypeScript", 4, 2),
61 core("billing", "billing", "Rust", 0, 3),
62 core("integrations", "integrations", "Rust", 1, 3),
63 core("webhooks", "webhooks", "Rust", 2, 3),
64 core("og", "og", "TypeScript · cards", 3, 3),
65
66 { id: "d1", x: 13, y: 598, w: 144, h: 50, title: "D1", sub: "a database each", shape: "store" },
67 { id: "do", x: 171, y: 598, w: 144, h: 50, title: "Durable Objects", sub: "live rooms" },
68 { id: "queues", x: 329, y: 598, w: 144, h: 50, title: "Queues", sub: "the event bus" },
69 { id: "blobs", x: 487, y: 598, w: 144, h: 50, title: "R2 and KV", sub: "packs, packages, files", shape: "store" },
70 { id: "gitstore", x: 645, y: 598, w: 144, h: 50, title: "Artifacts", sub: "git repositories", shape: "store" },
71 { id: "containers", x: 803, y: 598, w: 144, h: 50, title: "Containers", sub: "one sandbox per job" },
72 ],
73 edges: [
74 { from: "browser", to: "web", label: "HTTPS" },
75 { from: "git", to: "web", label: "smart HTTP" },
76 { from: "agents", to: "api", label: "REST, MCP" },
77 { from: "runners", to: "api", label: "long poll" },
78 { from: "visitors", to: "pages" },
79 { from: "web", to: "core", toAt: (195 - 480) / 952, label: "`POST /rpc/<method>`", tone: "accent" },
80 { from: "api", to: "core", toAt: (575 - 480) / 952, label: "`POST /rpc/<method>`", tone: "accent" },
81 { from: "core", to: "platform", label: "bindings" },
82 ],
83};
84
85// ── Requests ───────────────────────────────────────────────────────────────
86
87const pageRequest: Sequence = {
88 kind: "sequence",
89 caption: "A signed-in page: the viewer is resolved once, then every loader calls its service at the same time",
90 actors: [
91 { id: "browser", title: "Browser", tone: "external" },
92 { id: "web", title: "Site", sub: "apps/web", tone: "accent" },
93 { id: "identity", title: "identity", sub: "services/identity" },
94 { id: "work", title: "work", sub: "services/work" },
95 { id: "repos", title: "repos", sub: "services/repos" },
96 ],
97 steps: [
98 { from: "browser", to: "web", label: "`GET /acme/app/pull/12`, `g1t_session`" },
99 { from: "web", to: "identity", label: "`user_for_session`" },
100 { from: "identity", to: "web", label: "the user: workspaces, roles, grants", reply: true },
101 { phase: "Loaders start together" },
102 { from: "web", to: "work", label: "`POST /rpc/<method>` with the viewer" },
103 { from: "web", to: "repos", label: "`POST /rpc/<method>` with the viewer" },
104 { note: "each decides with `access::can`, without asking identity", over: ["work", "repos"] },
105 { from: "work", to: "web", label: "`{ ok, value }`, its D1 time", reply: true },
106 { from: "repos", to: "web", label: "`{ ok, value }`, its D1 time", reply: true },
107 { from: "web", to: "browser", label: "HTML, with `Server-Timing`", reply: true },
108 ],
109};
110
111const apiRequest: Sequence = {
112 kind: "sequence",
113 caption: "An MCP tool call: the same token check, scopes, and service calls as the REST API",
114 actors: [
115 { id: "agent", title: "Coding agent", sub: "MCP client", tone: "external" },
116 { id: "api", title: "API and MCP", sub: "apps/api", tone: "accent" },
117 { id: "identity", title: "identity", sub: "services/identity" },
118 { id: "work", title: "work", sub: "services/work" },
119 ],
120 steps: [
121 { from: "agent", to: "api", label: "`tools/call` `pull_request`, `Bearer g1t_…`" },
122 { from: "api", to: "api", label: "rate limit by token hash" },
123 { from: "api", to: "identity", label: "`user_for_access_token`" },
124 { from: "identity", to: "api", label: "the user, the token’s scopes", reply: true },
125 { from: "api", to: "api", label: "the gate: do the scopes allow it?" },
126 { from: "api", to: "work", label: "`POST /rpc/<method>` with the viewer" },
127 { from: "work", to: "api", label: "`{ ok, value }` in camelCase", reply: true },
128 { from: "api", to: "agent", label: "JSON-RPC result, in snake_case", reply: true },
129 ],
130};
131
132// ── Git ────────────────────────────────────────────────────────────────────
133
134const pushPath: Sequence = {
135 kind: "sequence",
136 caption: "A push: checked before anything is stored, answered as soon as the refs move, announced afterwards",
137 width: 900,
138 actors: [
139 { id: "git", title: "git", sub: "your machine", tone: "external" },
140 { id: "web", title: "Site", sub: "g1t.sh", tone: "accent" },
141 { id: "repos", title: "repos", sub: "services/repos", tone: "accent" },
142 { id: "identity", title: "identity" },
143 { id: "work", title: "work" },
144 { id: "store", title: "Artifacts", sub: "the git store", tone: "store" },
145 { id: "events", title: "events" },
146 ],
147 steps: [
148 { from: "git", to: "web", label: "`POST …/git-receive-pack`" },
149 { from: "web", to: "repos", label: "passed on whole" },
150 { from: "repos", to: "identity", label: "`user_for_git_credentials`" },
151 { from: "identity", to: "repos", label: "the user, or no one", reply: true },
152 { from: "repos", to: "work", label: "`ref_rules`" },
153 { phase: "Checks read the pack once, side by side" },
154 { from: "repos", to: "repos", label: "workflow gate · rules · sizes · push protection" },
155 { from: "repos", to: "store", label: "the pack, with a store credential" },
156 { from: "store", to: "repos", label: "report-status", reply: true },
157 { from: "repos", to: "repos", label: "bump `refs_version`" },
158 { from: "repos", to: "git", label: "the report, with `Server-Timing`", reply: true },
159 { phase: "After the answer, in waitUntil" },
160 { from: "repos", to: "store", label: "`log`: did each ref move?" },
161 { from: "repos", to: "events", label: "publish `git.push`", async: true },
162 ],
163};
164
165const clonePath: Graph = {
166 kind: "graph",
167 caption: "A clone or fetch: answered from the nearest cache that can, and from the git store only when none can",
168 width: 930,
169 height: 316,
170 nodes: [
171 { id: "git", x: 10, y: 140, w: 130, title: "git", sub: "clone, fetch", tone: "external" },
172 { id: "web", x: 200, y: 140, w: 130, title: "Site", sub: "g1t.sh" },
173 { id: "repos", x: 390, y: 140, w: 140, title: "repos", sub: "upload-pack", tone: "accent" },
174 { id: "colo", x: 740, y: 12, w: 180, title: "Colo cache", sub: "refs 60 s, objects forever" },
175 { id: "kv", x: 740, y: 92, w: 180, title: "KV, sealed", sub: "refs, store credentials", shape: "store" },
176 { id: "packs", x: 740, y: 172, w: 180, title: "R2 `g1t-git-packs`", sub: "fresh-clone packs", shape: "store" },
177 { id: "store", x: 740, y: 252, w: 180, title: "Artifacts", sub: "the git store", shape: "store" },
178 ],
179 edges: [
180 { from: "git", to: "web" },
181 { from: "web", to: "repos", label: "`REPOS.fetch`" },
182 ...(
183 [
184 ["colo", "ref listing, this colo", 38],
185 ["kv", "ref listing, shared", 121],
186 ["packs", "a fresh clone", 201],
187 ["store", "everything else", 281],
188 ] as const
189 ).map(([to, label, y]) => ({
190 from: "repos",
191 to,
192 fromSide: "right" as const,
193 toSide: "left" as const,
194 bend: 570,
195 label,
196 labelAt: [655, y] as [number, number],
197 tone: to === "store" ? ("accent" as const) : undefined,
198 })),
199 ],
200};
201
202// ── Events ─────────────────────────────────────────────────────────────────
203
204const SUBSCRIBERS = [
205 "work",
206 "runner",
207 "actions",
208 "webhooks",
209 "security",
210 "search",
211 "deployments",
212 "projects",
213 "repos",
214 "packages",
215 "billing",
216 "integrations",
217 "artifacts",
218];
219
220const eventFanout: Graph = {
221 kind: "graph",
222 caption: "One publish, written once to the log, then copied to a queue for each service that subscribes",
223 width: 960,
224 height: 500,
225 groups: [
226 { x: 4, y: 4, w: 176, h: 400, label: "Publishers" },
227 { x: 778, y: 4, w: 178, h: 492, label: "Subscriber queues", tone: "accent" },
228 ],
229 nodes: [
230 { id: "p-repos", x: 16, y: 40, w: 152, h: 46, title: "repos", sub: "`git.push`, `repo.*`" },
231 { id: "p-work", x: 16, y: 110, w: 152, h: 46, title: "work", sub: "`pull.*`, `work_item.*`" },
232 { id: "p-actions", x: 16, y: 180, w: 152, h: 46, title: "actions", sub: "`workflow.*`" },
233 { id: "p-identity", x: 16, y: 250, w: 152, h: 46, title: "identity", sub: "`workspace.*`" },
234 { id: "p-more", x: 16, y: 320, w: 152, h: 46, title: "and the rest", sub: "any service" },
235 { id: "publish", x: 222, y: 180, w: 150, h: 46, title: "events", sub: "`publish` → `sendBatch`", tone: "accent" },
236 { id: "bus", x: 410, y: 180, w: 150, h: 46, title: "Queue `g1t-events`", sub: "batches of 100" },
237 { id: "deliver", x: 600, y: 180, w: 150, h: 46, title: "events", sub: "`deliver`", tone: "accent" },
238 { id: "log", x: 600, y: 40, w: 150, h: 50, title: "D1 `g1t-events`", sub: "the log, by `seq`", shape: "store" },
239 { id: "notify", x: 600, y: 320, w: 150, h: 46, title: "notify", sub: "live pages, inbox" },
240 { id: "dlq", x: 410, y: 320, w: 150, h: 46, title: "Dead letters", sub: "`g1t-events-dlq`", tone: "warn" },
241 ...SUBSCRIBERS.map((name, i) => ({ id: `s-${name}`, x: 790, y: 40 + i * 35, w: 154, h: 29, title: name })),
242 ],
243 edges: [
244 ...["p-repos", "p-work", "p-actions", "p-identity", "p-more"].map((from) => ({
245 from,
246 to: "publish",
247 fromSide: "right" as const,
248 toSide: "left" as const,
249 bend: 196,
250 })),
251 { from: "publish", to: "bus", async: true },
252 { from: "bus", to: "deliver", async: true },
253 { from: "deliver", to: "log", label: "`INSERT OR IGNORE`" },
254 { from: "deliver", to: "notify", label: "`repo_changed`" },
255 { from: "bus", to: "dlq", label: "after 3 tries", async: true, tone: "warn" },
256 ...SUBSCRIBERS.map((name) => ({ from: "deliver", to: `s-${name}`, fromSide: "right" as const, toSide: "left" as const, bend: 770, async: true })),
257 ],
258};
259
260const followEvents: Sequence = {
261 kind: "sequence",
262 caption: "Following a repository’s events: a long poll that resumes from the last event it saw",
263 width: 680,
264 actors: [
265 { id: "cli", title: "g1t CLI", sub: "`work events --follow`", tone: "external" },
266 { id: "api", title: "API", sub: "apps/api", tone: "accent" },
267 { id: "events", title: "events", sub: "services/events" },
268 ],
269 steps: [
270 { from: "cli", to: "api", label: "`list_events` `after=evt_…`, `wait=25`" },
271 { from: "api", to: "events", label: "`list` after the cursor" },
272 { from: "events", to: "api", label: "nothing yet", reply: true },
273 { note: "asks again about once a second, for up to 25 s", over: ["api", "events"] },
274 { from: "api", to: "events", label: "`list` after the cursor" },
275 { from: "events", to: "api", label: "events, in `seq` order", reply: true },
276 { from: "api", to: "cli", label: "events, oldest first", reply: true },
277 { from: "cli", to: "cli", label: "ask again after the last id" },
278 ],
279};
280
281// ── Work ───────────────────────────────────────────────────────────────────
282
283const mergeQueue: Graph = {
284 kind: "graph",
285 caption: "The merge queue: each entry is tested on top of the entries ahead of it, all at once, and lands in order",
286 width: 920,
287 height: 318,
288 nodes: [
289 { id: "main", x: 10, y: 40, w: 140, title: "`main`", sub: "where it stands", tone: "store" },
290 { id: "c1", x: 200, y: 40, w: 160, title: "`g1t-queue/…41`", sub: "main + #41", tone: "accent" },
291 { id: "c2", x: 380, y: 40, w: 160, title: "`g1t-queue/…42`", sub: "+ #41 + #42", tone: "accent" },
292 { id: "c3", x: 560, y: 40, w: 160, title: "`g1t-queue/…43`", sub: "+ #41 + #42 + #43", tone: "accent" },
293 { id: "c4", x: 740, y: 40, w: 170, title: "`g1t-queue/…44`", sub: "+ #41 + #42 + #44", tone: "accent" },
294 { id: "k1", x: 200, y: 150, w: 160, h: 44, title: "required checks pass", tone: "success" },
295 { id: "k2", x: 380, y: 150, w: 160, h: 44, title: "required checks pass", tone: "success" },
296 { id: "k3", x: 560, y: 150, w: 160, h: 44, title: "a required check fails", tone: "warn" },
297 { id: "k4", x: 740, y: 150, w: 170, h: 44, title: "required checks pass", tone: "success" },
298 { id: "l1", x: 200, y: 254, w: 160, h: 44, title: "#41 lands first" },
299 { id: "l2", x: 380, y: 254, w: 160, h: 44, title: "#42 lands next" },
300 { id: "l3", x: 560, y: 254, w: 160, h: 44, title: "#43 leaves the queue", tone: "warn" },
301 { id: "l4", x: 740, y: 254, w: 170, h: 44, title: "#44 lands third" },
302 ],
303 edges: [
304 { from: "main", to: "c1" },
305 { from: "c1", to: "c2" },
306 { from: "c2", to: "c3" },
307 { from: "c2", to: "c4", fromSide: "top", toSide: "top", bend: 14, async: true, label: "retested without #43", labelAt: [640, 14] },
308 { from: "c1", to: "k1", label: "`merge_group`" },
309 { from: "c2", to: "k2", label: "`merge_group`" },
310 { from: "c3", to: "k3", label: "`merge_group`", tone: "warn" },
311 { from: "c4", to: "k4", label: "`merge_group`" },
312 { from: "k1", to: "l1", tone: "success" },
313 { from: "k2", to: "l2", tone: "success" },
314 { from: "k3", to: "l3", tone: "warn" },
315 { from: "k4", to: "l4", tone: "success" },
316 ],
317};
318
319const coordination: Sequence = {
320 kind: "sequence",
321 caption: "Two agents on one repository: sessions and claims are advisory, and each learns of the other from events",
322 width: 860,
323 actors: [
324 { id: "a", title: "Agent A", sub: "its own harness", tone: "external" },
325 { id: "api", title: "API and MCP", sub: "apps/api" },
326 { id: "work", title: "work", sub: "services/work", tone: "accent" },
327 { id: "events", title: "events" },
328 { id: "b", title: "Agent B", sub: "`--follow`", tone: "external" },
329 ],
330 steps: [
331 { from: "a", to: "api", label: "`session` `start`, `harness_session_id`" },
332 { from: "api", to: "work", label: "start, idempotent on that id" },
333 { from: "a", to: "api", label: "`work_item` `work` #12" },
334 { from: "api", to: "work", label: "item, repos, claims, sessions, pulls, blockers" },
335 { from: "a", to: "api", label: "`work_item` `claim` #12" },
336 { from: "api", to: "work", label: "claim: 30 min unless renewed" },
337 { from: "work", to: "events", label: "`work.claimed`", async: true },
338 { from: "api", to: "b", label: "its `list_events` long poll returns `work.claimed`", reply: true },
339 { note: "B may still claim #12; it is told about A, and nothing is refused", over: ["work", "b"] },
340 { phase: "While A works" },
341 { from: "a", to: "api", label: "heartbeat, entries: renew the claim" },
342 { note: "quiet for 15 min: idle; for 2 h: ended; noticed on the next read", over: ["api", "work"] },
343 ],
344};
345
346// ── Compute ────────────────────────────────────────────────────────────────
347
348const actionsJob: Sequence = {
349 kind: "sequence",
350 caption: "A workflow job on g1t’s runners, from the push that starts it to the status on the commit",
351 width: 900,
352 actors: [
353 { id: "events", title: "events" },
354 { id: "actions", title: "actions", sub: "services/actions", tone: "accent" },
355 { id: "runner", title: "runner", sub: "services/runner" },
356 { id: "sandbox", title: "Sandbox", sub: "`g1t-runner`", tone: "success" },
357 { id: "api", title: "API", sub: "apps/api" },
358 { id: "work", title: "work", sub: "services/work" },
359 ],
360 steps: [
361 { from: "events", to: "actions", label: "`git.push` on `g1t-events-actions`", async: true },
362 { from: "actions", to: "actions", label: "match workflows, plan the jobs" },
363 { from: "actions", to: "work", label: "`set_commit_status` pending" },
364 { from: "actions", to: "runner", label: "`start_actions_job`" },
365 { from: "runner", to: "sandbox", label: "a Container for this job alone" },
366 { phase: "The job runs" },
367 { from: "sandbox", to: "api", label: "`POST /actions/jobs/{job}`: steps, logs" },
368 { from: "api", to: "actions", label: "`job_report`" },
369 { from: "api", to: "sandbox", label: "go on, or cancel", reply: true },
370 { note: "HTTP(S) leaves only through the runner’s egress handler", over: ["runner", "sandbox"] },
371 { phase: "The job ends" },
372 { from: "actions", to: "work", label: "`set_commit_status` success or failure" },
373 { from: "actions", to: "events", label: "`workflow.completed`", async: true },
374 ],
375};
376
377const sandboxes: Graph = {
378 kind: "graph",
379 caption: "Where a job runs: in a Container on g1t’s runners, or inside three walls on a machine of yours",
380 width: 940,
381 height: 330,
382 groups: [
383 { x: 4, y: 4, w: 452, h: 322, label: "g1t’s runners: Cloudflare Containers", tone: "success" },
384 { x: 24, y: 40, w: 230, h: 190, label: "one sandbox per job" },
385 { x: 474, y: 4, w: 462, h: 322, label: "Your machine: three walls", tone: "success" },
386 { x: 490, y: 40, w: 300, h: 274, label: "VM: WSL2, vfkit or QEMU" },
387 { id: "sysbox", x: 506, y: 76, w: 268, h: 222, label: "Sysbox container: `g1t-runner`" },
388 ],
389 nodes: [
390 { id: "h-runner", x: 44, y: 78, w: 190, title: "`g1t-runner`", sub: "does one job, exits" },
391 { id: "h-docker", x: 44, y: 156, w: 190, title: "its own dockerd", sub: "when `DOCKER=on`" },
392 { id: "egress", x: 290, y: 78, w: 150, title: "egress handler", sub: "all HTTP(S) leaves here", tone: "accent" },
393 { id: "hosts", x: 290, y: 250, w: 150, title: "allowed hosts", sub: "registries, your APIs", tone: "external" },
394 { id: "job", x: 526, y: 116, w: 228, title: "job container", sub: "new for every job" },
395 { id: "dockerd", x: 526, y: 210, w: 228, title: "its own dockerd", sub: "never the host’s `docker.sock`" },
396 { id: "api", x: 802, y: 160, w: 130, title: "api.g1t.sh", sub: "long poll, outbound", tone: "external" },
397 ],
398 edges: [
399 { from: "h-runner", to: "egress" },
400 { from: "egress", to: "hosts", label: "`g1tsec_…` → the secret" },
401 { from: "sysbox", to: "api", fromSide: "right", toSide: "left" },
402 ],
403};
404
405const deploymentPath: Graph = {
406 kind: "graph",
407 caption: "Deployments: built in a sandbox, uploaded to a dispatch namespace, served by one Worker that looks up the app by hostname",
408 width: 940,
409 height: 330,
410 nodes: [
411 { id: "repos", x: 10, y: 30, w: 160, title: "repos", sub: "a push to a branch" },
412 { id: "deployments", x: 260, y: 30, w: 170, title: "deployments", sub: "services/deployments", tone: "accent" },
413 { id: "build", x: 520, y: 30, w: 180, title: "build sandbox", sub: "`g1t-runner`, `MODE=deploy`", tone: "success" },
414 { id: "kv", x: 260, y: 150, w: 170, title: "KV `g1t-domains`", sub: "hostname → app", shape: "store" },
415 { id: "wfp", x: 520, y: 150, w: 180, title: "dispatch namespace", sub: "`g1t-deployments`", tone: "accent" },
416 { id: "telemetry", x: 760, y: 150, w: 170, title: "telemetry", sub: "tail events, per project" },
417 { id: "visitors", x: 10, y: 268, w: 160, title: "Visitors", sub: "`*.g1t.page`, your domain", tone: "external" },
418 { id: "pages", x: 260, y: 268, w: 170, title: "pages", sub: "services/pages", tone: "accent" },
419 { id: "otel", x: 760, y: 268, w: 170, title: "Your software", sub: "OTLP to otel.g1t.sh", tone: "external" },
420 ],
421 edges: [
422 { from: "repos", to: "deployments", label: "`git.push`", async: true },
423 { from: "deployments", to: "build", label: "`start_deploy`" },
424 { from: "deployments", to: "wfp", fromSide: "bottom", toSide: "top", fromAt: 0.4, toAt: -0.4, bend: 116, label: "the built Worker" },
425 { from: "deployments", to: "kv", fromAt: -0.2, toAt: -0.2, label: "domains" },
426 { from: "visitors", to: "pages" },
427 { from: "pages", to: "kv", label: "which app?" },
428 { from: "pages", to: "wfp", fromSide: "right", toSide: "bottom", label: "`APPS.get(app)`" },
429 { from: "wfp", to: "telemetry", async: true },
430 { from: "otel", to: "telemetry", label: "OTLP" },
431 ],
432};
433
434// ── Data and access ────────────────────────────────────────────────────────
435
436/** Each port: its adapter on g1t.sh, the port, and its adapter when self-hosted, each with a second line. */
437const PORT_ROWS: [string, string, string, string, string, string][] = [
438 ["Cloudflare Artifacts", "namespaces, sharded", "`GitStore`", "services/repos", "gitstore", "bare repositories, `git http-backend`"],
439 ["R2", "a bucket per use", "`BlobStore`", "crates/blobstore", "S3", "RustFS, MinIO, Ceph, AWS"],
440 ["Email Sending", "from g1t.sh", "`EMAIL`", "a `send_email` binding", "the mail Worker", "Mailpit, then your SMTP"],
441 ["Containers", "services/runner", "`RUNNER`", "a service binding", "the off Worker", "answers that it is off"],
442 ["D1, KV, Queues, DOs", "Cloudflare’s", "Workers bindings", "unchanged code", "workerd", "SQLite files on a volume"],
443];
444
445const portsAndAdapters: Graph = {
446 kind: "graph",
447 caption: "Ports and adapters: the same service code, with a Cloudflare adapter on g1t.sh and an open one when you run it yourself",
448 width: 940,
449 height: 400,
450 groups: [
451 { x: 6, y: 4, w: 262, h: 392, label: "On g1t.sh" },
452 { x: 662, y: 4, w: 272, h: 392, label: "Self-hosted, in Docker Compose", tone: "success" },
453 ],
454 nodes: PORT_ROWS.flatMap(([hosted, hostedSub, port, portSub, self, selfSub], i) => {
455 const y = 40 + i * 72;
456 return [
457 { id: `h${i}`, x: 22, y, w: 230, h: 50, title: hosted, sub: hostedSub },
458 { id: `p${i}`, x: 352, y, w: 230, h: 50, title: port, sub: portSub, tone: "accent" as const },
459 { id: `s${i}`, x: 678, y, w: 240, h: 50, title: self, sub: selfSub, tone: "success" as const },
460 ];
461 }),
462 edges: PORT_ROWS.flatMap((_, i) => [
463 { from: `p${i}`, to: `h${i}` },
464 { from: `p${i}`, to: `s${i}` },
465 ]),
466};
467
468const identityFlow: Graph = {
469 kind: "graph",
470 caption: "Every credential becomes the same viewer, resolved once per request; each service decides for itself",
471 width: 940,
472 height: 340,
473 groups: [{ x: 4, y: 4, w: 226, h: 332, label: "Credentials" }],
474 nodes: [
475 { id: "cookie", x: 18, y: 32, w: 198, h: 50, title: "Session cookie", sub: "`g1t_session`, the site", tone: "external" },
476 { id: "pat", x: 18, y: 92, w: 198, h: 50, title: "Access token", sub: "`g1t_…`, scoped", tone: "external" },
477 { id: "oauth", x: 18, y: 152, w: 198, h: 50, title: "OAuth token", sub: "MCP clients, apps", tone: "external" },
478 { id: "job", x: 18, y: 212, w: 198, h: 50, title: "Job or run token", sub: "one workflow job", tone: "external" },
479 { id: "gitpw", x: 18, y: 272, w: 198, h: 50, title: "git password", sub: "refused with 2FA on", tone: "external" },
480 { id: "identity", x: 286, y: 146, w: 160, h: 62, title: "identity", sub: "`user_for_…`", tone: "accent" },
481 { id: "viewer", x: 556, y: 146, w: 170, h: 62, title: "the viewer", sub: "kind, workspaces, grants" },
482 { id: "services", x: 836, y: 146, w: 98, h: 62, title: "services", sub: "`access::can`", tone: "accent" },
483 ],
484 edges: [
485 ...["cookie", "pat", "oauth", "job", "gitpw"].map((from) => ({ from, to: "identity", fromSide: "right" as const, toSide: "left" as const, bend: 256 })),
486 { from: "identity", to: "viewer", label: "once per request" },
487 { from: "viewer", to: "services", label: "in every RPC body" },
488 ],
489};
490
491// ── Shipping ───────────────────────────────────────────────────────────────
492
493const STAGES: [string, string, string][] = [
494 ["push", "a push to `main`", ""],
495 ["check", "check, plan", "manifest, tests"],
496 ["migrate", "migrations", "every pending one"],
497 ["core", "core", "the services"],
498 ["edge", "edge", "api, pages, status"],
499 ["front", "front", "site, sudo, docs"],
500 ["smoke", "smoke", "sign-in works"],
501];
502
503const deployStages: Graph = {
504 kind: "graph",
505 caption: "How g1t ships itself: each stage starts only when the one before it succeeded",
506 width: 940,
507 height: 140,
508 groups: [{ x: 140, y: 4, w: 796, h: 132, label: "`.g1t/workflows/deploy.yml`, on g1t Actions", tone: "accent" }],
509 nodes: STAGES.map(([id, title, sub], i) => ({
510 id,
511 x: 10 + i * 133,
512 y: 56,
513 w: 118,
514 h: 50,
515 title,
516 sub: sub || undefined,
517 tone: id === "push" ? ("external" as const) : id === "smoke" ? ("success" as const) : undefined,
518 })),
519 edges: STAGES.slice(1).map(([id], i) => ({ from: STAGES[i][0], to: id })),
520};
521
522export const ARCHITECTURE: Record<string, Spec> = {
523 "platform-map": platformMap,
524 "page-request": pageRequest,
525 "api-request": apiRequest,
526 "push-path": pushPath,
527 "clone-path": clonePath,
528 "event-fanout": eventFanout,
529 "follow-events": followEvents,
530 "merge-queue": mergeQueue,
531 coordination,
532 "actions-job": actionsJob,
533 sandboxes,
534 "deployment-path": deploymentPath,
535 "ports-and-adapters": portsAndAdapters,
536 "identity-flow": identityFlow,
537 "deploy-stages": deployStages,
538};