gitd 0.7.0 is out →
GITD
Git for agent sandboxes

A git checkout
for every agent

Run ten or a hundred AI agents on the same repository. gitd gives each one its own writable copy in milliseconds, and turns their commits into branches — your token never enters a sandbox.

Apache-2.0 · v0.7.0 · Linux · any git host

$ REPO=https://github.com/kubernetes/kubernetes$ gitd prepare $REPO /work/agent-1✓ fetched once, template built · 12.2 s$ gitd prepare $REPO /work/agent-2✓ ready · overlay · 23 ms · 4.3 MB# agent-1 edits, tests and commits$ gitd publish /work/agent-1 agents/fix✓ pushed agents/fix with gitd’s token
GIT HOSTGITDSANDBOXESgithub.comkubernetes+ agents/fixPOOLobjectsTEMPLATEfiles, read onlyTOKENkept here, never handed outfetch once+ layerAGENT-1credentials: none+ layerAGENT-2credentials: nonecommitpush, with the token
What changes

A hundred clones,
or a layer each

Give a hundred agents the same repository and you usually get a hundred clones, a hundred fetches and a token in every sandbox. With gitd they share one pool and one template, and each gets a thin layer of its own.

Without gitd, three sandboxes each clone the repository and hold a token; with gitd, one pool and one template are shared, each sandbox's workspace is a thin copy-on-write layer, and gitd keeps the token and publishes the branch.
How it works

One pool, one template,
a layer per agent

gitd runs the official git binary in a closed environment, and builds everything aside before moving it into place.

GIT HOST1 fetchPOOL · app.gitsynced · kept whole

Pool

One bare repository per repository you use, fetched once and kept in sync. The only copy that talks to the network.

templates/acme/app/3f9c2e1/
cmd/
server.go
routes.go● shared · read only
go.mod
README.md

Template

One checkout per commit, written once and shared, read only, by every workspace of that commit.

templateshared · read onlyagent-1its changes onlyagent-2its changes onlyagent-3its changes only

Workspace

A copy-on-write layer on the template, mounted where the agent works. Plain git; only what the agent changes takes disk.

PREPAREWORKCOMMITPUBLISHagents/fix-flaky-testplain git, in its own copy

Publish

The agent commits; gitd pushes the commits to a new branch from the pool, with the token it keeps. Never forced.

Every decision, with its reasons: DESIGN.md

Benchmark

100 agents,
one repository

Each agent gets its own copy of kubernetes/kubernetes, on a GitHub runner, 4 CPUs. Measured by a workflow anyone can run again.

Time for all 100
100 × git clone980 s
100 × gitd prepare14.5 s
Disk
100 × git clone41,870 MB
100 × gitd prepare850 MB

67×

faster

49×

less disk

Each workspace
100 × git clone9.8 s
100 × gitd prepare23 ms
Network
100 × git clone100 fetches
100 × gitd prepare1 fetch

First workspace: 12.2 s, the fetch and the template.

99 workspaces in 2.0 s, 8 at a time; all 100 clean to git.

4.3 MB each, mostly git’s own index.

Run the benchmark yourself →

Get started

From nothing to a branch
in five steps

Or run all of it with Docker: two sandboxes, copy-on-write workspaces, and no token in either.

  1. 01

    Run gitd

    One binary, or the image. It answers on a Unix socket; gitd-mount, a tiny root helper, mounts the copy-on-write workspaces so gitd needs no privilege.

    $ docker run -d --name gitd \
      -v gitd:/var/lib/gitd \
      ghcr.io/dooservice/gitd
  2. 02

    Give it a credential

    Credentials live in gitd’s configuration, each naming where its secret is, never the secret. No agent or sandbox ever holds one.

    [[credentials]]
    prefix = "https://github.com/your-org"
    token_env = "GITHUB_TOKEN"
  3. 03

    Prepare a workspace per agent

    The first one fetches; every one after takes milliseconds and almost no disk.

    $ REPO=https://github.com/your-org/app
    $ gitd prepare $REPO /work/agent-1
  4. 04

    Let the agent work

    Mount the workspace into its sandbox. It is a normal git checkout: edit, test, commit.

    $ git commit -am "Fix the login"
  5. 05

    Publish a branch

    gitd pushes the agent’s commits to a new branch with the token it keeps, ready for a pull request.

    $ gitd publish /work/agent-1 agents/fix
For agents and platforms

Call it from wherever
your agents run

No call takes a credential: gitd reads and publishes with the ones it keeps.

Eight tools for Claude Code, Cursor or any MCP client. None takes a credential.

{
  "mcpServers": {
    "gitd": {
      "command": "gitd",
      "args": ["mcp"]
    }
  }
}

Python

Docs →

pip install gitd

from gitd import Gitd

with Gitd() as gitd:
    ws = gitd.prepare(
        "https://github.com/acme/app",
        "/work/agent-1",
    )
    gitd.publish(ws.path, "agents/fix")

TypeScript

Docs →

npm install gitd-client

import { Gitd } from "gitd-client";

const gitd = new Gitd();
const ws = await gitd.prepare({
  url: "https://github.com/acme/app",
  path: "/work/agent-1",
});
await gitd.publish(ws.path, "agents/fix");

CLI and API

Docs →

The same binary is its own client; ConnectRPC (gRPC, Connect) on the socket for any language.

gitd prepare URL PATH
gitd describe PATH
gitd publish PATH BRANCH
gitd audit --since 24h
Security

Agents work.
gitd holds the keys.

What gitd protects, from whom, and what it leaves to you is written down, each claim with the code or the test behind it.

SANDBOX

git push origin agents/fix

credentials: none

GITD · PUBLISH FROM THE POOL

POST /acme/app.git/git-receive-pack

Authorization: Basic ••••••••••••

No token in a sandbox

Credentials stay in gitd and reach git only as a header scoped to one repository. Pushing happens from the pool, never from the workspace an agent could tamper with.

gitd audit --since 24h

09:12:04agents/fix-login✓ pushed
09:14:51main✕ BRANCH_REFUSED
09:20:17agents/deps✓ pushed

Publishing under rules

New branches only, never forced, and under a prefix you choose (agents/…). Every publish, done or refused, is in an audit log.

workspace/.git/config

user.name = agent-1✓
remote.origin.url = …✓
filter.x.clean = …✕ CONFIG_REFUSED

A workspace cannot reach out

Before gitd runs git in a workspace, its configuration and git directory are checked: nothing in them may run a command or lead gitd elsewhere.

GITD

uid gitd · no capabilities

GITD-MOUNT

root · 4 capabilities

mount overlay templates/… + layers/… → workspaces/agent-1

✓ only inside the directories root configured

No privilege for gitd

gitd runs unprivileged. gitd-mount, a small root helper, mounts overlays only inside the directories root configured, for gitd’s user only.

Works with

Any git host,
the sandboxes you use

Git hosts, over HTTPS or SSH

  • GitHub
  • GitLab
  • Bitbucket
  • Gitea
  • Forgejo
  • Codeberg
  • Your own

Nested GitLab groups, any port, submodules included.

Sandboxes

  • Docker, Podman, containerd, gVisorNow
  • bubblewrap, nsjail, LandlockNow
  • CI runnersNow
  • Firecracker microVMs, E2B, Modal, DaytonaRoadmap 0.8
  • Kubernetes, Kata ContainersRoadmap 0.9

The roadmap →

FAQ

Questions,
answered

How is it different from git worktree?

Worktrees share one repository’s refs and configuration, cannot check out the same branch twice, and break when mounted into a container. A gitd workspace is a repository of its own that borrows objects from a pool and files from a template.

Does gitd need root?

No. gitd runs unprivileged; copy-on-write workspaces are mounted by gitd-mount, a small helper that holds only the capabilities mounting an overlay takes. Without it, gitd writes each workspace’s files and still fetches once.

Which git hosts does it work with?

Any host over HTTPS or SSH, on any port: GitHub, GitLab with nested groups, Bitbucket, Gitea, Forgejo, Codeberg or your own. Submodules included.

What does an agent get?

A normal git checkout: it can edit, run, diff, stash and commit with plain git. When it is done, gitd publishes its commits as a new branch.

Is git LFS supported?

Not yet: LFS files are pointers in a workspace, and gitd says which. Support will come if users need it.

What does it cost?

Nothing. gitd is open source under the Apache License 2.0.

Open source, Apache-2.0

Give every agent its own copy.
Keep the token.