跳转到主要内容
Core 是较为底层的内部实现。一般的发帖、feed 获取、通知等场景并不需要它。只有当你需要直接操作 AT Protocol 数据结构时才需要参考本页。

AT Protocol 的数据模型概览

AT Protocol 的仓库采用了内容寻址式数据结构。数据的保存、传输和校验依赖以下几个组件。
作用
CBOR二进制序列化
CID内容寻址(哈希标识符)
CAR仓库归档的读写
TID时序 record key
Varint变长整数编码(内部使用)

CBOR

CBOR 类提供了 AT Protocol 所采用的 DAG-CBOR 格式的编解码。作为标准 CBOR 的扩展,它支持 CID link(tag 42)。

编码

use Revolution\Bluesky\Core\CBOR;

$record = [
    '$type'     => 'app.bsky.feed.post',
    'text'      => 'Hello, Bluesky!',
    'createdAt' => '2025-01-01T00:00:00.000Z',
];

$bytes = CBOR::encode($record); // 二进制字符串

解码

use Revolution\Bluesky\Core\CBOR;

// 解码单个 item
$data = CBOR::decode($bytes);

// 解码流开头的 item,并返回剩余部分
[$item, $remainder] = CBOR::decodeFirst($bytes);

// 解码流中所有 item
$items = CBOR::decodeAll($bytes);

用途

如果想手动计算和校验帖子的 CID,需要使用 CBOR 编码(另见 verify 页面)。
use Revolution\Bluesky\Core\CBOR;
use Revolution\Bluesky\Core\CID;

$record = data_get($block, 'value');
$cbor   = CBOR::encode($record);
$bool   = CID::verify($cbor, data_get($block, 'cid'));

CID (Content Identifier)

CID 是基于数据哈希的自描述标识符。在 AT Protocol 中,SHA-256 哈希会被 multihash 包裹,再通过 multicodec 和 multibase 编码。

CIDv0 与 CIDv1

版本编码首字符用途
v0base58btcQm...旧规格(blob)
v1base32bafy...新规格(record)

主要 API

use Revolution\Bluesky\Core\CID;

// 从数据生成 CID
$cid = CID::encode($data, CID::DAG_CBOR); // 用于 record
$cid = CID::encode($data, CID::RAW);      // 二进制(图片等)

// 校验 CID
$bool = CID::verify($data, $cid, CID::DAG_CBOR);
$bool = CID::verify($file, $cid, CID::RAW);

// 解析 CID
$decoded = CID::decode($cid); // ['version', 'codec', 'hash']

// 查看 CID 版本
$version = CID::version($cid); // 0 or 1

// 与字节表示互转
$bytes = CID::decodeBytes($cid);
$cid   = CID::encodeBytes($bytes);

CAR (Content Addressable aRchive)

CAR 文件是把 AT Protocol 仓库以 block 序列形式存储的二进制格式。通过 com.atproto.sync.getRepo 获取的数据就是这种格式。

解码

use Revolution\Bluesky\Core\CAR;

// 解码整个 CAR 文件(root 与全部 block)
['roots' => $roots, 'blocks' => $blocks] = CAR::decode($carData);

// 仅获取 root CID
$roots = CAR::decodeRoots($carData);

// 遍历 block
foreach (CAR::blockIterator($carData) as [$cid, $block]) {
    // $cid: CID 字符串,$block: 二进制数据
}

// 作为 record map 获取
foreach (CAR::blockMap($carData) as $key => $record) {
    // $key: record key, $record: 已解码的数组
}

校验带签名的 Commit

要确认 CAR 文件确实属于某个用户,需要用 DID Document 中的公钥来校验 Signed Commit 的签名。
use Revolution\Bluesky\Core\CAR;
use Revolution\Bluesky\Crypto\DidKey;
use Revolution\Bluesky\Facades\Bluesky;
use Revolution\Bluesky\Support\DidDocument;

$did = 'did:plc:***';

$didDoc    = DidDocument::make(Bluesky::identity()->resolveDID($did)->json());
$publicKey = DidKey::parse($didDoc->publicKey());

$signed    = CAR::signedCommit($carData);
$bool      = CAR::verifySignedCommit($signed, $publicKey);
参考实现:DownloadRepoCommand

TID (Timestamp Identifier)

TID 是 AT Protocol 中作为 record key 使用的时序 ID。它由微秒级时间戳与 clock ID 组合而成,用 base32 编码,共 13 个字符。
例: 3jujm55ngfc24

生成与转换

use Revolution\Bluesky\Core\TID;

// 生成下一个 TID
$tid = TID::next();
echo $tid->toString(); // 类似 "3jujm55ngfc24" 的字符串

// 从字符串构建 TID
$tid = TID::fromStr('3jujm55ngfc24');

// 从时间戳(微秒)和 clock ID 构建
$tid = TID::fromTime(microtime(true) * 1000000, 0);

TID 的结构

通过 TID::s32encode()TID::s32decode() 可以在整数与字符串之间互相转换。

Varint(变长整数)

Varint 是 CAR / CBOR 二进制解析时使用的内部工具类。通常不需要直接使用。
use Revolution\Bluesky\Core\Varint;

// 对整数编码
$bytes = Varint::encode(1234);

// 从流中解码(返回值与消费的字节数)
[$value, $bytesRead] = Varint::decodeStream($stream);

关于 Core 的 mock

Core 的功能(CBOR/CID/CAR/TID)不会访问外部服务,因此测试时不需要 mock。
// 测试里也可以直接使用
$bytes = CBOR::encode(['text' => 'Hello']);
$cid   = CID::encode($bytes, CID::DAG_CBOR);
$bool  = CID::verify($bytes, $cid);

参考链接

Source: src/Core/
最后修改于 2026年7月13日