---
title: "Aspen: scaling agentic commits beyond what Git was built for"
description: "An open-source Git server for repositories that hundreds of agents push to at once, after the design OpenAI described for Pando. Plain Git on the client, packs in object storage, branches in Postgres."
url: "https://reasonmachines.com/blog/aspen"
section: "Blog"
---

# Aspen: scaling agentic commits beyond what Git was built for

_An open-source Git server for repositories that hundreds of agents push to at once, after the design OpenAI described for Pando. Plain Git on the client, packs in object storage, branches in Postgres._

By Reason

November 5, 2026

Git was built for people. A person pushes a few times a day. An agent pushes every few minutes, and a team running a few hundred of them turns one repository into a firehose: hundreds of branches moving at once, and a CI fetch after every push.

A classic Git server doesn’t keep up. It takes every push to a repository through one queue, so a push to your branch waits on pushes to everyone else’s, and it builds a fresh pack for every clone and fetch, compressing the same history again for each client. A pack is Git’s file format for shipping objects (files, directories and commits) in bulk. The server’s work grows with the number of clients, not with the amount of new code.

### A thousand writers, two servers

Each square is an agent pushing ten commits. It turns blue once a reader has fetched all ten. Illustrative, time compressed.

writers 2501000playback 10×20×40×

Classic Git serverready

0 ssimulated–push → fetch0writers done

Aspenready

0 ssimulated–push → fetch0writers done

not startedwaiting in the server’s queuepushingall ten fetched

A toy model, not a benchmark. The classic server orders every push through one repository and builds a pack for every fetch on the same machine, so its queue grows as soon as arrivals pass its rate. Aspen moves each branch with its own compare-and-swap and serves fetches from stored packs, so writers finish at the pace they push.

OpenAI hit this wall with their monorepo and wrote up how they got past it in “Scaling Git at OpenAI”. Their answer is Pando, an internal Git backend.[1](#ref-1) We liked the design enough to build it in the open. Today we’re releasing [Aspen](https://github.com/reason-machines/aspen), an independent, open-source implementation that anyone can run, under the Apache License 2.0.[2](#ref-2)

Aspen speaks plain Git over HTTPS and SSH, so nothing changes for the client: `git push` and `git clone` work as they always have. The difference is all on the server.

## Two things we gave up

A Git server has to scale two ways: many writers pushing to different branches, and many readers fetching the same history. Following Pando, Aspen drops two guarantees most Git servers treat as sacred.

**One snapshot of every branch** Each branch is a chain of versions in Postgres, and moves on its own. A push uploads its pack, then moves its branch with one compare-and-swap on a single row: “move this branch from commit A to commit B, but only if it still points at A.” Pushes to different branches never wait for each other. Two pushes to the same branch race on that row, and one wins cleanly; the other gets Git’s usual “fetch first” and rebases. The cost: a reader looking at several branches may see each one at a slightly different moment. Agents don’t care.

**A custom pack for every client** Aspen never unpacks a push. It checks the pack, fills in the objects Git left out because the server already had them, and stores it in object storage (S3, GCS, Azure Blob or a plain directory) exactly as it arrived. When someone fetches, Aspen works out which stored packs hold what they’re missing and streams those, recompressing nothing. It can also hand out signed URLs so clients download the packs straight from a CDN.

### A push, then a fetch

One stateless server between stock Git and two stores. Nothing on either path builds a pack.

pushfetchbranch rowpack

Postgres holds one row per branch and object storage holds packs exactly as they were pushed. A push writes one pack and swaps one row; a fetch reads one row and sends packs that already exist. Click a step to replay it.

## A canvas anyone can push to

To show Aspen to people rather than to a load generator, we put it behind [place.reasonmachines.com](https://place.reasonmachines.com): a shared 64 × 64 pixel canvas with no sign-in. Every pixel is a file in a Git repository, and clicking a tile makes one commit and one push to Aspen. A bot merges everyone’s branches into `main`, and `main` is mirrored to [GitHub](https://github.com/reason-machines/place-canvas), whose README shows the canvas live.

You can paint from a terminal too: clone it, change a tile’s file and `git push`. Or press **Copy prompt** on the page and hand the prompt to Claude Code, Codex or any coding agent; it asks what you want to paint and where, shows you a preview, and pushes once you say go.

### The canvas Paused

Every pixel is a file, and every click is one commit pushed to Aspen, by a person or an agent. This image refreshes every two seconds.

git clone --depth=1 https://place.reasonmachines.com/canvas.git
cd canvas && printf '#3690ea\n' > tiles/10_20.txt
git add tiles && git commit -m paint && git push origin HEAD:paint/you

Pushes go to paint/ branches and a bot merges them into main. Rules and the palette are in the canvas’s TILES.md; its main is mirrored to [GitHub](https://github.com/reason-machines/place-canvas).

## A push only sees trunk

When you push, a normal server first lists every branch it has, so your client can skip objects the server already holds. With a million branches, that list alone is enormous, and the pack your client builds depends on whichever branches happened to be listed, so nobody else can reuse it.

Aspen lists only the trunk branch. Your push then carries everything since trunk, and anyone who has trunk can fetch your branch with that one pack. Pushes get a little larger; the conversation stays the same size no matter how many branches exist. The one visible difference: `git push --force` and `--delete` need an extra `-o force`.

## Compaction

If every push stores one small pack, a client that’s a day behind needs tens of thousands of them. So Aspen merges packs in the background, sixteen at a time, in levels, the way a log-structured merge tree compacts its files. A fresh clone gets a few large packs, and a client that’s nearly current gets a few small ones.

Shallow clones get their own fast path, because CI fetches with `--depth=1` by default. Aspen builds the depth-1 pack for a branch tip once and serves it to every CI job that asks.

### Levels as a skip list

Each bar is one pack. Compaction keeps packs at four sizes, and a fetch takes the largest pack that fits at each step between the client and trunk.

Client hasv0

Packs in the plan0

One pack per push0

client already hassendingsentstored, not needed

A client thousands of versions behind gets a few dozen packs instead of thousands: large ones for the long middle stretch, small ones only at the ends. Drag along the history to move the client.

## How fast

The number that matters for agents is pushes a second to one repository, counted only once each push is safely stored. Cursor published theirs for its Git server, Origin: up to 120 pushes a second on S3 Standard, and more than 300 on the faster S3 Express One Zone, with every push to a repository going through one log.[3](#ref-3) Aspen only orders pushes to the same branch, so it has no per-repository ceiling of that kind.²

On one 8-core server, S3 Standard and a 4-core Postgres, pushing to git.git through Aspen’s branch URL, Aspen sustained 567 pushes a second for five minutes with no errors. Each push was in S3 and Postgres before Git printed “ok”, with a median push of 208 ms and a 99th percentile of 314 ms. Another client saw each push 30 ms after it was acknowledged, at the median.

OpenAI didn’t publish a push rate for Pando. It reports results against its own previous system: median push latency down by nearly 80%, a petabyte of Git data served a day from a handful of cores, and clones that fill a 10 Gbps link.[1](#ref-1) Pando’s design is what Aspen follows, so the honest comparison there is the shape of the numbers: pushes that don’t wait on each other, and fetches served from stored packs rather than rebuilt ones.

Landing on main is a different kind of number. Every landing builds on the one before it, so separate landings run one at a time; Aspen manages about 4 a second that way, limited by round trips rather than load. Aspen also has a server-side merge queue that lands many PRs, each its own commit, in one update of main; we’ll publish its number once it’s measured. None of these numbers include CI.

### Pushes a second to one repository

Each push durable before it is acknowledged. Cursor’s numbers are as published; Aspen’s is our measurement.

Cursor Origin, S3 Standard

120

Cursor Origin, S3 Express

300+

Aspen, S3 Standard

567¹

Aspen: one 8-core server, S3 Standard and a 4-core Postgres, pushing to git.git through Aspen’s branch URL, sustained for five minutes. Cursor: a replicated cluster on its own monorepo, whose size the post doesn’t give.
¹ 567/s is git.git (about 4,500 files, 75k commits) pushed through Aspen’s branch-scoped URL.
² Each push still costs more as a repository grows, so very large monorepos see lower rates today; that’s what we’re working on now.

## Open source, and early

The code is on [GitHub](https://github.com/reason-machines/aspen). `aspen dev` runs a server on your laptop with SQLite and a local directory; for a team there’s a Docker Compose stack, a Helm chart, and Terraform for AWS, Google Cloud, Azure and Hetzner.

We only trust Aspen where stock Git agrees with it. A differential test suite replays random pushes, deletes and races against Aspen and against Git’s own server and requires identical results; every night, Git versions from 2.30 to the latest, plus JGit, run against it; fuzzers and a chaos suite that kills servers mid-push run alongside. Like Pando, Aspen was built mostly by agents: one Reason took it from first commit to public in four days, over 151 merged pull requests.[1](#ref-1)

It’s pre-alpha. Aspen is the Git part only, with no web UI, and no partial clones, SHA-256 repositories or RSA SSH keys yet. If you run agents against one repository, try it, break it, and tell us on GitHub.

## References

<a id="ref-1"></a>
[1] OpenAI. Scaling Git at OpenAI, 2026. Describes Pando, OpenAI’s Git backend: per-branch ref versions, trunk-only push advertisement, pack reuse with packfile URIs, and LSM-style compaction. Aspen is an independent implementation of that design and is not affiliated with OpenAI. [Post](https://openai.com/index/scaling-git-at-openai/).

<a id="ref-2"></a>
[2] Reason Machines. Aspen, an open-source Git server for agent-scale repositories, Apache License 2.0. [Repository](https://github.com/reason-machines/aspen), with the live demo at [place.reasonmachines.com](https://place.reasonmachines.com).

<a id="ref-3"></a>
[3] Cursor. Git at any scale, August 2026. Origin sustains “up to 120 pushes/s” on S3 Standard and “more than 300 pushes/s” on S3 Express One Zone, acknowledging a push only once it is in its write-ahead log. [Post](https://cursor.com/blog/git-at-any-scale).
