chadtmiller.com/knotless
git that lives in your Atmosphere account
7
# 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 `git` with a remote helper, or plain git
smart HTTP for reads.
- A source of truth besides the PDS. `knotless-serve` keeps 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
```mermaid
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](collaboration.md#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](collaboration.md#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](storage.md#what-a-stock-pds-offers) has the table of limits and
what each one forces.
## Reading order
1. [How repositories are stored](storage.md): which records hold a
repository, and how push, fetch and compaction work within a stock PDS.
2. [Naming and identity](identity.md): what identifies a repository, and
how renames, transfers and commit authors work.
3. [Access](access.md): how knotless logs in, what it asks for, and how
private repositories are kept and read.
4. [Gateway](gateway.md): how a collaborator writes to an account that is
not theirs, and how branches are protected.
5. [Collaboration](collaboration.md): how pulls, issues, states, merges,
forks and moderation work when each person writes to their own account.
6. [knotless-serve](serve.md): what the optional server's cache, appview,
moderation and web pages do, and how fresh they are.
7. [The runner](runner.md): which workflows run, when, in what order, and
with which caches and secrets.
8. [Shared steps](reusable-workflows.md): how a workflow uses steps
another repository publishes, pinned so they cannot change under it.
9. [Merge queue](merge-queue.md): how queued pulls are checked and landed
together in batches.
10. [Releases](packages.md): how a repository publishes versions of a
package, as records that never change.
11. [Integrity and trust](security.md): what each party can and cannot do.
12. [Prior art](prior-art.md): what came before, and what knotless takes
from it.