Skip to content
147 linesCodeBlameRaw
1---
2title: An open letter to Cloudflare
3description: From the team at Flagon, Inc. building g1t, a git platform that runs entirely on Cloudflare. What works, where we hit walls, and what we'd ask for.
4---
5
6Dear Cloudflare,
7
8We're the small team at Flagon, Inc. building g1t, a git platform where
9people and coding agents work in the same issues, pull requests and merge
10queue. Every part of it runs on you: about twenty Workers, a D1 database per
11service, Artifacts for every repository and every pull request, Containers
12for agents and CI, R2, KV, Queues, and Cloudflare for SaaS for our customers'
13domains. We have no servers.
14
15This is a thank-you, and a list of what would help us most next.
16
17## Why we built on you
18
19A git platform is usually a fleet: storage nodes, a job system, a database
20cluster, a CDN in front, and people on call for all of it. We wanted to run
21one for thousands of teams with a handful of people. You offered a global
22platform where the unit of work is a request, the unit of storage is a
23Durable Object, and nothing costs money while nobody is using it.
24
25Artifacts is the reason g1t exists in this shape. One Durable Object per
26repository is the right isolation unit: a busy repository doesn't slow its
27neighbours, and there is nothing to shard by hand. It speaks real git, so
28stock clients cloned, fetched and pushed through our proxy from the first
29day. `fork()` is a single call, and per-pull-request isolation for agents
30fell out of it almost for free. Scoped tokens that expire on their own let
31us hand a sandbox a credential that dies with it. The read binding powers
32every page we render, blame, mergeability and search, without a git client
33anywhere.
34
35The rest of the platform held up too. Rust compiled to WebAssembly runs our
36services. D1's read replication is free and good. Containers gave us
37sandboxes in three sizes. With a cached credential and ref listing, a
38`git fetch` with nothing new answers in under half a second, and most of
39our pages answer in under 250 ms. We went from an empty repository to a
40launch on this stack, and most of it worked the first time.
41
42## Where we hit walls
43
44Running a real platform for many teams found the edges. None of these
45stopped us. Each one costs us code, latency or certainty, and each one will
46cost the next team building on you the same.
47
48**What a billable operation is.** Artifacts pricing names "repo operations,
49such as create, push, pull, and clone", and the metrics list a different set
50of event names. Neither says whether binding reads, token mints or ref
51listings count. We price from cost, so this decides what our customers pay.
52Depending on the answer, our model for a few thousand workspaces lands
53anywhere between about $1.8k and $31k a month. Today we count every clone,
54fetch and push ourselves, and hope it matches.
55
56**What a fork stores.** Forks are the natural primitive for a pull request,
57and agents open pull requests by the thousand. We can't find whether a fork
58shares objects with its source or copies them. If it copies, an agent-heavy
59account reaches the 1 TB account limit in days, and at that point every push
60in the account fails, for every customer at once. We keep forks for now,
61and are measuring it ourselves.
62
63**A write path and a pre-receive hook.** The binding reads, but it can't
64list refs, move them, or write objects. So to land a pull request we speak
65git's wire protocol to our own storage from inside a Worker, buffering packs
66in an isolate with 128 MB to share. With no hook before refs move, branch
67protection and secret scanning only hold for pushes through our proxy, which
68parses every pack in WebAssembly before forwarding it. We wrote a second
69implementation of git's pack format to get there.
70
71**Ref-change events and the cost of a credential.** Push events need one
72subscription per repository, which doesn't scale to tens of thousands of
73repositories and forks. We record ref changes ourselves, and a test scans
74our own source to make sure every code path that moves a ref says so. Every
75git credential takes three binding calls and about 0.8 s to mint, so we
76cache sealed tokens across isolates in KV.
77
78**Placement that follows data.** Smart Placement once ran our site in
79Amsterdam for a visitor in Denver, while every D1 primary we have is in
80western North America. Every query crossed the Atlantic, and our Explore
81page took 0.85 s instead of 0.17 s. We turned placement off everywhere and
82measure each Worker by hand.
83
84**D1 sessions across service bindings.** Read replicas need a bookmark to
85give read-your-writes. Our site calls seven services, each with its own
86database, so we built a header protocol to carry bookmarks through service
87bindings into a cookie and back.
88
89**Containers that build images and keep disks.** We found no supported way
90to run Docker or BuildKit in a Container, so our own CI can't rebuild our
91sandbox image; that waits for a machine outside. Container disk is
92ephemeral, so the self-hostable git store we'd like as a warm fallback has
93to live off Cloudflare.
94
95**Inbound TCP for SSH.** Git users expect `git@host:owner/repo`. Workers
96take no inbound TCP, so g1t is HTTPS only. We've applied for the beta and
97are waiting.
98
99## What we built in the meantime
100
101A per-workspace operation counter that is our best guess at your invoice. A
102fork sweep we can switch on once we know what forks cost. A smart HTTP
103client inside a Worker for landing, catch-up, mirrors and imports. Our own
104push policy in front of Artifacts. A versioned ref cache with a test that
105guards it. Two layers of credential caching. A bookmark protocol for D1.
106A probe that deploys throwaway Workers to measure placement, and a
107`Server-Timing` header on every response so we see the next regression.
108Image builds on a laptop.
109
110All of it works. Most of it is code we'd happily delete.
111
112## What we're asking for
113
1141. A published definition of a billable Artifacts operation, with
115 per-repository metrics that use the same names as the invoice.
1162. Documented fork storage, an expiry on `fork()`, a repository's stored
117 bytes in `info()`, and a warning before the account storage limit, with
118 failures per repository rather than account-wide.
1193. Ref listing, atomic compare-and-swap ref updates and streaming pack
120 writes in the binding.
1214. A pre-receive hook that a Worker answers.
1225. Account-level ref-change events to a Queue, a read-after-write guarantee
123 for refs, and git forwarding authenticated by the binding, with no token
124 to mint.
1256. Placement that accounts for D1 primaries and service bindings, and D1 as
126 a placement target.
1277. D1 sessions that travel across service bindings.
1288. Image builds and persistent volumes for Containers.
1299. Inbound TCP, so git can run over SSH.
13010. A support path during the beta, snapshot restore and export for
131 repositories, and a date for general availability with an SLA.
132
133## Let's work on it together
134
135We chose you on purpose, and we'd choose you again. A small team running a
136global git platform with no servers is the promise of what you've built,
137and g1t shows that it mostly holds. We'd like to help close the
138rest of the gap: traces, test repositories, early builds to try, or a call
139with the teams involved. Whatever is useful.
140
141You can reach us through [g1t.sh](https://g1t.sh/support). Our code lives
142at [g1t.sh/flagon-io/g1t](https://g1t.sh/flagon-io/g1t), on the platform
143it describes.
144
145With thanks,
146
147The team at Flagon, Inc.