Core Types
Branded types and AT Protocol identifiers used throughout the server.
Branded Types
Branded types provide type safety for AT Protocol identifiers, preventing accidental misuse of strings.
DID (Decentralized Identifier)
type DID = string & { readonly __brand: 'DID' };
Description: Permanent, unique identifier for users and repositories.
Format: did:plc:
followed by base32-encoded identifier
Examples:
const userDid: DID = "did:plc:abc123xyz789" as DID;
const repoDid: DID = "did:plc:def456uvw012" as DID;
Characteristics:
- Permanent (never changes)
- Globally unique
- Cryptographically verifiable
- Use for internal references
ATURI (AT Protocol URI)
type ATURI = string & { readonly __brand: 'ATURI' };
Description: URI for AT Protocol resources (posts, likes, follows, etc.).
Format: at://[DID]/[collection]/[rkey]
Examples:
const postUri: ATURI = "at://did:plc:abc123/app.bsky.feed.post/xyz789" as ATURI;
const likeUri: ATURI = "at://did:plc:abc123/app.bsky.feed.like/like123" as ATURI;
const followUri: ATURI = "at://did:plc:abc123/app.bsky.graph.follow/follow456" as ATURI;
Components:
- DID: Repository identifier
- Collection: Record type (e.g.,
app.bsky.feed.post
) - Record Key (rkey): Unique record identifier
NSID (Namespaced Identifier)
type NSID = string & { readonly __brand: 'NSID' };
Description: Namespaced identifier for Lexicon schemas and collections.
Format: Reverse domain notation with segments
Examples:
const postType: NSID = "app.bsky.feed.post" as NSID;
const likeType: NSID = "app.bsky.feed.like" as NSID;
const profileType: NSID = "app.bsky.actor.profile" as NSID;
Common NSIDs:
app.bsky.feed.post
- Postsapp.bsky.feed.like
- Likesapp.bsky.feed.repost
- Repostsapp.bsky.graph.follow
- Followsapp.bsky.graph.block
- Blocksapp.bsky.actor.profile
- Profiles
CID (Content Identifier)
type CID = string & { readonly __brand: 'CID' };
Description: Content-addressed identifier using IPFS CID format.
Format: Base32-encoded multihash
Examples:
const postCid: CID = "bafyreiabc123xyz789..." as CID;
const imageCid: CID = "bafkreidef456uvw012..." as CID;
Characteristics:
- Content-addressed (hash of content)
- Immutable
- Verifiable
- Used for data integrity
AT Protocol Types
IAtpSession
interface IAtpSession {
did: DID;
handle: string;
accessJwt: string;
refreshJwt: string;
active: boolean;
}
Description: Authenticated session information.
Fields:
did
- User's DIDhandle
- User's handleaccessJwt
- Access token (2 hour lifetime)refreshJwt
- Refresh token (90 day lifetime)active
- Whether session is active
IAtpProfile
interface IAtpProfile {
did: DID;
handle: string;
displayName?: string;
description?: string;
avatar?: string;
banner?: string;
followersCount?: number;
followsCount?: number;
postsCount?: number;
}
Description: User profile information.
Fields:
did
- User's DID (required)handle
- User's handle (required)displayName
- Display namedescription
- Bio textavatar
- Avatar image URLbanner
- Banner image URLfollowersCount
- Number of followersfollowsCount
- Number of followspostsCount
- Number of posts
IAtpPost
interface IAtpPost {
uri: ATURI;
cid: CID;
author: IAtpProfile;
record: {
text: string;
createdAt: string;
reply?: {
root: { uri: ATURI; cid: CID };
parent: { uri: ATURI; cid: CID };
};
embed?: unknown;
langs?: string[];
labels?: unknown;
tags?: string[];
};
replyCount?: number;
repostCount?: number;
likeCount?: number;
indexedAt: string;
viewer?: {
repost?: ATURI;
like?: ATURI;
};
}
Description: Post data structure.
Fields:
uri
- Post URIcid
- Post CIDauthor
- Post author profilerecord
- Post record datatext
- Post text contentcreatedAt
- Creation timestampreply
- Reply information (if reply)embed
- Embedded contentlangs
- Language codestags
- Hashtags
replyCount
- Number of repliesrepostCount
- Number of repostslikeCount
- Number of likesindexedAt
- Index timestampviewer
- Viewer-specific data (when authenticated)
Type Guards
Validating Branded Types
function isDID(value: string): value is DID {
return value.startsWith('did:');
}
function isATURI(value: string): value is ATURI {
return value.startsWith('at://');
}
function isCID(value: string): value is CID {
return value.startsWith('bafy') || value.startsWith('bafk');
}
Usage Examples
Working with DIDs
// Store user DID
const userDid: DID = profile.did;
// Use in API calls
const followers = await getFollowers({ actor: userDid });
Working with AT URIs
// Store post URI
const postUri: ATURI = post.uri;
// Use for operations
await likePost({ uri: postUri, cid: post.cid });
await replyToPost({
text: "Great post!",
root: postUri,
parent: postUri
});
Working with CIDs
// Verify content integrity
const expectedCid: CID = post.cid;
const actualCid: CID = calculateCID(post.record);
if (expectedCid === actualCid) {
console.log('Content verified');
}
Best Practices
Type Safety
- Use branded types for all AT Protocol identifiers
- Don't cast strings to branded types without validation
- Implement type guards for runtime validation
- Use TypeScript strict mode
Identifier Storage
- Store DIDs for permanent references
- Store AT URIs for resource references
- Store CIDs for content verification
- Don't rely on handles (they can change)
Validation
- Validate format before casting to branded types
- Check for null/undefined values
- Handle invalid identifiers gracefully
- Log validation errors