Expand description
An atproto repository: a signed commit over a Merkle search tree, and the CAR file that carries it.
This is the layer that turns records a server happens to hold into something a stranger can check. Three steps, and each one is worthless without the next:
- Every record is encoded canonically and named by the hash of those
bytes —
didbot-data. - Those names are keyed into a Merkle search tree whose shape no
implementation gets to choose, giving one root CID over the whole
repository —
didbot_data::mst. - An account’s key signs a
Commitnaming that root, and the whole lot goes out as acarfile.
A Proof is the fourth step and the one that makes the other three worth
having: the blocks on one key’s path through the tree, so a reader can
check that one record is in the repository without being handed all of it.
read_record is that reader, for a record another server holds.
A repository is built whole by a Builder, and after that changed one
record at a time by a Draft, which re-signs the edited tree as the next
commit. Both land on the same repository for the same records.
use didbot_key::SigningKey;
use didbot_repo::Builder;
let key = SigningKey::generate();
let mut repo = Builder::new();
repo.insert(
"com.example.thing",
"3lqmnopqrstuv",
&serde_json::json!({"text": "quernstone", "emoji": "🜁"}),
)?;
let repo = repo.commit("did:web:kestrel.example", "3lqmnopqrstuv", None, &key);
// The signature is what a reader checks, with a key it got from the DID
// document rather than from us.
repo.commit().verify(&key.verifying_key())?;
let car = repo.to_car();§Why this is not in didbot-pds
The same reason didbot-data is not: this sits below the server
rather than inside it. It has no clock, no store, no notion of an account
and no opinion about when a commit should happen — a caller hands over
records, a revision and a key, and gets bytes. That makes the whole of it
testable without a server, and it keeps the largest crate in the workspace
from growing the two things most likely to be reused elsewhere.
§What is deliberately absent
A reader of its own exports. car::read and read_record read
what another server sends. Nothing here reads this crate’s own output back
to check it: the implementation that checks an export is not ours, and a
decoder written to check our own encoder would agree with it about
anything the two got wrong together.
A history. prev is a parameter and this crate will chain commits if
a caller supplies one, but nothing here keeps them.
Blobs. No lexicon in this project carries one, so there is no block in a repository here that is not DAG-CBOR.
Modules§
- car
- CARv1: the file a repository is handed over as.
- commit 🔒
- The commit: the one block in a repository that is signed.
- repository 🔒
- A repository: records, the tree over them, and the commit that signs it.
- verify 🔒
- Reading one record out of another server’s repository, from the proof
that server answers
com.atproto.sync.getRecordwith.
Structs§
- Builder
- A repository under construction.
- Commit
- A commit before it is signed.
- Draft
- A signed repository being changed, one record at a time, into its next commit.
- Proof
- The blocks that prove what a repository says about one key.
- Repository
- A signed repository, ready to be handed to somebody who does not trust us.
- Rewritten
- The blocks an edit added and took away.
- Signed
Commit - A commit and the signature over it.
Enums§
- Commit
Error - Why a commit could not be trusted.
- Commit
Key - A key another repository’s commits are signed with, as its DID document publishes it. atproto signs with either of two curves; this crate signs with secp256k1 only and reads both.
- Proof
Error - Why a proof establishes nothing about a record.
- Repo
Error - Why a repository could not be assembled.
Constants§
- REPO_
VERSION - The repository format this crate writes.
Functions§
- read_
record - The record at
collection/rkeyindid’s repository, asproofshows it, in its JSON form.Nonewhen the proof shows the repository holds nothing there. - tree_
key - The key a record sits at in the tree.