| 1 | --- |
| 2 | title: Git |
| 3 | description: A pluggable, embeddable, self-hostable git server on Cloudflare Workers, Durable Objects, and R2. Smart HTTP for any client, a typed REST plane with pull requests, and a GitHub-compatible API. |
| 4 | --- |
| 5 | |
| 6 | `alchemy/Git` is a pluggable, embeddable, self-hostable git server. It |
| 7 | speaks git's smart HTTP to any client, serves a typed REST plane with |
| 8 | pull requests, and answers the GitHub REST v3 API for `gh` and Octokit. |
| 9 | It runs on Cloudflare Workers, Durable Objects, and R2, embeds in your |
| 10 | own `HttpApi`, and each part of it is an Effect Layer you can replace: |
| 11 | |
| 12 | ```typescript |
| 13 | import * as Cloudflare from "alchemy/Cloudflare"; |
| 14 | import * as Git from "alchemy/Git"; |
| 15 | import * as Effect from "effect/Effect"; |
| 16 | import * as Layer from "effect/Layer"; |
| 17 | import * as HttpRouter from "effect/http/HttpRouter"; |
| 18 | import * as Http from "alchemy/Http"; |
| 19 | import { Authentication } from "./api.ts"; |
| 20 | |
| 21 | export const GitObjects = Cloudflare.R2.Bucket("GitObjects"); |
| 22 | |
| 23 | const GitLive = Git.ApiLive.pipe( // ordinary routes beside your application API |
| 24 | Layer.provide(Git.ApiHandlersLive), // shared Git handlers |
| 25 | Layer.provide(Authentication.layer), // your middleware: who may call what |
| 26 | Layer.provide(Git.ReposDurableObject), // refs, objects, pull requests |
| 27 | Layer.provide(Git.RegistryDurableObject), // owner/name → repo |
| 28 | Layer.provide(Git.HasherInline), // push verification |
| 29 | Layer.provide(Git.BlobStoreR2(GitObjects)), // packs, bundles, large pushes |
| 30 | ); |
| 31 | |
| 32 | export default Cloudflare.Worker( |
| 33 | "Git", |
| 34 | { main: import.meta.url, ...Git.GIT_WORKER_OPTIONS }, |
| 35 | Effect.gen(function* () { |
| 36 | const fetch = yield* HttpRouter.toHttpEffect(GitLive.pipe(Layer.provide(Http.Platform))); |
| 37 | return { fetch }; |
| 38 | }), |
| 39 | ); |
| 40 | ``` |
| 41 | |
| 42 | `Authentication` is the application's route middleware. [Getting Started](/git/getting-started) |
| 43 | defines it; [HTTP routes](/git/blocks/server#add-git-to-your-application) explains their composition. |
| 44 | |
| 45 | Each line of the graph is one decision with its own implementations. |
| 46 | Changing a line changes the decision and nothing else: |
| 47 | |
| 48 | ```diff lang="typescript" |
| 49 | -const GitObjects = Cloudflare.R2.Bucket("GitObjects"); |
| 50 | +const GitObjects = AWS.S3.Bucket("GitObjects"); |
| 51 | |
| 52 | const GitLive = Git.ApiLive.pipe( |
| 53 | Layer.provide(Git.ApiHandlersLive), |
| 54 | Layer.provide(Authentication.layer), |
| 55 | Layer.provide(Git.ReposDurableObject), |
| 56 | Layer.provide(Git.RegistryDurableObject), |
| 57 | Layer.provide(Git.HasherInline), |
| 58 | - Layer.provide(Git.BlobStoreR2(GitObjects)), |
| 59 | + Layer.provide(Git.BlobStoreS3(GitObjects)), |
| 60 | ); |
| 61 | ``` |
| 62 | |
| 63 | Bytes now live in S3. Application authorization stays in your HTTP handlers. |
| 64 | |
| 65 | The engine holds no users, no credentials, and no policy. Who may call |
| 66 | a route is the middleware applied to its route layer, so the same host |
| 67 | runs behind a shared secret, behind Better Auth, or behind any other |
| 68 | authentication. Your handler decodes the push, authorizes its proposed changes, |
| 69 | then calls `Git.Engine.preparePush` and `commitPush`. Endpoints use Effect's `HttpApiEndpoint`; implementations use |
| 70 | `HttpApiBuilder.group` and `handleAll`. Add your own groups or replace a |
| 71 | handler in a Git group before registering it. |
| 72 | [git.alchemy.run](https://git.alchemy.run) runs this file. The alchemy |
| 73 | monorepo, 44,051 objects in a 67 MiB pack, is hosted on it and clones |
| 74 | back byte-identical under `git fsck --strict`. |
| 75 | |
| 76 | ## Build one |
| 77 | |
| 78 | [Getting Started](/git/getting-started) is a complete shared-credential quickstart. |
| 79 | The tutorial starts with a Git push and builds one behavior at a time. Every |
| 80 | part ends with a deployment and a check you can run: |
| 81 | |
| 82 | 1. [Push your first repository](/git/tutorial/part-1) — deploy Git, push a commit, |
| 83 | and clone it back. |
| 84 | 2. [Control access](/git/tutorial/part-2) — require a shared credential and verify |
| 85 | anonymous requests fail. |
| 86 | 3. [Publish a repository](/git/tutorial/part-3) — allow anonymous clones while |
| 87 | keeping writes protected. |
| 88 | 4. [Give users their own credentials](/git/tutorial/part-4) — use accounts and |
| 89 | individual API keys. |
| 90 | 5. [Add your application's API](/git/tutorial/part-5) — serve `/me` beside Git |
| 91 | with the same authenticated user. |
| 92 | 6. [Protect a branch](/git/tutorial/part-6) — reject deletion of `main` with an |
| 93 | application policy. |
| 94 | |
| 95 | ## Use it |
| 96 | |
| 97 | - [Cloning & pushing](/git/clone-and-push) — standard clients, what |
| 98 | the wire supports, credentials. |
| 99 | - [Repositories](/git/repositories) — create, fork, import, and manage |
| 100 | over the typed REST plane. |
| 101 | - [Pull requests](/git/pull-requests) — server-side merge base, diffs, |
| 102 | and merges. |
| 103 | - [GitHub API compatibility](/git/github-api) — `gh api` and Octokit |
| 104 | against your host. |
| 105 | |
| 106 | ## Shape it |
| 107 | |
| 108 | [Building blocks](/git/blocks) is the reference for each line of the |
| 109 | graph: the contract, what ships, how to write your own. |
| 110 | [Recipes](/git/recipes) maps requirements to lines, with |
| 111 | [Scaling](/git/recipes/scaling) as the why behind the table and complete |
| 112 | stack files to copy. |
| 113 | |
| 114 | ## Not a GitHub replacement |
| 115 | |
| 116 | No issues, reviews, comments, checks, Actions, or GraphQL. `gh api` |
| 117 | works. `gh pr create` and the rest of the porcelain do not. What it is: |
| 118 | a git remote you own, with an API, that you can build on. |
| 119 | |
| 120 | Looking for the GitHub *provider*, which manages repos, Actions |
| 121 | secrets, and webhooks on github.com? That is [GitHub](/github). |