SwarmOps
Pre-release · milestone M8 · MIT licence

A git replacement for coding-agent swarms

Many writers, one store, one JSON envelope per command. SwarmOps is fully embedded, written in Rust: one program, no daemon, no external database, for 10 to 50 agent processes committing to one repository on one host.

  • Full fsync, no fast mode
  • Refs move by compare-and-swap
  • MCP server built in

Why SwarmOps

A swarm of agents is not a team of people

It needs other things from source control. SwarmOps is built around three of them.

Many agents write at once

Tens of agent processes commit into one repository on one host. Object writes take no lock, metadata writes queue in the kernel for one lock, and refs move only by compare-and-swap.

Agents read answers, not prose

Every command prints one JSON envelope: a stable E_* code, a fixed exit code, whether retrying can help, machine-readable details and a hint, often the next command to run.

Agents get killed mid-write

A commit returns only once its journal frame is on disk, a crash leaves no stale lock, and the next writer repairs what a crash cut short. Crash sweeps, injected I/O errors and simulated power losses test it through the real binary.

32 writersprocesses writing to one store at once, 1,000 transactions each, in the two contention tests: at store level, and as commits through the binary
120.9 / smetadata transactions through the one lock, with full fsync, at store level: a changeset record each, no objects, no ref move. Whole commits through the binary: 72.7 / s
427 msmedian commit through the binary with 32 processes committing at once, about 406 ms of it the wait for the lock (p99 656 ms, longest 918 ms); a commit alone takes 21 ms
13.8×object writes on 32 threads against one, in one process (15.5× beside a busy writer), as the disk and ext4's journal commit concurrent fsyncs together

From just perf after M1's last merge (2026-10-16), with the writer lock that ships, on one busy development host (WSL2, ext4 on a virtual disk, 32 threads; load 14.6–16.4). docs/perf.md has every run, with its spread and load. No git baseline has been measured, and a side-by-side benchmark against git worktrees is not on the roadmap.

Inside the store

One lock for metadata, none for objects

Every operation is a library function: swarmops-repo does the work, swarmops-ops exposes it as the StoreOps operations, and the binary parses one command line, calls one operation and prints its envelope.

An isometric drawing of the store as a machine: commit cartridges wait on a feed rail behind one gate, the metadata lock, which lets one through at a time in no strict order; a press stamps each one as a frame onto a paper tape, the append-only journal; beside it, object pellets drop into sixteen bins with no gate.
Fig. 1, the native store as a machine: writers wait for one lock, which the kernel hands to one of them at a time, in no strict order, and each commit becomes one CRC-framed frame appended to the journal; objects need no lock. Open the interactive versionOpen the interactive version, drawn with Anatomy (MIT, notice).
  1. swarmops-cliswarmops and so: the commands, and swarmops mcp
  2. swarmops-apithe contract: request and response types, E_* codes, the envelope
  3. swarmops-opsLocalOps: the operations over a local store
  4. swarmops-gitgit import and export: git's bytes, through gix
  5. swarmops-repothe repository engine
  6. swarmops-mergetree and line diffs and the three-way merge; no I/O
  7. swarmops-storeobject files and the metadata store
  8. swarmops-coreids, paths and the canonical codec; no I/O
store/
├─ FORMAT   # the layout version, written last by init
├─ REPO     # the repository's id and name
├─ objects/ # one self-verifying file per blob,
│  ├─ 00/ … ff/  # tree and changeset: 256 fan-outs
│  └─ tmp/       # staged, synced, then linked
└─ meta/
   ├─ journal.log  # append-only, CRC-framed: the truth
   ├─ journal.off  # each frame's offset
   ├─ journal.ckpt # how far it is materialized
   ├─ idx/         # the changeset index: its columns,
   ├─ parents.log  # each changeset's parents,
   ├─ sorted/      # and its sorted views
   ├─ derived/     # derived values: a cache, never synced
   ├─ external_ids/ # git commit ids, imported and exported
   ├─ refs/        # one file per ref, moved by CAS
   └─ lock         # one flock for every writer

SwarmOps and git

Judged strictly, ahead and behind

SwarmOps is not a general-purpose git replacement: it imports from git (M3) and exports to it (M8), so today's pipelines keep running. Where the two compare, this is where it stands.

DimensionSwarmOps todayAgainst git
HashBLAKE3-256 ids with the object's kind and layout version hashed in; every read re-hashes.Ahead of git's default SHA-1: 128-bit collision resistance against at most 80 bits, by cryptanalysis, not by benchmark. Parity with git's opt-in SHA-256.
Order of writesEvery commit and ref move is one frame of one journal, under one lock: a single global order.Ahead by design
Stale locksThe writer lock is an flock, released by the kernel when its holder dies.Ahead by design A git process killed with SIGKILL while it holds a .lock file leaves the file behind, to be removed by hand.
DurabilityFull fsync is the only mode: an object is synced before a journal frame names it, and a commit returns after its frame's fdatasync.Ahead by design Git's defaults (2.43) sync packs, pack metadata and the commit graph, not loose objects, refs or the index.
Machine-readable contractOne JSON envelope per command, with a stable E_* code, an exit code, retryable, details and a hint; a JSON Schema for every request and response.Ahead by design for errors, which git reports as free text on stderr. Git's plumbing and --porcelain formats narrow the gap for output. Known gap: four codes are not raised yet.
Commits from many writersConcurrent commits all succeed, short of a lock wait past 10 s or, since M1.6, a snapshot that meets a new commit in each of its 32 rounds (the retryable E_BUSY).Parity with agents in separate git worktrees, on lost commits and lock failures.
One ref by compare-and-swapref set --expect or --create, ref delete --expect; setting a ref to the target it holds is a replay. Since M1.6, commit --advance moves a ref in the commit's own transaction, or records nothing.Parity with git update-ref, plus the replay.
Reading a path at a revisioncat REV:PATH and ls REV[:PATH], without a checkout.Parity with git show and git ls-tree.
Offline, on one hostNo network code: every operation runs against the host's own store.Parity
Throughput of many writersWriters take turns, each commit holding the store-wide lock across two journal fdatasyncs, and two fsyncs more when it moves a ref.Behind by design Agents in git worktrees serialize nothing, and git's defaults sync none of a commit's files.
Refs beyond one compare-and-swapmain and release/<name> only, moved one per call.Behind No multi-ref transactions, tags, free-form branch names or symbolic refs.
Breadth of readsOne file or one directory per call; since M2, a file from a byte offset or by a range of lines, and a directory with the directories below it to a depth, each file with its size; sparse workspaces come in M5.Behind Git has batch reads, git grep <tree> and sparse checkouts.
Storage on diskOne file per object, zstd-compressed when that makes it smaller; no packs, no delta compression.Behind by design in bytes and in files, against git's packs.
ConflictsSince M3, E_GIT_IMPORT_DIVERGED signals a conflict when a git import's target and the git history have diverged. land merges three-way without a checkout and reports a conflict as E_CONFLICT with a JSON report: each conflicting file's kind, its hunks and the target's changeset and agent that last changed it. land --resolve resolves them without a checkout, path by path, against the report's theirs.Parity with git merge-tree --write-tree, which merges without a checkout and reports typed conflicts. Resolving without a checkout is one request here, where git takes plumbing (git hash-object, a temporary index, git commit-tree).
ReviewNothing today; change requests, approvals and a landing gate come in M7.Behind today Git ships patch-review tools, and Gerrit and GitHub add review on top. M7 is not yet comparable
Sync between hostsSince M8, through a git remote, one writing host at a time: the writer exports, then pushes with git, and the others fetch with git, then import; two hosts that both write stop rather than merge. Offline-first fetch and push against one upstream come in M11.Behind Git clones, fetches and pushes over any transport, and bundles without one.
EcosystemThe CLI, swarmops schema, an MCP server, git import since M3 and git export since M8. No hooks, signing, submodules or LFS.Behind after v1 too: git parity is not a v1 goal, and git has MCP servers as well.
MaturityPre-release: v0.1.0 is the first tag, and the first spec dates from 2026-10-15.Behind Git has hosted itself since 2005.
Landingland merges an agent's change onto main and publishes it as one changeset in one transaction, merging again and retrying by itself when main moves; a retry with the same key replays it. The landing model chosen by measurement comes after the pilot.Ahead by design of git alone, whose push refuses a moved branch and leaves the fetch, the merge and the retry to the pusher; not yet measured under a swarm's load.

Every dimension of the README's comparison, which gives each verdict's evidence. Ahead: built, with evidence. Ahead or behind by design: judged from the design and the code, never measured against git. Not yet comparable: not built yet. No git baseline has been measured; the verdicts come from the design, the code and git's own documentation and source.

Quick start

Build it, make a store, commit

Linux, x86_64 or aarch64, glibc 2.34 or newer. A store must live on a native Linux filesystem. so is the short name of swarmops: the same program.

Install
curl -fsSL https://get.swarmops.cc/install.sh | bash        # swarmops and so, into ~/.local/bin
First commands
export SWARMOPS_REPO=~/.local/state/swarmops/repos/acme   # or --repo <store> on each command
export SWARMOPS_AGENT=agent/worker-1                 # or --as <family>/<instance>

so init "$SWARMOPS_REPO" --name acme
printf '#!/bin/sh\necho hello\n' > run.sh                 # the client file the second change reads
cat > changes.json <<'EOF'
[{"path": "src/lib.rs", "op": "put", "content": {"inline": "pub fn f() {}\n"}},
 {"path": "run.sh", "op": "put", "content": {"file": "./run.sh"}, "mode": "exec"}]
EOF
so commit -m "first" --changes changes.json --advance main  # creates main at the new changeset
so cat main:src/lib.rs
so ls main:src
so show main
so log -n 20
so verify --deep

A change list is a JSON array of puts and deletes; a put's content is {"inline": …}, {"base64": …}, {"file": …}, a file the CLI reads, or {"blob": …}, a blob stored before with so blob put. --advance main moves main to the new changeset in the commit's own transaction, only if main still names the commit's parent (for this first commit, only if main does not exist yet); otherwise the commit records nothing and fails with E_CAS_MISMATCH. Every command, revision and exit code is in the README.

MCP

The operations, as tools

swarmops mcp serves the store to an agent on stdin and stdout. Each tool's arguments are the operation's request, and its result is the envelope the CLI prints. The server speaks MCP 2026-07-28, and 2025-11-25 and 2025-06-18 to clients that open with initialize.

Claude Code
claude mcp add swarmops -- \
  so mcp --repo <store> --as <agent>
.mcp.json
{"mcpServers": {"swarmops": {
  "command": "swarmops",
  "args": ["mcp", "--repo", "/path/to/store"],
  "env": {"SWARMOPS_AGENT": "${SWARMOPS_AGENT:-}"}}}}
  • repo_info
  • resolve_rev
  • read_file
  • read_tree
  • show
  • log
  • path_history
  • diff
  • op_log
  • ref_list
  • ref_set
  • ref_delete
  • undo
  • commit
  • land
  • blob_put
  • blob_has
  • verify

Roadmap

Built in the open, milestone by milestone

Pre-release: v0.1.0 is the first tag; no crate is published, and every crate of the workspace is at 0.1.0. The milestones follow the design specs.

Delivered

  1. M0the workspace and its gates, the canonical codec and its golden vectors
  2. M1the single-agent core: commit, read, log and ref compare-and-swap; crash sweeps
  3. M1.5hardening: CI, fault injection, power-loss images, the MCP server
  4. M1.6the code-review follow-ups: the error contract an agent sees, structured verify findings, and commit --advance, the commit primitive M4's landing reuses
  5. M2diff, history and the derived-data engine
  6. M3git import
  7. M4merge and landing, and the operation log with undo
  8. M8git export, so work lands back in git

In development

  1. PilotSwarmOps' own repository through the round trip: imported, changed and landed by agents, exported back to git

Usable by one agent from M1; by a swarm, landing through MCP, from M4; on a git repository and back, from M8, in the first release; with workspaces from M5; with review from M7; as the complete v1 at M9; across hosts, offline-first, from M11.