CARファイルのフォーマットについて
全体
At ProtocolではCAR v1が使用される。
IPLD : Specification: Content Addressable aRchives (CAR / .car) v1
|--------- Header --------| |---------------------------------- Data -----------------------------------|
[ varint | DAG-CBOR block ] [ varint | CID | block ] [ varint | CID | block ] [ varint | CID | block ] …
Dataの中の[ varint | CID | block ]
について
- varint
- LEB128形式で1Data単位のバイト数が保存されている(この値はCID以降のみでvarintとして使った分は含まない)
- 何バイト使って表現しているかは1Data単位のサイズ次第
- DAG-CBOR block(Headerのみ)
- ルートになるDataのCIDが含まれる(複数の場合もある)
- CID(ATPではv1)
- 0 : CIDのバージョン
- 1 : blockのcodec(0x71 : DAG-CBOR)
- 2 : CIDのHashの形式(0x12 : sha2-256)
- 3 : CIDのHashのサイズ(0x20 : 32byte)
- 4~ : 5バイト目からサイズ分だけがバイナリで保存される
- block
- CIDで指定されているcodecでエンコードされたバイナリデータ
CIDについて
仕様 : CID (Content IDentifier) Specification
CARファイルとしてのCIDとAt Protocol内で使われるCIDで異なる解釈をしないといけない。
CARファイルはContent Addressable aRchivesと言われるとおり内容(ATPではレコード)にアクセスするための参照情報も別途保存され、そこで使用される(別途と言ってもレコード情報に混ざって1Data単位として保存される)。
前述のCID領域の4バイト目以降をbase58でエンコード(バイナリ→ASCII)した文字列で扱われる場合と、CIDの領域全体をbase32でエンコード(バイナリ→ASCII)した文字列で扱われる場合がある。
後者がATPでの形式で、BlueskyのPDSのデータを見るだけならこちらのみでOKのはず。
前述のCIDの説明の補足
At Protocolとしてはv1が基本だが、0x12, 0x20で始まる場合、CIDv0となる。 その場合、0x20(32)バイト分のバイナリデータがCIDの領域となる(合計34バイト)(バイナリはsha2-256)。 一般的なCARファイルのデコードではどちらにも対応が必要となる。
DAG-CBORでのエンコードについて
IPLD : Specification: DAG-CBOR
基本はレコードのJSONに相当する文字列情報がそのまま入っているが(JSONそのままと言う意味ではない)、下記の例のようにBLOBへのリンクなど$link
での参照はCBORのタグ42番でmultibase形式のCIDのバイナリが保存される。
おそらく誰の持ち物かに関わらずレコードに含まれないデータ(主にblob)へのリンクの場合にこの形式になる。
At ProtocolのAPIで取得できる形式
"image": {
"$type": "blob",
"ref": {
"$link": "bafkreif4zzap2zrkvrdavcpvw7hdnne667p5qb6y4ycuoaq3iyuxbv3kmq"
},
"mimeType": "image/jpeg",
"size": 865706
}
CARファイルに保存されている形式(のデータ構造イメージ)
"image": {
"ref": 42(h'00 01 55 12 20 bc ce 40 fd 66 2a ac 46 0a 89 f5 b7 ce 36 b4 9e f7 df d8 07 d8 e6 05 47 02 1b 46 29 70 d7 6a 64'),
"size": 865706,
"$type": "blob",
"mimeType": "image/jpeg"
},
下記のようなケースはcidが直接入っている。
"reply": {
"root": {
"cid": "bafyreictjyd65u646lhwdlw72i3vuzlould2rwwtilaapbzeh33by55tky",
"uri": "at://did:plc:mqxsuw5b5rhpwo4lw6iwlid5/app.bsky.feed.post/3kqiqkjf7mk2e"
},
"parent": {
"cid": "bafyreiapo7fw3riy2edn2paj3zl2wf7ojurgoj7gcdzpfe2sgrwxbawanm",
"uri": "at://did:plc:mqxsuw5b5rhpwo4lw6iwlid5/app.bsky.feed.post/3kqiqkzwxrc2a"
}
},
仕様 : IPLD content identifiers (CIDs) in CBOR
先頭の0x00
は固定で付与されるため、2バイト目以降をbase32でエンコード(バイナリ→ASCII)してb
を先頭に追加するとAt Protocolで使われている形式になる。
先頭に付与するb
を含めたエンコードについての概要はMultibaseのMultibase Table
で下記の部分が該当する。
Unicode, character, encoding, description,
U+0062, b, base32, RFC4648 case-insensitive - no padding,
通常、元のデータが5バイト単位でないとパディングの=
が末尾に追加されるが、説明のとおり省いている。
参考 : base32のわかりやすい説明