Skip to content

Git

alchemy/Git is a pluggable, embeddable, self-hostable git server. It speaks git’s smart HTTP to any client, serves a typed REST plane with pull requests, and answers the GitHub REST v3 API for gh and Octokit. It runs on Cloudflare Workers, Durable Objects, and R2, embeds in your own HttpApi, and each part of it is an Effect Layer you can replace:

import * as Cloudflare from "alchemy/Cloudflare";
import * as Git from "alchemy/Git";
import * as Effect from "effect/Effect";
import * as Layer from "effect/Layer";
import * as HttpRouter from "effect/unstable/http/HttpRouter";
import * as Http from "alchemy/Http";
import { Authentication } from "./api.ts";
export const GitObjects = Cloudflare.R2.Bucket("GitObjects");
const GitLive = Git.ApiLive.pipe( // ordinary routes beside your application API
Layer.provide(Git.ApiHandlersLive), // shared Git handlers
Layer.provide(Authentication.layer), // your middleware: who may call what
Layer.provide(Git.ReposDurableObject), // refs, objects, pull requests
Layer.provide(Git.RegistryDurableObject), // owner/name → repo
Layer.provide(Git.HasherInline), // push verification
Layer.provide(Git.BlobStoreR2(GitObjects)), // packs, bundles, large pushes
);
export default Cloudflare.Worker(
"Git",
{ main: import.meta.url, ...Git.GIT_WORKER_OPTIONS },
Effect.gen(function* () {
const fetch = yield* HttpRouter.toHttpEffect(GitLive.pipe(Layer.provide(Http.Platform)));
return { fetch };
}),
);

Authentication is the application’s route middleware. Getting Started defines it; HTTP routes explains their composition.

Each line of the graph is one decision with its own implementations. Changing a line changes the decision and nothing else:

const GitObjects = Cloudflare.R2.Bucket("GitObjects");
const GitObjects = AWS.S3.Bucket("GitObjects");
const GitLive = Git.ApiLive.pipe(
Layer.provide(Git.ApiHandlersLive),
Layer.provide(Authentication.layer),
Layer.provide(Git.ReposDurableObject),
Layer.provide(Git.RegistryDurableObject),
Layer.provide(Git.HasherInline),
Layer.provide(Git.BlobStoreR2(GitObjects)),
Layer.provide(Git.BlobStoreS3(GitObjects)),
);

Bytes now live in S3. Application authorization stays in your HTTP handlers.

The engine holds no users, no credentials, and no policy. Who may call a route is the middleware applied to its route layer, so the same host runs behind a shared secret, behind Better Auth, or behind any other authentication. Your handler decodes the push, authorizes its proposed changes, then calls Git.Engine.preparePush and commitPush. Endpoints use Effect’s HttpApiEndpoint; implementations use HttpApiBuilder.group and handleAll. Add your own groups or replace a handler in a Git group before registering it. git.alchemy.run runs this file. The alchemy monorepo, 44,051 objects in a 67 MiB pack, is hosted on it and clones back byte-identical under git fsck --strict.

Getting Started is a complete shared-credential quickstart. The tutorial starts with a Git push and builds one behavior at a time. Every part ends with a deployment and a check you can run:

  1. Push your first repository — deploy Git, push a commit, and clone it back.
  2. Control access — require a shared credential and verify anonymous requests fail.
  3. Publish a repository — allow anonymous clones while keeping writes protected.
  4. Give users their own credentials — use accounts and individual API keys.
  5. Add your application’s API — serve /me beside Git with the same authenticated user.
  6. Protect a branch — reject deletion of main with an application policy.

Building blocks is the reference for each line of the graph: the contract, what ships, how to write your own. Recipes maps requirements to lines, with Scaling as the why behind the table and complete stack files to copy.

No issues, reviews, comments, checks, Actions, or GraphQL. gh api works. gh pr create and the rest of the porcelain do not. What it is: a git remote you own, with an API, that you can build on.

Looking for the GitHub provider, which manages repos, Actions secrets, and webhooks on github.com? That is GitHub.