atproto libraries implementation in ocaml

AT Protocol Tools Guide#

This guide demonstrates how to use the example tools together to explore the AT Protocol ecosystem. We'll walk through a complete workflow: finding interesting posts in the firehose, investigating user identities, and inspecting their repositories.

Prerequisites#

Build all examples:

dune build examples/

Set up your credentials for bsky_bot (required for authenticated operations):

export BSKY_USER="yourhandle.bsky.social"
export BSKY_PASS="xxxx-xxxx-xxxx-xxxx"  # App password from Settings > App Passwords

Overview of Tools#

Tool Purpose
feed_generator Subscribe to the firehose and filter events
identity_tool Resolve handles/DIDs and verify identities
repo_inspector Download and explore user repositories
bsky_bot Interact with Bluesky (post, timeline, profiles)

Step 1: Find Interesting Posts with feed_generator#

Start by subscribing to the firehose and filtering for posts containing a keyword of interest. Let's look for posts about "ocaml":

`dune exec examples/feed_generator/feed_generator.exe -- \
  --filter posts \
  --keyword "ocaml" \
  --limit 5 \
  --content`

Example output:

Feed Generator
==============

Type filter: posts
Keyword: "ocaml"

Connecting to wss://bsky.network/xrpc/com.atproto.sync.subscribeRepos...
Connected!

#commit seq=12345678 repo=did:plc:abc123xyz456
        +app.bsky.feed.post/3abc123 "Just discovered OCaml! The type system is amazing..."
#commit seq=12345680 repo=did:plc:def789uvw012
        +app.bsky.feed.post/3def456 "Working on my first OCaml project today..."

--- Stats: 1523 events (5 matched) | seq=12345700 | 8.2s | 185.7 evt/s ---

Key information extracted:

  • did:plc:abc123xyz456 - The DID of a user who posted about OCaml
  • app.bsky.feed.post/3abc123 - The record key of their post

Generating a Feed Skeleton#

If you want to build a custom feed, use the --skeleton flag to output a feed skeleton:

dune exec examples/feed_generator/feed_generator.exe -- \
  --filter posts \
  --keyword "rust" \
  --limit 20 \
  --skeleton

This outputs a JSON structure you can serve via app.bsky.feed.getFeedSkeleton:

{
  "feed": [
    {"post": "at://did:plc:xxx/app.bsky.feed.post/abc123"},
    {"post": "at://did:plc:yyy/app.bsky.feed.post/def456"}
  ]
}

Monitoring Multiple Event Types#

Watch for posts, likes, and follows simultaneously:

dune exec examples/feed_generator/feed_generator.exe -- \
  --filter posts,likes,follows \
  --limit 50

Step 2: Verify User Identity with identity_tool#

Now that we have a DID from the firehose, let's verify the identity and get more details about the user.

Verify Identity (Bidirectional)#

The default mode verifies that the DID and handle point to each other:

dune exec examples/identity_tool/identity_tool.exe -- did:plc:abc123xyz456

Example output:

PASSED
DID:    did:plc:abc123xyz456
Handle: alice.bsky.social
Key:    zQ3shXjHeiBuRCKmM...
PDS:    https://morel.us-east.host.bsky.network

This tells us:

  • The identity verification passed (handle and DID are correctly linked)
  • The user's handle is alice.bsky.social
  • Their signing key (for verifying commits)
  • Their PDS (Personal Data Server) URL

Resolve Handle to DID#

If you have a handle and need the DID:

dune exec examples/identity_tool/identity_tool.exe -- -H alice.bsky.social

Output:

Handle: alice.bsky.social
DID:    did:plc:abc123xyz456

Resolve DID to Full Document#

Get the complete DID document with all services and keys:

dune exec examples/identity_tool/identity_tool.exe -- -d did:plc:abc123xyz456

Output:

DID:     did:plc:abc123xyz456
Handle:  at://alice.bsky.social
Key:     zQ3shXjHeiBuRCKmM... (Multikey)
Service: AtprotoPersonalDataServer -> https://morel.us-east.host.bsky.network

Step 3: Inspect User Repository with repo_inspector#

With the verified DID, we can download and inspect the user's entire repository. The tool automatically resolves the user's PDS from their DID document.

Repository Summary#

Get an overview of the repository:

dune exec examples/repo_inspector/repo_inspector.exe -- did:plc:abc123xyz456

Example output:

Repository Inspector
====================

Resolving PDS for did:plc:abc123xyz456...
PDS: https://morel.us-east.host.bsky.network

Fetching https://morel.us-east.host.bsky.network/xrpc/com.atproto.sync.getRepo?did=did:plc:abc123xyz456...

CAR Header
----------
Version: 1
Roots:   1
  - bafyreib...
Blocks:  1234

Commit
------
DID:     did:plc:abc123xyz456
Version: 3
Rev:     3la7j2xyz...
Data:    bafyreic...

Collections
-----------
  app.bsky.feed.post: 456
  app.bsky.feed.like: 789
  app.bsky.graph.follow: 123
  app.bsky.actor.profile: 1

Total records: 1369

List Collections#

See all collections with record counts:

dune exec examples/repo_inspector/repo_inspector.exe -- \
  did:plc:abc123xyz456 --collections

Browse Records#

View individual records:

dune exec examples/repo_inspector/repo_inspector.exe -- \
  did:plc:abc123xyz456 --records --limit 10

Output:

Records
-------
app.bsky.feed.post/3abc123
  CID: bafyreig...
  Text: Just discovered OCaml! The type system is amazing...

app.bsky.feed.post/3def456
  CID: bafyreih...
  Text: Working on my second OCaml project...

... and 454 more

Filter by Collection#

View only posts:

dune exec examples/repo_inspector/repo_inspector.exe -- \
  did:plc:abc123xyz456 --records --collection app.bsky.feed.post --limit 20

View only follows:

dune exec examples/repo_inspector/repo_inspector.exe -- \
  did:plc:abc123xyz456 --records --collection app.bsky.graph.follow --limit 20

Verify Commit Signature#

Check the repository commit signature:

dune exec examples/repo_inspector/repo_inspector.exe -- \
  did:plc:abc123xyz456 --verify

Skip PDS Resolution#

If you already know the PDS URL (from identity_tool or a previous run), you can skip the resolution step:

dune exec examples/repo_inspector/repo_inspector.exe -- \
  did:plc:abc123xyz456 --pds https://morel.us-east.host.bsky.network

Step 4: Get User Profile with bsky_bot#

For a more user-friendly view, use bsky_bot to get the profile (requires authentication):

dune exec examples/bsky_bot/bsky_bot.exe -- \
  --user "$BSKY_USER" \
  --password "$BSKY_PASS" \
  --profile alice.bsky.social

Output:

Logged in as yourhandle.bsky.social
Handle:    @alice.bsky.social
DID:       did:plc:abc123xyz456
Name:      Alice
Bio:       OCaml enthusiast and functional programming advocate
Followers: 1234
Following: 567
Posts:     456

View Your Timeline#

See recent posts from people you follow:

dune exec examples/bsky_bot/bsky_bot.exe -- \
  --user "$BSKY_USER" \
  --password "$BSKY_PASS" \
  --timeline --limit 10

Follow a User#

Follow someone using their DID (obtained from identity_tool or feed_generator):

dune exec examples/bsky_bot/bsky_bot.exe -- \
  --user "$BSKY_USER" \
  --password "$BSKY_PASS" \
  --follow did:plc:abc123xyz456

Complete Workflow Example#

Here's a complete example workflow to find OCaml enthusiasts and explore their content:

#!/bin/bash

# 1. Find users posting about OCaml
echo "=== Finding OCaml posts ==="
dune exec examples/feed_generator/feed_generator.exe -- \
  --filter posts --keyword "ocaml" --limit 3 --json 2>/dev/null | \
  head -3

# Let's say we found: did:plc:z72i7hdynmk6r22z27h6tvur

DID="did:plc:z72i7hdynmk6r22z27h6tvur"

# 2. Verify the identity
echo -e "\n=== Verifying identity ==="
dune exec examples/identity_tool/identity_tool.exe -- "$DID"

# 3. Get their handle from the verification and look up their profile
HANDLE="jay.bsky.social"  # Extracted from step 2

echo -e "\n=== Getting profile ==="
dune exec examples/bsky_bot/bsky_bot.exe -- \
  --user "$BSKY_USER" --password "$BSKY_PASS" \
  --profile "$HANDLE"

# 4. Inspect their repository
echo -e "\n=== Repository overview ==="
dune exec examples/repo_inspector/repo_inspector.exe -- "$DID"

# 5. See their recent posts
echo -e "\n=== Recent posts ==="
dune exec examples/repo_inspector/repo_inspector.exe -- \
  "$DID" --records --collection app.bsky.feed.post --limit 5

Advanced Usage#

JSON Output for Scripting#

The feed_generator supports JSON output for integration with other tools:

dune exec examples/feed_generator/feed_generator.exe -- \
  --filter posts --keyword "atproto" --limit 5 --json --content

You can pipe this to jq for further processing:

dune exec examples/feed_generator/feed_generator.exe -- \
  --filter posts --limit 10 --json 2>/dev/null | \
  jq -r '.repo' | sort | uniq

Resume from Cursor#

If you need to resume from a specific point in the firehose:

dune exec examples/feed_generator/feed_generator.exe -- \
  --cursor 1234567890 --limit 100

Monitoring Identity Changes#

Watch for handle changes and identity updates:

dune exec examples/feed_generator/feed_generator.exe -- \
  --filter identities,handles --limit 20

Watching Account Events#

Monitor account activations and deactivations:

dune exec examples/feed_generator/feed_generator.exe -- \
  --filter accounts --limit 10

Architecture Overview#

                           +-------------------+
                           |   Bluesky Relay   |
                           |  bsky.network     |
                           +-------------------+
                                    |
                                    | WebSocket (firehose)
                                    v
                           +-------------------+
                           | feed_generator    |
                           | (filter events)   |
                           +-------------------+
                                    |
                                    | DID extracted
                                    v
+-------------------+      +-------------------+      +-------------------+
|  identity_tool    |<---->|   PLC Directory   |      |   User's PDS      |
| (resolve/verify)  |      |   DID Documents   |      |                   |
+-------------------+      +-------------------+      +-------------------+
         |                                                     ^
         | DID verified                                        |
         v                                                     |
+-------------------+                                          |
|  repo_inspector   |------------------------------------------+
| (download & parse)|         com.atproto.sync.getRepo
+-------------------+

+-------------------+
|    bsky_bot       |-------> Authenticated XRPC calls
| (social actions)  |         (post, timeline, profile, follow)
+-------------------+

Troubleshooting#

Connection Errors#

If the firehose connection fails:

  • Check your internet connection
  • The relay at bsky.network may be temporarily unavailable
  • Try again after a few seconds

Identity Resolution Failures#

If identity verification fails:

  • The handle may have changed recently (DNS propagation delay)
  • The DID document may be temporarily unavailable
  • Try resolving the handle and DID separately to diagnose

Large Repositories#

For users with many records:

  • Use --limit to restrict the number of records shown
  • Use --collection to filter to specific record types
  • Repository download may take several seconds for active users

Next Steps#

  • Build a custom feed generator using feed_generator as a template
  • Create a bot that responds to mentions using bsky_bot
  • Build analytics tools using repo_inspector to analyze user behavior
  • Implement identity verification in your applications using identity_tool

See the individual README files in each example directory for more details: