A BROWSER-NATIVE GIT CONCEPT

A repo in
every tab.

Your browser is a working copy.
The bucket is the shared history.

Put a filesystem in the browser, let each tab make real Git commits, and use a write-ahead log to publish them to Cloudflare R2.

See how the pieces fit
01 / LOCAL FILES

A filesystem in the browser.

OPFS or IndexedDB holds the working files and .git. Edits and commits happen locally.

02 / INDEPENDENT COPIES

Each tab gets its own repo.

Give every replica its own storage namespace. One copy can change while another stays put.

03 / SHARED HISTORY

Publish changes to R2.

isomorphic-git creates Git objects. A walgit-inspired log coordinates what becomes shared.

THE IDEA, IN MOTION

Follow one commit.

THREE INDEPENDENT COPIESSIMULATION · NO UPLOADS
Tab ACOPY 01
~/replicas/tab-a/
notes.md original
.git/ objects + refs
main · c0ffee1
Up to date
Tab BCOPY 02
~/replicas/tab-b/
notes.md original
.git/ objects + refs
main · c0ffee1
Up to date
Another browserCOPY 03
~/replicas/browser-c/
notes.md original
.git/ objects + refs
main · c0ffee1
Up to date
Pages FunctionAuthenticate · check access · read / write R2SMALL API

Cloudflare R2

the published repository
revision 0
GIT PACKSpacks/<hash>.packBase history
WRITE-AHEAD LOGwal/<entry>.jsonNo new entries
SHARED POINTERmanifest.jsonmain → c0ffee1
Upload the objects first. Publish the manifest last.
READY / 04 STEPS

Same history. Separate working copies.

All three copies start at the same commit. Make a change in Tab A to see how it reaches the others.

This page illustrates the proposed architecture. It does not run Git, create an OPFS filesystem, or connect to an R2 bucket.

FOUR PIECES, CLEAR JOBS

The browser does Git.
The bucket keeps history.

Cloudflare Pages serves the website. These are the pieces behind the proposed experience.

01

OPFS / IndexedDB Local filesystem

Store working files, the Git index, objects, and refs. Start with LightningFS for isomorphic-git, or use an OPFS adapter. Unpublished work lives here.

02

isomorphic-git Git in JavaScript

Create commits, read trees, make branches, and build packs inside the browser. A Web Worker can keep Git work off the page’s main thread.

03

A walgit-inspired WAL Publication & recovery

Upload immutable Git data and log entries. Advance one manifest with a conditional write. New copies recover published history from a checkpoint and the log.

04

Pages Function + R2 Access & shared storage

Check repository access in the Function. Use R2’s ETag conditions to reject stale writes. Keep bucket credentials on the server.

THE MOMENT A PUSH BECOMES REAL

“Only if nobody
got here first.”

Read the manifest and its ETag together. A write succeeds only if that ETag still matches. If it changed, fetch the new history and reconcile.

R2 performs this check atomically. A read followed by an unconditional overwrite would leave a race.

inside the Pages FunctionJavaScript
// Objects and WAL entry are already uploaded.
const result = await env.REPOS.put(
  manifestKey,
  JSON.stringify(nextManifest),
  { onlyIf: { etagMatches: expectedEtag } }
);

if (result === null) {
  // Another writer changed the manifest.
  return new Response("Fetch and reconcile", {
    status: 412
  });
}
One successful manifest update = one published state.
DETAILS THAT MATTER

The edges of the idea.

Tabs need deliberate isolation.

Browser storage is shared by origin. Give each copy its own directory or database namespace. Tabs using the same copy need a local lock.

A push is a publish, not a keystroke.

R2 documents a limit of one write per second to the same key. Keep frequent edits local and publish commits in batches when needed.

The manifest must stay fresh.

Read it through the R2 binding and return it without caching. Immutable packs can be cached. Give every update a new sequence and operation ID.

This is an adaptation of walgit.

Upstream walgit is a Rust server. A browser version needs a JavaScript WAL implementation. Standard git clone would also need a Git protocol endpoint.