
I Looked Into "Pando," OpenAI's Rebuilt Git Server
This page has been translated by machine translation. View original
Hi there! I'm Yuji Nishimura from the Operations Department!
When you hand multiple tasks to AI agents, in addition to the work of writing code, pushes to branches and fetches from CI also increase. Pando, developed by OpenAI, is a Git-compatible backend that supports this kind of parallel development.
What's interesting is that while it maintains Git's operational model, it changes "what the server computes every time" and "which updates are coordinated together" on the server side. In this article, we'll focus on the official post Architecting Pando and follow the flow from push to fetch with concrete examples.
What Pando aims to solve is the growth of "writers" and "readers"
Pando is not a Git client. It is a server-side mechanism that accepts pushes and provides the data needed for fetch and clone. It is used across OpenAI's monorepo.
The official post explains that the increase in parallel development with Codex is the background. One person advances multiple changes, and each push triggers multiple CI jobs that fetch the same commit, so both writes and reads increase.
For example, suppose agent A fixes authentication, B adds tests, and C fixes the documentation. Since each is a change to a separate branch, the side serving the repository wants to accept them independently. Furthermore, even if CI readers increase, it would help not to redo the process of distributing the same history each time.
To meet these two requirements, Pando combines coordinating updates per ref with reusing packfiles across readers.
First, let's sort out Git's "objects, refs, and packfiles"
Git objects are data such as blobs that hold file contents, trees that hold directory structure, and commits that link history. By following a commit to its trees, blobs, and parent commits, you can reach the necessary files and history.
A branch ref, on the other hand, is a reference that indicates "which commit it currently points to." Objects are the substance of the history, and refs are its entry point.
A packfile is a format that bundles multiple objects and stores and transfers them using compression and delta representation. It is not simply a "list of changed files."
What matters here is that a pushed pack does not necessarily contain all the objects needed for that history. History that the server has already agreed it holds can be omitted when sending. Even if you hand only the pack to another reader, that reader cannot reconstruct the history if it does not have the omitted parts.
In other words, to reuse a pack, you need to know not only which pack you stored, but also what that pack depends on.
A waiting problem that adding more servers doesn't solve
The official post describes three-phase commit (3PC) and a shared write-ahead log (WAL) as distributed Git approaches considered before Pando.
| Approach modeled in the official post | How updates proceed | Where load concentrates |
|---|---|---|
| 3PC | Servers holding replicas of the repository reach agreement on ref updates in multiple phases | Disks on the replicas, and the communication needed for agreement |
| WAL | Packs are stored and ref updates are recorded in a shared manifest | The shared manifest that determines update order, and propagation to each replica |
For example, with the WAL approach, even if pushes to the auth branch and the documentation branch are separate, if they finally update the same manifest, they conflict there. Even if you scale up the object storage, this common update point remains.
This is a description of approaches that the official post modeled for comparison. It does not mean that all Git hosting services have the same internal implementation or the same constraints. The original article's comparison also needs to be read with this premise in mind.
Two assumptions Pando reconsidered
Pando moves away from a design that always satisfies the following two things.
| Assumption reconsidered | Pando's choice | What is traded off |
|---|---|---|
| Treat all refs as the state of the entire repository at a single point in time | Coordinates updates per ref and records the dependencies of the necessary history | Reading multiple branches does not necessarily reflect the state at the same point in time |
| Build a dedicated, duplicate-free pack for each reader | Selects and reuses stored packs | Some objects the reader already has may be fetched again |
This choice reduces "work aligned across the whole repository" and "work rebuilt for each reader." However, to that extent, managing dependencies and choosing which packs to hand over become important.
Push: store the data and update the branch's entry point
The following is a conceptual diagram I made based on the official announcement. It is not the official architecture diagram. The metadata cache and similar components are omitted.

Organizing the official post's explanation of push, the flow is as follows.
- The Git client sends a pack.
- While validating the received pack, Pando stores it as immutable data in the pack storage location (blob storage).
- In the storage location for reference information (metadata store), it records the new ref version, its dependencies, and the pack's location.
- The ref pointer, which points to the branch's current ref version, is updated to the new record within a transaction.
Steps 3 and 4 are performed in the same transaction.
A ref version is a record representing one branch update. It holds not only the new tip commit but also the pack that supplied it and the past ref versions it depends on. It is not a mechanism that simply stores "a branch name and a commit ID."
Also, there is no need to unpack received packs into each server's local repository before making them deliverable. Pando's servers mainly handle Git negotiation and pack selection.
Since what's coordinated for update conflicts is the individual ref pointer, pushes to different branches can proceed independently. For concurrent updates to the same branch, conflict coordination remains.
Making data fetchable right after a successful push
Recording the ref version and updating the ref pointer are done synchronously. As a result, a newly updated commit can be fetched immediately afterward, providing consistency that lets you read your own writes (read-your-own-write).
Asynchronous work such as scheduling compaction is also recorded in the same transaction. The processing that makes data available is separated from the processing that makes later delivery more efficient.
The O(1) in the official post refers to the number of metadata reads and writes at push time. It does not mean that even transferring and validating a large pack finishes in constant time regardless of size.
Why using only the trunk as a common base lets packs be reused
Before a push, the server tells the client information such as the refs it holds. The official post calls this advertise. The client uses that information to determine which objects it doesn't need to send.
The more refs are advertised, the wider the range that can be omitted from the pushed pack. However, other readers do not necessarily have the same omitted parts, which makes pack reuse harder. Conversely, if no base is shown at all, the burden of including the entire history is large.
In Pando, for a push to a new working branch, the designated trunk branch is shown by default as the common base. The trunk here is the branch serving as the development baseline, and below it is called main.
Example of pushing twice to the same branch
The following figure is a conceptual diagram I made to explain dependencies. The commit and pack names and the readers' states are fictional, and it is not an official architecture diagram.

Consider the case where, starting from commit T1 on main, changes A1 and A2 are made on feature/auth.
| Timing | Common base | Range covered by the working branch's pack |
|---|---|---|
| 1st: push A1 | History up to T1 | Objects needed for a party that has T1 to reconstruct A1 |
| 2nd: push A2 | History up to T1 | Objects needed for a party that has T1 to reconstruct A1 and A2 |
If the base is still T1 the second time, the data needed for A1 is included again. The transfer size is larger than sending only A2, which is what was added since the previous push, but other readers that have T1 can fetch the working branch using only the second pack.
In this example the base is fixed at T1. The range that can actually be omitted depends on the history agreed upon in the negotiation at push time. Also, this does not mean that the common base itself is sent, or that the entire repository is transferred every time.
Separating the safety check for updates from the history omitted from the pack
Here you may wonder: "If the current position of the working branch isn't communicated, can't we check whether someone else has updated it?"
The OpenAI Git build introduced in the official post includes an improvement called explicit haves. It separates communicating the current position of the branch being updated from specifying the history that may be omitted from the pack.
This lets --force-with-lease check that the working branch hasn't moved from what was expected, while the pack itself can be built on top of the trunk. "Checking the branch update" and "optimizing the data sent" are treated as separate problems. The client-side improvements are also an element that makes pack reuse work.
Fetch: instead of generating a pack dedicated to each reader, select stored packs
In fetch negotiation, the client communicates the commits it has as have and the commits it wants to obtain as want. Pando maps these to ref versions and selects a combination of stored packs so that the necessary objects can be assembled.
In the earlier example, a reader that has T1 fetches the pack containing A1 and A2. On the other hand, a reader that has only up to T0 also needs the missing parts up to T1. Pando selects not only the working branch's pack but also its dependencies.
What differs per reader is mainly which packs are combined. Because the same pack can be delivered to many people, the burden of repeating compression for each reader can be reduced. However, some of the objects in each pack may overlap with what the reader already has.
Separating pack selection from delivery of the actual data
packfile-uri, which Pando uses, is a mechanism by which Git's response can tell the client the URI from which to download a pack. Pando leaves the pack in the response empty and has the client obtain the actual data via the URI.
Delivery of the actual data is handled by a service called Express, which caches blob storage data by region. There is also a separate cache for metadata. Therefore, the process in which Pando selects packs and the process of delivering large packs to readers can each be scaled up.
For example, when 100 CI machines fetch the same commit, the setup can deliver the same pack from the cache. This explains reducing the work of "producing data 100 times for 100 machines"; it does not mean the network transfer to the 100 machines itself becomes unnecessary.
What causal consistency guarantees, and what it doesn't align
Pando passes to readers the history and dependencies of the writers that produced the data being fetched. This design avoids the state where a pack can be fetched but the necessary base is missing. Updates to the same ref are coordinated so as to be consistent with processing them in sequence (serializability).
On the other hand, there is no guarantee that all refs are read as the state at the same instant. The official post gives an example where foo and bar are updated during clone negotiation.
| Reader | Observation of foo | Observation of bar |
|---|---|---|
| Reader A | Before update | After update |
| Reader B | After update | Before update |
The explanation is that such combinations are possible. Satisfying the dependencies of each history being fetched is different from aligning the observation points of all branches.
For development that updates separate working branches, being able to safely update and fetch each branch matters more than all refs being at the same point in time. However, for processes that assume a same-point-in-time state across multiple refs, that assumption needs to be reviewed. This is a note to keep in mind when reading the explanation of causal consistency.
Compaction: merge small packs and shorten the search
Just reusing packs would mean handing a large number of small packs to a reader who hasn't fetched for a long time. So Pando performs compaction in the background.
Taking the smallest-unit packs created by pushes as L0, it groups ranges of consecutive ref versions into a cover, and further groups wider ranges into higher levels. When a cover is sufficiently filled, the objects in that range are reconstructed into a single pack.
For illustration, suppose eight pushes produced eight small packs. If a reader needs all eight, and a consolidated pack for that range has been prepared, the reader can fetch them together instead of fetching the eight individually. The number 8 is for illustration and is not Pando's actual consolidation condition.
Compaction has the following two effects.
- It rearranges and compresses objects, reducing the transfer size and the number of packs to fetch.
- It groups ranges of history hierarchically, reducing the burden of tracing ref versions one by one when selecting the necessary packs.
The latter is similar to using a table of contents to skip unrelated chapters and find the place you want. A cover is useful for the search even before recompression is finished. It is not merely a process that concatenates multiple files.
The official post describes metadata reads at fetch time as O(log Δ). Δ is the shortest ref version distance between what the client has and the fetch target. This describes the search cost of pack selection, and does not mean the amount of data downloaded is also logarithmic.
Also, lower-level packs are retained even after consolidation. This is because there are readers that are only slightly behind, and uses that read individual objects. It needs to be understood separately from the usual "process of discarding old packs to reclaim space." The compaction section covers both transfer efficiency and the search before the fetch begins.
How to read the performance and availability
OpenAI reports that in production it reduced the median push latency by about 80%, and confirmed that clone and fetch can saturate a 10 Gbps link on a standard cloud VM.
These are results in OpenAI's environment. They are not figures indicating that you would get the same performance just by swapping your Git client. Also, the method-comparison simulation on the official page is a model for explanation and is distinguished from production benchmarks.
As of November 4, 2026, the official announcement has no general installation instructions or pricing information for Pando itself. How to share the technology is under consideration. The OpenAI Git fork that has been published is also separate from the availability of Pando itself.
Improvements on the Git client side can be followed through the public patch tracker. Besides whether you can deploy Pando yourself, I also want to watch which improvements are contributed back to Git itself.
Summary
Pando's design is connected as a flow: coordinate updates per branch, store the packs received in a push along with their dependencies, and reuse them in fetch. Negotiation that uses the trunk as a common base, ref versions, packfile-uri, regional caches, and compaction support this flow.
In exchange, it does not prioritize same-point-in-time state across all refs or duplicate-free transfer for each reader. In an environment with growing parallel pushes and repeated fetches, which guarantees and processes to keep and where to simplify is a design decision, and Pando is a case that lets us understand that decision concretely.
