Help us improve
Share bugs, ideas, or general feedback.
From atproto-skills
This skill should be used when the user is constructing, parsing, exporting, importing, or signature-validating an AT Protocol user repository in Rust, TypeScript, or Go — the durable per-account record store represented as a Merkle Search Tree inside a signed commit, serialized on the wire as a CAR v1 file. Triggers on phrases like "CAR file", "CAR v1", "car export", "com.atproto.sync.getRepo", "com.atproto.sync.getBlocks", "listMissingBlocks", "subscribeRepos", "repo export", "MST", "Merkle Search Tree", "MST node", "tree entry", "left subtree", "fanout", "prefix compression", "key height", "repo commit", "commit signature", "sig field", "signing bytes", "UnsignedCommit", "commit version 3", "rev TID", "prev commit", "DAG-CBOR", "DRISL", "canonical CBOR", "map key ordering", "tag 42", "block store", "block CID mismatch", "verify repo", "inductive verification", "prevData", "record CID in MST", "at://<did>/<collection>/<rkey>". Also triggers on dependency/import names like `atproto-repo`, `atproto-dasl`, `atproto-record`, `@atproto/repo`, `@atproto/lex-cbor`, `@atproto/lex-data`, `@atproto/crypto`, `indigo/atproto/repo`, `indigo/atproto/repo/mst`, `indigo/atproto/atdata`, `github.com/ipld/go-car`, `go-block-format`, `cbor-gen`, or API names like `LoadRepoFromCAR`, `VerifyCommitSignatureFromCar`, `VerifyCommitMessage`, `readCar`, `readCarWithRoot`, `verifyRepoCar`, `verifyDiffCar`, `signCommit`, `verifyCommitSig`, `Mst::insert_recursive`, `Tree.RootCID`, `MST.create`, `MST.load`, `DataDiff.of`. Use this skill to implement a repo reader, a repo writer, a commit signer/verifier, an MST diff tool, a CAR streaming parser, a firehose consumer, or to debug failures like "CID mismatch", "unknown codec in CAR", "MST node not found", "prefix_len exceeds previous key", "signature invalid", "prev null vs absent", "partial tree", "missing block", or "inverted tree root didn't match prevData". Covers DRISL (canonical DAG-CBOR) encoding rules, CAR v1 byte framing, MST structure (SHA-256 fanout 4, sorted entries, prefix compression, left subtree pointer), commit record fields and signing bytes, signature verification (including the `prev: null` vs omitted cross-implementation divergence), record placement (NSID + rkey), and cross-language interop. Does NOT cover CID parsing/construction (see `atproto-cid`), DID resolution / locating the signing key (see `atproto-identity-resolution`), lexicon-level record validation or XRPC method invocation or typed `$type` record dispatch (see `atproto-lexicon`), or OAuth (see `atproto-oauth`). Bluesky-domain record idioms (richtext facets, embeds) are out of scope for this plugin entirely.
npx claudepluginhub ngerakines/atproto-skills --plugin atproto-skillsHow this skill is triggered — by the user, by Claude, or both
Slash command
/atproto-skills:atproto-repositoryThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
An AT Protocol **repository** is a single account's entire record store, addressed by its DID. Every record lives at a key in a Merkle Search Tree; the tree's root CID is sealed by a signed commit; the commit plus every block it transitively references are the repository. On the wire, it travels as a **CAR v1** file.
go/README.mdgo/car.mdgo/commit.mdgo/drisl.mdgo/mst.mdrust/README.mdrust/car.mdrust/commit.mdrust/drisl.mdrust/mst.mdshared/car-v1.mdshared/commit-and-signing.mdshared/data-model.mdshared/divergence-matrix.mdshared/drisl.mdshared/mst.mdshared/test-vectors.mdtypescript/README.mdtypescript/car.mdtypescript/commit.mdThis skill should be used when the user is working with AT Protocol or DASL CIDs (Content Identifiers) in Rust, TypeScript, or Go — parsing, constructing, validating, verifying, or debugging them. Triggers on phrases like "parse a CID", "compute a CID for this record", "verify a blob CID", "why is this CID invalid", "CID mismatch", "tag 42 in DAG-CBOR", "identity multibase prefix", "the $link format", "base32lower", "digest length error", "what does the b prefix mean", "CIDv1 vs CIDv0", "bafyrei / bafkrei prefix", "dag-cbor codec", "multihash", "BLAKE3 CID", "BDASL". Also triggers on dependency/import names like `atproto-dasl`, `cid`, `multihash-codetable`, `libipld`, `multiformats`, `@ipld/dag-cbor`, `CID.parse`, `CID.create`, `github.com/ipfs/go-cid`, `go-multihash`, or lockfile names `Cargo.toml`, `package.json`, `pnpm-lock.yaml`, `go.mod`, `go.sum`. Covers the strict DASL CID profile (CIDv1 only, codec raw 0x55 or dag-cbor 0x71, hash SHA-256 0x12, 32-byte digest, base32lower string form with 'b' prefix, 36-byte binary form), the BDASL extension permitting BLAKE3 (0x1e) for large-file content, the DAG-CBOR wire form (CBOR tag 42 plus identity multibase 0x00 prefix), and the AT Protocol JSON `{"$link": "..."}` form. Use this skill to implement CID support in SDKs, clients, servers, firehose consumers, repo tooling, or diagnostic scripts in any of the three supported languages; for an unsupported language point at `shared/spec.md` and a reference implementation. Does NOT cover general IPFS / multiformats CID questions (DASL is a strict subset — reject anything outside the allowed constants); for MST tree traversal, CAR inspection, or DAG-CBOR canonicalization beyond the CID tag see `atproto-repository`.
Guides building on AT Protocol (atproto/Bluesky): authoring Lexicons, app views, firehose consumption, DIDs/handles, repositories, records, XRPC endpoints, OAuth.
Guides correct BosStr usage, type parameterization, and string allocation patterns in Jacquard Rust AT Protocol library to prevent common errors.
Share bugs, ideas, or general feedback.
An AT Protocol repository is a single account's entire record store, addressed by its DID. Every record lives at a key in a Merkle Search Tree; the tree's root CID is sealed by a signed commit; the commit plus every block it transitively references are the repository. On the wire, it travels as a CAR v1 file.
This skill routes to per-language guides for Rust, TypeScript, and Go, sitting on top of a language-neutral spec in shared/.
varint ‖ header-cbor ‖ (varint ‖ 36-byte-CID ‖ bytes)*. Roots list declares the commit CID.{l?, e:[{p,k,v,t?}]}. Keys are <collection>/<rkey>, max 1024 bytes.{did, version:3, data, rev, prev, sig}. prev is null for genesis (though see §divergence). Signing bytes = DAG-CBOR of the commit minus sig.r ‖ s ECDSA (k-256 or p-256), low-S normalized. Verified against the #atproto Multikey in the signer's DID document.Full normative rules: shared/drisl.md, shared/car-v1.md, shared/mst.md, shared/commit-and-signing.md, shared/data-model.md. Fixtures: shared/test-vectors.md. Cross-language differences: shared/divergence-matrix.md.
Before generating or reviewing any repo code, determine the target language from project files or the file being edited:
Cargo.toml, *.rs, any mention of atproto-repo / atproto-dasl / atproto-record → Rust — read from rust/.package.json, tsconfig.json, *.ts, *.tsx, imports of @atproto/repo / @atproto/lex-cbor / @atproto/crypto → TypeScript — read from typescript/. Also *.js/*.jsx when there is no .ts present.go.mod, *.go, imports of github.com/bluesky-social/indigo/atproto/repo → Go — read from go/.Prefer the file being edited over the repo root when they disagree: a .go consumer inside a TypeScript-workspace monorepo still means Go for that task.
If multiple languages are present and the task does not point at one unambiguously, ask which one applies. Never mix repo libraries across languages in generated code.
If an unsupported language is detected (Python, Java, Swift, …), point the user at shared/drisl.md, shared/car-v1.md, shared/mst.md, and shared/commit-and-signing.md for the wire format, and offer the Go indigo/atproto/repo source as the most complete reference implementation to transliterate from.
For every repo task:
shared/*.md first. They are short and define the rules your code must enforce.{lang}/drisl.md{lang}/car.md{lang}/mst.md{lang}/commit.md{lang}/README.mdshared/divergence-matrix.md whenever porting between languages or reviewing cross-stack interop. The prev: null vs omitted divergence alone has caused many hours of "why does my signature not verify" debugging.Always prefer the official library over hand-rolling: atproto-repo crate in Rust, @atproto/repo in TypeScript, github.com/bluesky-social/indigo/atproto/repo in Go.
┌──────────────────────────────┐
CAR v1 file ──────▶ │ header: { version:1, roots } │
(on the wire) │ block: [cid, data] … │
│ block: [cid, data] … │
└──────────────────────────────┘
│
▼
┌──────────────────────────────┐
Signed commit ─────▶ │ { did, version:3, data, rev, │
(root block) │ prev, sig } │
└──────────────────────────────┘
│ data: CID
▼
┌──────────────────────────────┐
MST node ──────────▶ │ { l?, e:[entry,entry,…] } │
(many blocks) │ entry = { p, k, v, t? } │
└──────────────────────────────┘
│ v: CID (leaf = record block)
▼
┌──────────────────────────────┐
Record block ──────▶ │ DAG-CBOR encoding of record │
(many blocks) │ { $type: "<nsid>", … } │
└──────────────────────────────┘
CAR just frames a sequence of (CID, bytes) blocks. The same block bytes travel over getRecord, the firehose, and any cache.
High-frequency failure modes; full detail in shared/divergence-matrix.md:
prev: null vs omitted — When signatures fail on genesis commits only, suspect cross-encoder prev drift. Rust reference impl omits prev for genesis (4-entry map); Go and TS always serialize prev: null (5-entry map). Strip the signature from raw bytes rather than re-encoding before verifying.add/update/delete return a new MST; the original is unchanged. Rust and Go mutate in place.verifyIncomingCarBlocks defaults on in TS; Go and Rust do not verify.Commit struct happens to be canonical, but anything you add must be checked.MissingBlockError in TS, treat the partial tree as expected and fall back to a partial walker. Go/Rust return a partial-tree sentinel instead.VerifyCommitSignatureFromCar is Go-only; TS needs a pre-resolved didKey and Rust needs the caller to do everything.Mst::insert_recursive can't cross heights — When building an MST from scratch in Rust, build bottom-up from sorted (key, cid) pairs, not top-down.Prefer these MCP tools when the goal is to compute or validate rather than teach an implementation how:
lexicon-garden → create_record_cid(record), transmogrify_record, invoke_xrpc (e.g. com.atproto.sync.getRepo, getBlocks).atpmcp → create_record_cid, get_record, transmogrify_record, generate_tid against a local PDS.Concrete example — computing a spec-conformant record CID against an in-memory record before writing a test vector:
create_record_cid({
"$type": "app.bsky.feed.post",
"text": "hello",
"createdAt": "2024-01-01T00:00:00.000Z"
})
→ "bafyreihsh..."
Use this to verify your own encoder output matches the canonical CID without having to boot a PDS. transmogrify_record is the round-trip companion — feed it a record, get back the DAG-CBOR bytes and the CID together.
For record-side helpers (TID generation, AT-URI parsing), the atproto-record Rust crate ships the cleanest reference; the TS equivalent is @atproto/syntax, and Go uses atproto/syntax in indigo.
VerifyCommitSignatureFromCar (Go) does identity lookup and signature check in one call. TypeScript and Rust need the signing key resolved separately before verification:
atproto-identity first, extract the #atproto verification method, then pass the Multikey into atproto-repo's verify loop. See ../atproto-identity-resolution/rust/resolution.md for the resolver and rust/commit.md for the verify surface.@atproto/identity → IdResolver.did.resolveAtprotoData(did) → yields { signingKey } as a didKey, then pass into verifyCommitSig. See ../atproto-identity-resolution/typescript/resolution.md and typescript/commit.md.VerifyCommitSignatureFromCar wraps both; no manual plumbing required.atproto-repository/
├── SKILL.md # this file — router
├── shared/
│ ├── drisl.md # canonical DAG-CBOR rules
│ ├── car-v1.md # CAR v1 byte layout
│ ├── mst.md # MST algorithm + invariants
│ ├── commit-and-signing.md # commit shape, signing bytes, verification
│ ├── data-model.md # records, NSIDs, TIDs, AT-URIs
│ ├── test-vectors.md # fixtures
│ └── divergence-matrix.md # cross-language differences
├── rust/
│ ├── README.md # atproto-repo / atproto-dasl / atproto-record setup
│ ├── drisl.md # atproto-dasl encoding
│ ├── car.md # CarReader / CarWriter
│ ├── mst.md # Mst<S>, insert_recursive, diff_entries
│ └── commit.md # Commit / UnsignedCommit / signing loop
├── typescript/
│ ├── README.md # @atproto/repo setup
│ ├── drisl.md # @atproto/lex-cbor
│ ├── car.md # readCar / writeCarStream / verifyIncomingCarBlocks
│ ├── mst.md # immutable MST class
│ └── commit.md # signCommit / verifyCommitSig / verifyRepo
└── go/
├── README.md # indigo/atproto/repo setup
├── drisl.md # cbor-gen + atdata
├── car.md # LoadRepoFromCAR / go-car
├── mst.md # Tree / Node / partial-tree semantics
└── commit.md # Commit / Sign / VerifyCommitSignatureFromCar
All reachable from the tree above. Listed here for quick grep:
shared/drisl.md, shared/car-v1.md, shared/mst.md, shared/commit-and-signing.md, shared/data-model.md, shared/test-vectors.md, shared/divergence-matrix.mdrust/README.md, rust/drisl.md, rust/car.md, rust/mst.md, rust/commit.mdtypescript/README.md, typescript/drisl.md, typescript/car.md, typescript/mst.md, typescript/commit.mdgo/README.md, go/drisl.md, go/car.md, go/mst.md, go/commit.md