CARファイルのフォーマットについて

@ioriayane.relog.tech

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を含めたエンコードについての概要はMultibaseMultibase Tableで下記の部分が該当する。

Unicode,    character,  encoding,           description,       
U+0062,     b,          base32,             RFC4648 case-insensitive - no padding,

通常、元のデータが5バイト単位でないとパディングの=が末尾に追加されるが、説明のとおり省いている。

参考 : base32のわかりやすい説明

ioriayane.relog.tech
IoriAYANE

@ioriayane.relog.tech

Blueskyクライアントの羽衣を作ってます
https://hagoromo.relog.tech/ja/
epubを作るソフトのLeMEを作ってます。
https://leme.style
X:@ioriayane
アイコンは X:@keikawagutiさん

Post reaction in Bluesky

*To be shown as a reaction, include article link in the post or add link card

Reactions from everyone (0)