A comprehensive Motoko library for building AT Protocol (ATProto) applications on the Internet Computer. This library provides core data structures, repository management, cryptographic operations, and utilities for implementing Personal Data Servers (PDS) and other AT Protocol services.
mops add atprotoTo set up MOPS package manager, follow the instructions from the MOPS Site
The AT Protocol (Authenticated Transfer Protocol) is the foundation for decentralized social networks like Bluesky. This library provides Motoko implementations of:
- Repository Management: Full IPLD-based repository with commit history and Merkle Search Trees
- Identity & DIDs: AT URI parsing and DID document handling
- Cryptographic Primitives: CID generation, DAG-CBOR encoding, and commit signing
- Data Structures: Blob references, strong references, and lexicon validators
- Merkle Search Trees: Efficient key-value storage with cryptographic proofs
import AtUri "mo:atproto/AtUri";
import DID "mo:did";
// Create an AT URI
let uri : AtUri.AtUri = {
authority = #plc(myPlcDid);
collection = ?{
id = "app.bsky.feed.post";
recordKey = ?"3jzfcijpj2z2a";
};
};
// Convert to text representation
let uriText = AtUri.toText(uri);
// Result: "at://did:plc:xxx/app.bsky.feed.post/3jzfcijpj2z2a"
// Parse from text
switch (AtUri.fromText("at://alice.bsky.social/app.bsky.feed.post/123")) {
case (?parsedUri) {
Debug.print("Successfully parsed AT URI");
};
case (null) {
Debug.print("Invalid AT URI format");
};
};import Repository "mo:atproto/Repository";
import DID "mo:did";
import TID "mo:tid";
import Result "mo:core/Result";
// Initialize an empty repository
let did = myPlcDid; // Your PLC DID
let rev = TID.now(); // Current timestamp
let signFunc = func(data : Blob) : async* Result.Result<Blob, Text> {
// Your signing implementation
};
let repo = await* Repository.empty(did, rev, signFunc);
// Create a new record
let createOp : Repository.WriteOperation = #create({
key = {
collection = "app.bsky.feed.post";
recordKey = "3jzfcijpj2z2a";
};
value = myPostRecord; // DAG-CBOR encoded value
});
// Apply the operation
switch (await* Repository.applyWrites(repo, [createOp], signFunc)) {
case (#ok({ repository = newRepo; results })) {
Debug.print("Record created successfully");
};
case (#err(error)) {
Debug.print("Error: " # error);
};
};import MST "mo:atproto/MerkleSearchTree";
import CID "mo:cid";
// Create an empty Merkle Search Tree
let mst = MST.empty();
// Add entries
let recordCid = myCidValue;
switch (MST.add(mst, "app.bsky.feed.post/123", recordCid)) {
case (#ok(newMst)) {
Debug.print("Entry added to MST");
// Retrieve the entry
switch (MST.get(newMst, "app.bsky.feed.post/123")) {
case (?cid) {
Debug.print("Found CID: " # CID.toText(cid));
};
case (null) {
Debug.print("Entry not found");
};
};
};
case (#err(error)) {
Debug.print("Error: " # error);
};
};import Commit "mo:atproto/Commit";
import CIDBuilder "mo:atproto/CIDBuilder";
import TID "mo:tid";
// Create an unsigned commit
let unsignedCommit : Commit.UnsignedCommit = {
did = myPlcDid;
version = 3;
data = mstRootCid; // CID of MST root
rev = TID.now();
prev = ?previousCommitCid;
};
// Generate CID for the unsigned commit
let commitCid = CIDBuilder.fromUnsignedCommit(unsignedCommit);
// Sign the commit
let signature = await signData(commitBlob);
let signedCommit : Commit.Commit = {
did = unsignedCommit.did;
version = unsignedCommit.version;
data = unsignedCommit.data;
rev = unsignedCommit.rev;
prev = unsignedCommit.prev;
sig = signature;
};import BlobRef "mo:atproto/Lexicons/BlobRef";
import CID "mo:cid";
import Json "mo:json";
// Create a blob reference
let blobRef : BlobRef.BlobRef = {
ref = blobCid;
mimeType = "image/jpeg";
size = 245678;
};
// Convert to JSON for AT Protocol messages
let blobJson = BlobRef.toJson(blobRef);
// Result: {"$type": "blob", "ref": {"$link": "..."}, "mimeType": "image/jpeg", "size": 245678}The Repository module provides a complete implementation of an AT Protocol repository with:
- Write Operations: Create, update, and delete records
- Commit History: Full history of all repository changes
- Record Storage: Efficient storage and retrieval of DAG-CBOR encoded records
- Export/Import: CAR file generation for repository synchronization
- Validation: Ensures repository integrity and proper commit chains
A specialized data structure for efficient, cryptographically verifiable key-value storage:
- Ordered Storage: Keys are stored in lexicographic order
- Cryptographic Proofs: Each node has a CID for verification
- Efficient Updates: Minimal tree modifications on changes
- Range Queries: Support for prefix-based searches
- Historical Nodes: Optional retention of previous tree states
Parse and construct AT Protocol URIs with support for:
- Handle Authorities:
at://alice.bsky.social/... - DID Authorities:
at://did:plc:xyz.../... - Collection Paths: References to specific collections
- Record Keys: Direct links to individual records
Work with Decentralized Identifier documents:
- Verification Methods: Public key management
- Service Endpoints: PDS and other service discovery
- JSON Serialization: Standard DID document format
- Key References: Support for key rotation
• Personal Data Servers: Build PDS implementations for the AT Protocol • Social Media Clients: Create Bluesky and AT Protocol clients • Repository Management: Implement repository operations for any AT Protocol service • Identity Services: Handle DID resolution and verification • Data Synchronization: Export and import repositories using CAR files • Federated Services: Build relays, app views, and other AT Protocol infrastructure • DAO Social Presence: Enable organizations to manage collective social media identities
// AT URI structure
public type AtUri = {
authority : { #handle : Text; #plc : DID.Plc.DID };
collection : ?{
id : Text;
recordKey : ?Text;
};
};
// Repository metadata
public type MetaData = {
head : CID.CID;
rev : TID.TID;
active : Bool;
status : ?Text;
};
// Write operations
public type WriteOperation = {
#create : { key : Key; value : DagCbor.Value };
#update : { key : Key; value : DagCbor.Value };
#delete : { key : Key };
};
// Commit structure
public type Commit = {
did : DID.Plc.DID;
version : Nat;
data : CID.CID;
rev : TID.TID;
prev : ?CID.CID;
sig : Blob;
};// Create an empty repository
public func empty(
did : DID.Plc.DID,
rev : TID.TID,
signFunc : (Blob) -> async* Result.Result<Blob, Text>
) : async* Result.Result<Repository, Text>;
// Apply write operations
public func applyWrites(
repo : Repository,
writes : [WriteOperation],
signFunc : (Blob) -> async* Result.Result<Blob, Text>
) : async* Result.Result<{
repository : Repository;
results : [WriteResult];
}, Text>;
// Get a record by key
public func getRecord(
repo : Repository,
key : Key
) : ?RecordData;
// List all records in a collection
public func listRecords(
repo : Repository,
collection : Text
) : [(Text, RecordData)];
// Export repository as CAR file
public func exportCar(
repo : Repository,
since : ?TID.TID
) : Result.Result<Blob, Text>;// Create an empty MST
public func empty() : MerkleSearchTree;
// Add an entry (fails if key exists)
public func add(
mst : MerkleSearchTree,
key : Text,
value : CID.CID
) : Result.Result<MerkleSearchTree, Text>;
// Put an entry (creates or updates)
public func put(
mst : MerkleSearchTree,
key : Text,
value : CID.CID
) : Result.Result<MerkleSearchTree, Text>;
// Remove an entry
public func remove(
mst : MerkleSearchTree,
key : Text
) : Result.Result<(MerkleSearchTree, CID.CID), Text>;
// Get an entry
public func get(
mst : MerkleSearchTree,
key : Text
) : ?CID.CID;
// Validate MST structure
public func validate(
mst : MerkleSearchTree
) : Result.Result<(), Text>;// Convert AT URI to text
public func toText(uri : AtUri) : Text;
// Parse AT URI from text
public func fromText(path : Text) : ?AtUri;// Create CID from blob
public func fromBlob(blob : Blob) : CID.CID;
// Create CID from commit
public func fromCommit(commit : Commit) : CID.CID;
// Create CID from MST node
public func fromMSTNode(node : MerkleNode.Node) : CID.CID;
// Create CID from DAG-CBOR value
public func fromDagCbor(cbor : DagCbor.Value) : CID.CID;src/
├── AtUri.mo # AT Protocol URI parsing and construction
├── Repository.mo # Repository management and operations
├── MerkleSearchTree.mo # Merkle Search Tree implementation
├── MerkleNode.mo # MST node structure
├── Commit.mo # Commit structures
├── CIDBuilder.mo # CID generation utilities
├── DagCborBuilder.mo # DAG-CBOR encoding helpers
├── JsonDagCborMapper.mo # JSON to DAG-CBOR conversion
├── DIDDocument.mo # DID document handling
└── Lexicons/
├── BlobRef.mo # Blob reference types
├── StrongRef.mo # Strong reference types
├── LexiconValidator.mo # Schema validation
└── App/Bsky/... # Bluesky lexicon types
└── Com/Atproto/... # AT Protocol lexicon types
mops testThis library is used by:
- motoko_atproto_pds: A complete PDS implementation for the Internet Computer, enabling DAOs and organizations to manage AT Protocol identities and post to Bluesky
Contributions are welcome! Please feel free to submit issues and pull requests.
This library builds upon several excellent Motoko packages:
- cid: Content Identifier implementation
- dag-cbor: DAG-CBOR encoding/decoding
- did: Decentralized Identifier utilities
- tid: Timestamp Identifier generation
- sha2: SHA-256 hashing
- json: JSON encoding/decoding
- core: Core Motoko utilities
This project is licensed under the MIT License - see the LICENSE file for details.