메인 콘텐츠로 건너뛰기
Core는 고급 내부 구현입니다. 일반적인 게시·피드 취득·알림 등에는 필요 없습니다. AT Protocol의 데이터 구조를 직접 조작하는 경우에 참조하세요.

AT Protocol의 데이터 모델 개요

AT Protocol의 저장소는 콘텐츠 주소형 데이터 구조를 채택하고 있습니다. 데이터의 저장·전송·검증에 다음 요소가 사용됩니다.
클래스역할
CBOR바이너리 직렬화
CID콘텐츠 주소(해시 식별자)
CAR저장소 아카이브 읽기·쓰기
TID시계열 레코드 키
Varint가변 길이 정수 인코딩(내부)

CBOR

CBOR 클래스는 AT Protocol이 채택한 DAG-CBOR 형식의 인코드·디코드를 제공합니다. 표준 CBOR의 확장으로 CID 링크(태그 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;

// 단일 아이템 디코드
$data = CBOR::decode($bytes);

// 스트림의 첫 아이템을 디코드하고 나머지를 반환
[$item, $remainder] = CBOR::decodeFirst($bytes);

// 스트림의 모든 아이템을 디코드
$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...오래된 사양(블롭)
v1base32bafy...새로운 사양(레코드)

주요 API

use Revolution\Bluesky\Core\CID;

// 데이터에서 CID를 생성
$cid = CID::encode($data, CID::DAG_CBOR); // 레코드용
$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 저장소를 블록의 열로 저장하는 바이너리 포맷입니다. com.atproto.sync.getRepo로 취득한 데이터가 이 형식입니다.

디코드

use Revolution\Bluesky\Core\CAR;

// CAR 파일 전체를 디코드(루트와 모든 블록)
['roots' => $roots, 'blocks' => $blocks] = CAR::decode($carData);

// 루트 CID만 취득
$roots = CAR::decodeRoots($carData);

// 블록을 순회
foreach (CAR::blockIterator($carData) as [$cid, $block]) {
    // $cid: CID 문자열, $block: 바이너리 데이터
}

// 레코드 맵으로 취득
foreach (CAR::blockMap($carData) as $key => $record) {
    // $key: 레코드 키, $record: 디코드된 배열
}

서명된 Commit의 검증

CAR 파일이 해당 사용자의 것인지 확인하려면, Signed Commit의 서명을 DID Document의 공개 키로 검증합니다.
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의 레코드 키에 사용되는 시계열 ID입니다. 마이크로초 단위 타임스탬프와 클록 ID를 조합해, base32로 인코드한 13자의 문자열입니다.
예: 3jujm55ngfc24

생성·변환

use Revolution\Bluesky\Core\TID;

// 다음 TID를 생성
$tid = TID::next();
echo $tid->toString(); // "3jujm55ngfc24" 같은 문자열

// 문자열로부터 TID를 생성
$tid = TID::fromStr('3jujm55ngfc24');

// 타임스탬프(마이크로초)와 클록 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 기능의 목킹에 대해

Core 기능(CBOR/CID/CAR/TID)은 외부 접근을 하지 않으므로, 테스트 시에 목킹할 필요가 없습니다.
// 테스트에서도 직접 사용 가능
$bytes = CBOR::encode(['text' => 'Hello']);
$cid   = CID::encode($bytes, CID::DAG_CBOR);
$bool  = CID::verify($bytes, $cid);

참고 링크

Source: src/Core/
마지막 수정일 2026년 7월 13일