···11-To recap: [records](https://atproto.com/guides/glossary#record) are kept in [repo](https://atproto.com/guides/glossary#data-repo)s on [PDS](https://atproto.com/guides/glossary#pds-personal-data-server)es.
11+# Test-drive a CAR
2233-**Today we're looking at the repo!** Or at least its serialized format.
33+When apps sync data from your PDS, it comes in a format called a [**C**ontent **A**ddressable a**R**chive](https://ipld.io/specs/transport/car/carv1/#format-description): a CAR file! 🚗
4455-The repo (repository) is a key-value store of repo-keys to records. The data structure used is called a Merkle Search Tree (MST), but we're not going to worry about that just yet.
55+This archive can contain your entire "repo" (repository), which is your **[records](https://atproto.com/guides/glossary#record)**, their **keys** (the record's `collection` and `rkey` combined). <small>It also has some additional structural info [for the MST](https://atproto.com/guides/data-repos#data-layout), but we're not going to worry about that just yet!</small>
6677-Wait, "repo keys"?? Remember [yesterday](./2) how an at-uri is made of three parts, `at://<did>/<collection>/<rkey>`? The second two parts together, `<collection>/<rkey>`, make up the actual raw keys (repo keys) inside your atproto repository.
77+## YOU WOULDN'T DOWNLOAD A CAR
8899+…who are we kidding,
9101111+<a id="download-car" href="" class="btn btn-primary btn-disabled">Loading (JS required) …</a>
10121111-TODO blah blah merkle tree but not getting too distracted because this is about the CAR
1313+Your challenge: **find the `verification_code` hidden inside**.
12141313-where do CARs come in?
1515+<details>
1616+ <summary>Hint: atproto CAR tools (easy-mode)</summary>
14171515-- nice neat serialization format
1616-- what you get when you export your repo
1717-- what apps get when they sync your repo
1818-- what PDSs send as proof for sync.getRecord
1919-- what gets sent over the firehose
1818+ There are some nice web tools for poking around in a CAR:
20192121-- header with root cid
2222-- blocks (cid, block)
2020+ <ul>
2121+ <li>
2222+ <a href="https://satnav.rsky.dev/" target="_blank">
2323+ Satnav
2424+ </a> from Blacksy
2525+ </li>
2626+ <li>
2727+ <a href="https://boat.kelinci.net/repo-archive-explore" target="_blank">
2828+ Archive explore
2929+ </a> on boat.kelinci.net
3030+ </li>
3131+ <li>
3232+ <a href="https://pdsls.dev/car/explore" target="_blank">
3333+ Explore archive
3434+ </a> at PDSls
3535+ </li>
3636+ </ul>
23372424-- block content is dag-cbor (already covered a bit from day 2)
2525-- there are also MST blocks (also dag-cbor, you can ignore them if you want)
3838+ Or, if you have the [goat](https://github.com/bluesky-social/goat?tab=readme-ov-file#goat-go-at-protocol-cli-tool) command-line utility installed, try running:
26392727-OH YEAH terminology note: dag-cbor is out, i think we're all drisl now? drisl-cbor?
28402929-- CIDs (ignorable)
4141+ ```bash
4242+ goat repo --help
4343+ ```
30443131-### Your CAR is ready
4545+</details>
32463333-Click the button below for your very own CAR export of an atproto repository. Search the CAR for your verification code and submit it below.
4747+<details>
4848+ <summary>CAR libraries (write some code)</summary>
34493535-<a id="download-car" href="" class="btn btn-primary btn-disabled">Loading (JS required) …</a>
5050+ If you're feeling adventurous, you can write a program to show the contents of a CAR file. Some libraries that can help:
5151+5252+ <ul>
5353+ <li>
5454+ <strong>python:</strong>
5555+ <a href="https://atproto.blue/en/latest/atproto/atproto_core.car.car.html" target="blank"><code>atproto_core.car</code></a>
5656+ (has sample code)
5757+ </li>
5858+ <li>
5959+ <strong>javascript:</strong>
6060+ <a href="https://github.com/mary-ext/atcute/tree/trunk/packages/utilities/repo" target="blank"><code>@atcute/repo</code></a>
6161+ (advanced)
6262+ </li>
6363+ <li>
6464+ <strong>golang:</strong>
6565+ <a href="https://pkg.go.dev/github.com/bluesky-social/indigo@v0.0.0-20260318212431-cbaa83aee9dd/atproto/repo" target="blank"><code>indigo/atproto/repo</code></a>
6666+ (advanced)
6767+ </li>
6868+ <li>
6969+ <strong>rust:</strong>
7070+ <a href="https://docs.rs/jacquard-repo/latest/jacquard_repo/index.html" target="blank"><code>jacquard_repo</code></a>
7171+ (advanced)
7272+ </li>
7373+ </ul>
7474+</details>
7575+7676+<details>
7777+ <summary>Binary spelunking (bits & bytes)</summary>
7878+7979+ Oh, you want hard-mode? Here:
8080+8181+ - [CAR format spec](https://ipld.io/specs/transport/car/carv1/#format-description)
8282+ - [MST atproto spec](https://atproto.com/specs/repository#mst-structure)
8383+8484+ <details>
8585+ <summary>Binary <span style="text-decoration: line-through">hint</span> cheat</summary>
8686+8787+ Text inside records is not compressed or weirdly encoded, and CAR just length-prefixes blocks without changing any bytes, so `strings` or even `hexdump -C` on your CAR might be worth trying…
8888+ </details>
8989+9090+</details>
9191+36923793<script>
9494+console.log("?????");
3895(function() {
3996 const button = document.getElementById('download-car');
4040- const carData = atob("{{car_base64}}");
9797+ console.log("{{{car_base64}}}");
9898+ const carData = atob("{{{car_base64}}}");
419942100 if (carData.length === 0) {
43101 button.textContent = 'Log in to download';
···45103 }
4610447105 button.classList.remove('btn-disabled');
4848- button.textContent = 'Download your CAR file';
106106+ button.textContent = 'Download a CAR';
4910750108 const bytes = new Uint8Array(carData.length);
51109 for (let i = 0; i < carData.length; i++) bytes[i] = carData.charCodeAt(i);