MarkTechPost4h agoenergy 104

Reason Machines Open-Sources Aspen, a Git Server Built for AI Agents

Reason Machines has released Aspen , an open-source Git server for AI agent traffic. It targets repositories that hundreds of agents push to at once. Aspen independently implements the design OpenAI described for Pando. TL;DR Size: One Rust binary. Branches live in PostgreSQL (or SQLite), packs live in object storage. Runs on: A laptop ( aspen dev ), one host via Docker Compose, or Helm and Terraform clouds. Performance: Pushes to different branches never wait on each other, and fetches reuse stored packs. Best: 567 pushes per second sustained for 5 minutes on one 8-core server (self-reported). Worst: Landing on main runs at about 4 per second, and the project is pre-alpha. Bottom line: Best: per-branch writes remove the per-repository push ceiling. Worst: no releases, no web UI, and the 24-hour durability gate is not met. What is Aspen? Aspen is a self-hostable Git server from Reason Machines that scales writes per branch, not per repository. It speaks plain Git over HTTPS and SSH. Packs go to S3-compatible object storage; branch history goes to PostgreSQL. Why do classic Git servers struggle with AI agents? An agent pushes every few minutes, and CI fetches after each push. Per Reason Machines , a classic server queues every push to a repository together. It also builds a fresh pack for every fetch. Server work grows with clients, not with new code. OpenAI hit this wall and built Pando. It cut median push latency by nearly 5x and serves nearly a petabyte daily. How does Aspen work? Aspen follows Pando and drops 2 claims most Git servers keep. Per-branch compare-and-swap Each branch is a chain of versions in one Postgres row. A push uploads its pack, then moves the branch with one compare-and-swap. Pushes to different branches never wait for each other. If 2 pushes race on one branch, one wins. The other gets Git’s usual “fetch first” error. The cost: branches may be read at slightly different moments. Stored packs instead of per-client packs Aspen never unpacks a push. It checks the pack, completes missing bases and stores it as-is. A fetch streams stored packs the client lacks, recompressing nothing. Signed URLs can serve packs from a CDN. Trunk-only advertisement and compaction A push sees only the trunk branch, so negotiation stays small with a million branches. Background compaction merges packs 16 at a time, in levels like a log-structured merge tree. A client far behind gets a few dozen packs, not thousands. Depth-1 CI fetches get a tip pack built once and served to every job. Auto play</button></div> <svg viewBox="0 0 820 250" aria-label="Aspen push and fetch flow"> <defs><marker id="aspA" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto"><path d="M0 0L10 5L0 10z" fill="#7cb8f5"/></marker></defs> <g font-family="Inter,sans-serif" font-size="13"> <rect x="20" y="40" width="150" height="60" rx="10" fill="#1d1a12" stroke="#3a3526"/><text x="95" y="66" fill="#f3efe3" text-anchor="middle" font-weight="700">Agent</text><text x="95" y="85" fill="#a59f8c" text-anchor="middle" font-size="11">git push</text> <rect x="20" y="150" width="150" height="60" rx="10" fill="#1d1a12" stroke="#3a3526"/><text x="95" y="176" fill="#f3efe3" text-anchor="middle" font-weight="700">CI job</text><text x="95" y="195" fill="#a59f8c" text-anchor="middle" font-size="11">git fetch</text> <rect x="320" y="85" width="170" height="80" rx="12" fill="#1d1a12" stroke="#3690ea" stroke-width="1.5"/><text x="405" y="120" fill="#f3efe3" text-anchor="middle" font-weight="800">Aspen</text><text x="405" y="140" fill="#a59f8c" text-anchor="middle" font-size="11">stateless server</text> <rect x="640" y="30" width="160" height="70" rx="10" fill="#1d1a12" stroke="#3a3526"/><text x="720" y="60" fill="#f3efe3" text-anchor="middle" font-weight="700">Postgres</text><text x="720" y="80" fill="#a59f8c" text-anchor="middle" font-size="11">1 row per branch</text> <rect x="640" y="150" width="160" height="70" rx="10" fill="#1d1a12" stroke="#3a3526"/><text x="720" y="180" fill="#f3efe3" text-anchor="middle" font-weight="700">Object storage</text><text x="720" y="200" fill="#a59f8c" text-anchor="middle" font-size="11">packs as pushed</text> <path id="aspE0" d="M170 70 L320 110" stroke="#3a3526" stroke-width="2" fill="none"/> <path id="aspE1" d="M490 140 L640 180" stroke="#3a3526" stroke-width="2" fill="none"/> <path id="aspE2" d="M490 110 L640 70" stroke="#3a3526" stroke-width="2" fill="none"/> <path id="aspE3" d="M320 140 L170 180" stroke="#3a3526" stroke-width="2" fill="none"/> <circle id="aspPk" r="7" fill="#3690ea"><animateMotion id="aspMo" dur="1.2s" repeatCount="indefinite"><mpath href="#aspE0"/></animateMotion></circle> </g></svg> <p class="cap" id="aspCap"></p> </div> <div class="pane" id="p-race"> <div class="btns"><button class="b" id="aspRun"> Run 128 writers</button><button class="b" id="aspReset">Reset</button></div> <div class="race"> <div class="rc"><h4>Classic server: one queue per repo</h4><div class="dots" id="aspD1"></div><div class="ct" id="aspC1">0 / 128 done</div></div> <div class="rc"><h4>Aspen: one CAS per branch</h4><div class="dots" id="aspD2"></div><div class="ct" id="aspC2">0 / 128 done</div></div> </div> <p class="note">Toy model, not a benchmark. Amber = waiting, blue = done. Mirrors the illustrative simulation in the Reason Machines launch post.</p> </div> <div class="pane" id="p-bench"> <div class="btns"><span class="sub">Pushes per second to one repository, each durable before acknowledgement</span></div> <div class="bar"><div class="bl"><span>Cursor Origin, S3 Standard (published)</span><span>120</span></div><div class="tr"><div class="fl" data-w="21.2" style="background:#6d6650"></div></div></div> <div class="bar"><div class="bl"><span>Cursor Origin, S3 Express One Zone (published)</spa

read the original at MarkTechPost →