A library for ATProtocol identities.
README.md

atproto-record#

Utilities for working with AT Protocol records.

Overview#

A Rust library for working with AT Protocol records, providing AT-URI parsing, TID generation, datetime formatting, and CID generation. Built on IPLD DAG-CBOR serialization for deterministic content addressing.

Features#

  • AT-URI parsing: Parse and validate AT Protocol URIs (at://authority/collection/record_key) with robust error handling
  • TID generation: Timestamp-based identifiers for AT Protocol records with microsecond precision
  • CID generation: Content Identifier generation using DAG-CBOR serialization and SHA-256 hashing
  • DateTime utilities: RFC 3339 datetime serialization with millisecond precision for consistent timestamp handling
  • Typed records: Type-safe record handling with lexicon type validation
  • Bytes handling: Base64 encoding/decoding for binary data in AT Protocol records
  • Structured errors: Type-safe error handling following project conventions with detailed error messages

CLI Tools#

The following command-line tool is available when built with the clap feature:

  • atproto-record-cid: Generate CID (Content Identifier) for AT Protocol records from JSON input

Library Usage#

Generating CIDs#

use serde_json::json;
use cid::Cid;
use sha2::{Digest, Sha256};
use multihash::Multihash;

// Serialize a record to DAG-CBOR and generate its CID
let record = json!({
    "$type": "app.bsky.feed.post",
    "text": "Hello world!",
    "createdAt": "2024-01-01T00:00:00.000Z"
});

let dag_cbor_bytes = serde_ipld_dagcbor::to_vec(&record)?;
let hash = Sha256::digest(&dag_cbor_bytes);
let multihash = Multihash::wrap(0x12, &hash)?;
let cid = Cid::new_v1(0x71, multihash);

println!("Record CID: {}", cid);

Generating TIDs#

use atproto_record::tid::Tid;

// Generate a new timestamp-based identifier
let tid = Tid::new();
println!("TID: {}", tid); // e.g., "3l2k4j5h6g7f8d9s"

// TIDs are sortable by creation time
let tid1 = Tid::new();
std::thread::sleep(std::time::Duration::from_millis(1));
let tid2 = Tid::new();
assert!(tid1 < tid2);

AT-URI Parsing#

use atproto_record::aturi::ATURI;
use std::str::FromStr;

// Parse an AT-URI into its components
let aturi = ATURI::from_str("at://did:plc:abc123/app.bsky.feed.post/3k2k4j5h6g")?;

// Access the parsed components
println!("Authority: {}", aturi.authority);   // "did:plc:abc123"
println!("Collection: {}", aturi.collection);  // "app.bsky.feed.post"
println!("Record Key: {}", aturi.record_key);  // "3k2k4j5h6g"

// The Display trait formats back to a valid AT-URI
println!("Full URI: {}", aturi);  // "at://did:plc:abc123/app.bsky.feed.post/3k2k4j5h6g"

DateTime Utilities#

use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};

// Use the datetime module for consistent RFC 3339 formatting
#[derive(Serialize, Deserialize)]
struct Record {
    #[serde(with = "atproto_record::datetime::format")]
    created_at: DateTime<Utc>,
    
    #[serde(with = "atproto_record::datetime::optional_format")]
    updated_at: Option<DateTime<Utc>>,
}

Command Line Usage#

The CLI tool requires the clap feature:

# Build with CLI support
cargo build --features clap --bins

# Generate CID from JSON file
cat record.json | cargo run --features clap --bin atproto-record-cid

# Generate CID from inline JSON
echo '{"$type":"app.bsky.feed.post","text":"Hello!"}' | cargo run --features clap --bin atproto-record-cid

# Example with a complete AT Protocol record
cat <<EOF | cargo run --features clap --bin atproto-record-cid
{
  "$type": "app.bsky.feed.post",
  "text": "Hello AT Protocol!",
  "createdAt": "2024-01-01T00:00:00.000Z"
}
EOF

The tool outputs the CID in base32 format:

bafyreibjzlvhtyxnhbvvzl3gj4qmg2ufl2jbhh5qr3gvvxlm7ksf3qwxqq

License#

MIT License