| … | … | @@ -0,0 +1,59 @@ |
| 1 | # atproto-repo |
| 2 | |
| 3 | Byte-exact **AT Protocol repository serialization** — CAR + DAG-CBOR + CID + MST + |
| 4 | signed commits — implemented in pure JS over Hypercore-friendly primitives. This is the |
| 5 | *wire layer*: it turns a set of records into the exact bytes a real Bluesky relay accepts, |
| 6 | and reads them back. Verified against the official atproto interop test vectors (CID |
| 7 | heights, MST structure, commit proofs, low-S signatures). |
| 8 | |
| 9 | Part of the **atproto-base** self-custody stack: |
| 10 | |
| 11 | ``` |
| 12 | atproto-repo ← you are here (serialization / codec) |
| 13 | atproto-store P2P-native repo storage (Hyperbee) |
| 14 | atproto-identity seed-anchored did:plc identity + auth |
| 15 | hyper-proto-peer the self-custody peer (multi-device swarm PDS) |
| 16 | atproto-bridge keyless membrane → Bluesky |
| 17 | ``` |
| 18 | |
| 19 | ## What it does |
| 20 | |
| 21 | - **CID** (`lib/cid.js`) — CIDv1 dag-cbor/sha-256, byte-exact. `cidForBytesSync`, |
| 22 | `cidForValue`, `cidForBlobSync`. |
| 23 | - **MST** (`lib/mst.js`) — the Merkle Search Tree that determines every repo root CID. |
| 24 | `buildMst`, `keyHeight` (leading-zero-nibble fanout), `MSTNode`. Matches the interop |
| 25 | `key_heights` / `common_prefix` fixtures exactly. |
| 26 | - **Commit** (`lib/commit.js`) — `unsignedCommit`, `signCommit` (ECDSA-secp256k1 **low-S** |
| 27 | over sha-256 of the dag-cbor unsigned commit), `commitCid`, `verifyCommit`. |
| 28 | - **CAR** (`lib/car.js`) — `readCar` / `writeCar` (the IPLD Content-Addressed aRchive that |
| 29 | `com.atproto.sync.getRepo` returns). |
| 30 | - **Repo** (`lib/repo.js`, main) — assemble/verify a full repo: `createRepo`, `loadCommit`, |
| 31 | `verifyRepo`, `listRecords`, plus the firehose proof helpers (`firehoseProofBlocks`, |
| 32 | `findRpathProof`, `reachableBlocks`) a `#commit` event must carry. |
| 33 | |
| 34 | ## Usage |
| 35 | |
| 36 | ```js |
| 37 | import { signCommit, unsignedCommit, commitCid } from 'atproto-repo/lib/commit.js' |
| 38 | import { cidForValue } from 'atproto-repo/lib/cid.js' |
| 39 | import { writeCar } from 'atproto-repo/lib/car.js' |
| 40 | |
| 41 | const unsigned = unsignedCommit({ did, rev, data: mstRootCid, prev }) |
| 42 | const signed = signCommit(unsigned, privateKey) // secp256k1 low-S |
| 43 | const cid = commitCid(signed) |
| 44 | ``` |
| 45 | |
| 46 | ## Design notes |
| 47 | |
| 48 | - **Pure JS crypto** (`@noble/curves`, `@noble/hashes`) — no Node `crypto` global, so it |
| 49 | runs unchanged under **Bare**. Signatures are canonical low-S, raw (r‖s), never DER. |
| 50 | - **Serializer, not storage.** The P2P home for records is |
| 51 | [`atproto-store`](../atproto-store) (a Hyperbee); this module serializes *over* it. |
| 52 | - Conformance is the whole point — if our output disagrees with an interop fixture, our |
| 53 | code is wrong. See the `atproto-interop-tests` fixtures. |
| 54 | |
| 55 | ## Test |
| 56 | |
| 57 | ``` |
| 58 | bare test/all.js # or: npm test |
| 59 | ``` |