chadtmiller.com/knotless
git that lives in your Atmosphere account
Design
How knotless works, and why it works that way. This page gives the goals, the pieces and how they fit, and an order for reading the pages after it. It assumes you know git, and roughly what atproto is: accounts that hold records and blobs on a server of their choosing, their PDS.
Goals
- Git in your own account. A repository is a few kinds of record in its owner's atproto account, and nowhere else. Nothing is installed on the PDS.
- Any stock PDS. Public repositories work against the reference PDS
(
@atproto/pds) as shipped, under its default limits. Private repositories also need the permissioned-data alpha of that PDS. On a PDS without it, every repository is public. - Fast enough for agents. Every operation is planned around round trips to the PDS. A warm push takes two, and a fetch of a small push one. Writers on separate branches never contend. Writers pushing straight to one branch land one per round, so agents work best on a branch each, or commit through a gateway, which queues commits per branch.
- Git objects need no trust. Every object is named by its hash and checked by the reader that indexes it. This goal is only partly met: where a ref points is read from the owner's PDS and not yet checked against the account's signed commits.
- Cheap to run. One server binary does every optional job, on one small VPS.
Non-goals
- Replacing git. Clients are plain
gitwith a remote helper, or plain git smart HTTP for reads. - A source of truth besides the PDS.
knotless-servekeeps a cache that can be thrown away and read again. It is still needed for some jobs: a collaborator pushes through a gateway, and listing a public repository's pulls and issues takes an appview. Anyone can run either. - A second way to write. The pages write as the account logged in to them (stars, follows, comments, opening, merging and queueing pulls, sharing), through the same code the CLI uses. A repository's content changes only through git, the CLI and agents.
- Running untrusted code by default. A runner checks a pull as it moves only if its author and its head repository's owner each write to the repository or are agents it names; an agent's runs get no secrets. Anyone else's runs only when the owner asks, and then once.
The pieces
flowchart LR
clients[git + remote helper,<br/>knotless CLI, knotless-mcp]
subgraph pds[Owner's PDS]
records[(dev.knotless.* records<br/>and pack blobs)]
space[(Spaces:<br/>private repositories)]
end
others[(Other accounts' PDSes:<br/>pulls, issues, comments, checks)]
jet[Jetstream or firehose]
subgraph serve[knotless-serve, optional]
gw[Gateway]
cache[Mirrors and smart HTTP]
index[(Appview index)]
web[Web pages]
end
run[knotless-run]
clients -->|owner reads and writes| records
clients -->|collaborators write| gw
gw -->|writes as the owner| records
clients -->|each writes their own| others
records & others --> jet
jet --> cache & index
jet -->|doorbell| run
records -->|refs| run
run -->|checks| others
cache & index --> web
knotless is the CLI and the git remote helper (git-remote-knotless), in
one crate. It talks to PDSes directly, with no server in between. The owner
writes their own records. Everyone else writes pulls, issues, comments and
states into their own accounts, and pushes to the owner's repository
through a gateway.
knotless-mcp offers the CLI's operations to agents as MCP tools over
stdio. Writes go through the same code as knotless commit, fork, pull
and merge. It acts as the logged-in account, or as KNOTLESS_IDENTIFIER
with KNOTLESS_PASSWORD. A public knotless-serve offers the same tools
over HTTP at /mcp, to any account that signs in with atproto OAuth,
unless the operator refuses it, as one taken down.
knotless-serve is one binary with several optional jobs: a read cache, an
appview that indexes records across accounts, a gateway, a moderation
service and the web pages. It follows Jetstream or the firehose, the public
streams of record changes, to know when to sync.
knotless-run runs workflows when a ref moves or a pull changes, and lands
merge queues. A stream event only tells it which repository to read again
from the owner's PDS. It writes each run as a check record in its own
account.
knotless-triage triages issues: a classifier chooses their labels, and
it writes them as label records in its own account, which count once the
owner names it a triager (see Labels).
knotless-agent review reviews the pulls that ask its account for one:
a command, Claude Code by default, reads each, and it writes the review
as its own comment, which counts as advice and never toward a branch's
required reviews (see Review agents).
Constraints
knotless runs against the reference PDS as shipped, and that PDS's limits shape the rest of the design. It has no locks and no per-record compare-and-swap, so each ref gets one from create-only claim keys. Only an account writes to its own records, so collaborators need a gateway. Blob size, request size, writes per call and rate limits decide how packs are stored and pushes are batched. What a stock PDS offers has the table of limits and what each one forces.
Reading order
- How repositories are stored: which records hold a repository, and how push, fetch and compaction work within a stock PDS.
- Naming and identity: what identifies a repository, and how renames, transfers and commit authors work.
- Access: how knotless logs in, what it asks for, and how private repositories are kept and read.
- Gateway: how a collaborator writes to an account that is not theirs, and how branches are protected.
- Collaboration: how pulls, issues, states, merges, forks and moderation work when each person writes to their own account.
- knotless-serve: what the optional server's cache, appview, moderation and web pages do, and how fresh they are.
- The runner: which workflows run, when, in what order, and with which caches and secrets.
- Shared steps: how a workflow uses steps another repository publishes, pinned so they cannot change under it.
- Merge queue: how queued pulls are checked and landed together in batches.
- Releases: how a repository publishes versions of a package, as records that never change.
- Integrity and trust: what each party can and cannot do.
- Prior art: what came before, and what knotless takes from it.