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

IoriAYANE
Table of Contents

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

全体

At ProtocolではCAR v1が使用される。

IPLD : Specification: Content Addressable aRchives (CAR / .car) v1

auto
|--------- 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で取得できる形式

JSON
    "image": {
        "$type": "blob",
        "ref": {
            "$link": "bafkreif4zzap2zrkvrdavcpvw7hdnne667p5qb6y4ycuoaq3iyuxbv3kmq"
        },
        "mimeType": "image/jpeg",
        "size": 865706
    }

CARファイルに保存されている形式(のデータ構造イメージ)

JSON
    "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が直接入っている。

JSON
    "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で下記の部分が該当する。

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

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

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

IoriAYANE
IoriAYANE @ioriayane.relog.tech

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

No comments yet