ProtoRune

A type-safe Elixir SDK for the AT Protocol with a built-in bot framework.

Status: v0.2.0 MVP - Core features complete, production ready for basic use cases.

What is ProtoRune?

ProtoRune provides Elixir developers with tools to build applications on the AT Protocol, the decentralized social networking protocol that powers Bluesky.

Key features:

Installation

Add to your mix.exs:

def deps do
[
{:proto_rune, "~> 0.2.0"}
]
end

Then run:

mix deps.get

Quick Start

Posting to Bluesky

# Login with your handle and app password
{:ok, session} = ProtoRune.login(
"your-handle.bsky.social",
"your-app-password"
)
# Post something
{:ok, post} = ProtoRune.Bsky.post(session, "Hello from Elixir!")

Rich Text with Mentions

alias ProtoRune.RichText
{:ok, rt} =
RichText.new()
|> RichText.text("Hello ")
|> RichText.mention("alice.bsky.social")
|> RichText.text("! Check out ")
|> RichText.link("ProtoRune", "https://github.com/zoedsoupe/proto_rune")
|> RichText.text(" ")
|> RichText.hashtag("elixir")
|> RichText.build()
{:ok, post} = ProtoRune.Bsky.post(session, rt)

Building a Bot

defmodule GreeterBot do
use ProtoRune.Bot,
name: __MODULE__,
strategy: :polling
require Logger
@impl true
def get_identifier, do: System.get_env("BOT_IDENTIFIER")
@impl true
def get_password, do: System.get_env("BOT_PASSWORD")
@impl true
def handle_event(:mention, payload) do
Logger.info("Got mentioned by #{payload.thread.post.author.handle}")
{:ok, :handled}
end
@impl true
def handle_event(_event, _payload) do
{:ok, :ignored}
end
end
# Start the bot
{:ok, pid} = GreeterBot.start_link()

Core Concepts

Sessions

Sessions contain authentication tokens and account information. All operations require a session:

# Create session
{:ok, session} = ProtoRune.login(identifier, password)
# Session contains:
# - access_jwt: Access token
# - refresh_jwt: Refresh token
# - did: Your decentralized identifier
# - handle: Your handle
# - service_url: Your PDS URL
# Refresh when needed
{:ok, fresh_session} = ProtoRune.refresh_session(session)

Identity Resolution

Work with DIDs (decentralized identifiers) and handles:

# Resolve handle to DID
{:ok, did} = ProtoRune.resolve_handle("alice.bsky.social")
# => "did:plc:abc123xyz"
# Resolve DID to document
{:ok, doc} = ProtoRune.resolve_did("did:plc:abc123xyz")
# Validate handle-to-DID binding
{:ok, doc} = ProtoRune.validate_identity("alice.bsky.social")

Social Operations

High-level functions for common Bluesky operations:

# Social interactions
{:ok, like} = ProtoRune.Bsky.like(session, post.uri, post.cid)
{:ok, repost} = ProtoRune.Bsky.repost(session, post.uri, post.cid)
{:ok, follow} = ProtoRune.Bsky.follow(session, "alice.bsky.social")
# Reading content
{:ok, timeline} = ProtoRune.Bsky.get_timeline(session, limit: 20)
{:ok, profile} = ProtoRune.Bsky.get_profile(session, "bob.bsky.social")
{:ok, thread} = ProtoRune.Bsky.get_post_thread(session, post_uri)
# Notifications
{:ok, notifs} = ProtoRune.Bsky.list_notifications(session)
{:ok, %{count: unread}} = ProtoRune.Bsky.get_unread_count(session)
# Moderation
{:ok, block} = ProtoRune.Bsky.block(session, "spammer.bsky.social")
{:ok, _} = ProtoRune.Bsky.mute(session, "noisy.bsky.social")

Architecture

ProtoRune follows AT Protocol's layered architecture:

ProtoRune (Public API)
|
+-- ProtoRune.Bsky (Bluesky high-level helpers)
| |
| +-- Actor (profiles)
| +-- Feed (posts, timeline)
| +-- Graph (follows, blocks)
| +-- Notification (notifications)
|
+-- ProtoRune.Atproto (Protocol layer)
| |
| +-- Identity (DID/handle resolution)
| +-- Repo (repository operations)
| +-- Server (session management)
|
+-- ProtoRune.XRPC (Transport layer)
|
+-- ProtoRune.Bot (Bot framework)

Development Setup

Clone with submodules to get AT Protocol lexicons:

git clone --recurse-submodules https://github.com/zoedsoupe/proto_rune.git
cd proto_rune
# Install dependencies
mix deps.get
# Run tests
mix test

Design Principles

ProtoRune follows these principles:

  1. Explicit over implicit: Pass sessions explicitly, no hidden global state
  2. Type safety: Runtime validation with compile-time type specs
  3. OTP native: Leverage GenServers and Supervisors for reliability
  4. Progressive disclosure: Simple tasks are simple, complex tasks are possible
  5. ATProto alignment: Reflect the protocol's layered architecture

Roadmap

Completed (v0.2.0 MVP):

Planned:

Contributing

Contributions are welcome! Please:

  1. Fork the repository
  2. Create a feature branch
  3. Write tests for new functionality
  4. Ensure all tests pass (mix test)
  5. Format code (mix format)
  6. Submit a pull request

See CONTRIBUTING.md for detailed guidelines.

Inspirations

ProtoRune draws inspiration from:

License

MIT License - see LICENSE for details.

Support

Built love by @zoedsoupe.