Skip to main content

Crate jacquard

Crate jacquard 

Source
Expand description

§Jacquard

A suite of Rust crates intended to make it much easier to get started with atproto development, without sacrificing flexibility or performance.

Jacquard is simpler because it is designed in a way which makes things simple that almost every other atproto library seems to make difficult.

§Features

  • Validated, spec-compliant, easy to work with, and performant baseline types.
  • Designed such that you can just work with generated API bindings easily.
  • Straightforward OAuth.
  • Server-side convenience features.
  • Lexicon Data value type for working with unknown atproto data (dag-cbor or json).
  • An order of magnitude less boilerplate than some existing crates.
  • Batteries-included, but easily replaceable batteries.
    • Easy to extend with custom lexicons using code generation or handwritten api types.
    • Stateless options (or options where you handle the state) for rolling your own.
    • All the building blocks of the convenient abstractions are available.
    • Use as much or as little from the crates as you need.

§Example

Dead simple API client: resume a stored OAuth session or open a browser login, then fetch the latest 5 posts. OAuth loopback is the default path for local scripts and CLIs where browser login is acceptable; app-password credential sessions are mainly for unattended workflows that must re-authenticate non-interactively.

use jacquard::api::app_bsky::feed::get_timeline::GetTimeline;
use jacquard::client::{Agent, FileAuthStore};
use jacquard::common::session::SessionHint;
use jacquard::oauth::client::OAuthClient;
use jacquard::xrpc::XrpcClient;
use jacquard::oauth::types::AuthorizeOptions;
use jacquard::oauth::loopback::LoopbackConfig;

const STORE_PATH: &str = "/tmp/jacquard-oauth-session.json";

#[tokio::main]
async fn main() -> miette::Result<()> {
    let login_hint = std::env::args().nth(1);
    let oauth = OAuthClient::with_default_config(FileAuthStore::new(STORE_PATH));
    let hint = SessionHint::from_optional_input(login_hint.as_deref());

    let Some(session) = oauth
       .resume_or_login_with_local_server(
           &hint,
           AuthorizeOptions::default(),
           LoopbackConfig::default(),
       )
       .await?
    else {
        miette::bail!(
            "no stored OAuth session found in {STORE_PATH}; pass a handle, DID, or PDS URL to log in"
        );
    };

    let agent: Agent<_> = Agent::from(session);
    let timeline = agent
        .send(GetTimeline::new().limit(5).build())
        .await?
        .into_output()?;

    for (i, post) in timeline.feed.iter().enumerate() {
        println!("\n{}. by {}", i + 1, post.post.author.handle);
        println!(
            "   {}",
            serde_json::to_string_pretty(&post.post.record).into_diagnostic()?
        );
    }
    Ok(())
}

§Component crates

Jacquard is split into several crates for modularity. The main jacquard crate re-exports most of the others, so you typically only need to depend on it directly.

  • jacquard-common - AT Protocol types (DIDs, handles, at-URIs, NSIDs, TIDs, CIDs, etc.)
  • jacquard-api - Generated API bindings from 646+ lexicon schemas
  • jacquard-axum - Server-side XRPC handler extractors for Axum framework (not re-exported, depends on jacquard)
  • jacquard-oauth - OAuth/DPoP flow implementation with session management
  • jacquard-identity - Identity resolution (handle → DID, DID → Doc, OAuth metadata)
  • jacquard-repo - Repository primitives (MST, commits, CAR I/O, block storage)
  • jacquard-lexicon - Lexicon resolution, fetching, parsing and Rust code generation from schemas
  • jacquard-lexgen - Code generation binaries
  • jacquard-derive - Macros (#[lexicon], #[open_union], #[derive(IntoStatic)], #[derive(LexiconSchema)], #[derive(XrpcRequest)])

§String backing types

Most generated Jacquard types are parameterized over a string backing type: Type<S = DefaultStr>. The default backing is owned and efficient. It is especially convenient when values need to be stored, moved independently of a response buffer, or passed through frameworks and APIs with DeserializeOwned bounds. In most examples you will not write the S parameter at all because builders, constructors, and .into_output() infer or choose the owned default for you.

When you are writing generic helpers or optimizing parsing, you can choose another backing such as String, &str, or CowStr<'_> with the BosStr trait. For API responses, use .into_output() as the normal path for owned/default-backed output. Use .parse::<CowStr<'_>>() or .parse::<&str>() when you specifically want to borrow from the response buffer.

§Client options

  • Stateless XRPC: any HttpClient (e.g., reqwest::Client) implements XrpcExt, which provides xrpc(base: Uri<String>) -> XrpcCall for per-request calls with optional CallOptions (auth, proxy, labelers, headers). Useful when you want to pass auth on each call or build advanced flows.
 #[tokio::main]
 async fn main() -> miette::Result<()> {
     let http = reqwest::Client::new();
     let base = Uri::parse("https://public.api.bsky.app").into_diagnostic()?;
     let resp = http
         .xrpc(base)
         .send(
             &GetAuthorFeed::new()
                 .actor(AtIdentifier::new_static("pattern.atproto.systems").unwrap())
                 .limit(5)
                 .build(),
         )
         .await?;
     let out = resp.into_output()?;
     println!("{}", serde_json::to_string_pretty(&out).into_diagnostic()?);
     Ok(())
 }
  • Stateful client (app-password): CredentialSession<S, T> where S: SessionStore<(Did, CowStr), AtpSession> and T: IdentityResolver + HttpClient. It auto-attaches bearer authorization, refreshes on expiry, and updates the base endpoint to the user’s PDS on login/restore.
  • Stateful client (OAuth): OAuthClient<S, T> and OAuthSession<S, T> where S: ClientAuthStore and T: OAuthResolver + HttpClient. The client is used to authenticate, returning a session which handles authentication and token refresh internally.
  • Agent<A: AgentSession> Session abstracts over the above two options and provides some useful convenience features via the AgentSessionExt trait.

Per-request overrides (stateless)

#[tokio::main]
async fn main() -> miette::Result<()> {
    let http = reqwest::Client::new();
    let base = Uri::parse("https://public.api.bsky.app").into_diagnostic()?;
    let resp = http
        .xrpc(base)
        .auth(AuthorizationToken::Bearer("ACCESS_JWT".into()))
        .accept_labelers(vec!["did:plc:labelerid".into()])
        .header(http::header::USER_AGENT, http::HeaderValue::from_static("jacquard-example"))
        .send(
            &GetAuthorFeed::new()
                .actor(AtIdentifier::new_static("pattern.atproto.systems").unwrap())
                .limit(5)
                .build(),
        )
        .await?;
    let out = resp.into_output()?;
    println!("{}", serde_json::to_string_pretty(&out).into_diagnostic()?);
    Ok(())
}

Re-exports§

pub use jacquard_api as api;
pub use jacquard_common as common;
pub use jacquard_identity as identity;
pub use jacquard_oauth as oauth;

Modules§

bos
Borrow-or-share traits for abstracting over owned and borrowed string representations. Borrow-or-share traits for abstracting over owned and borrowed string representations.
client
XRPC client implementation for AT Protocol
cowstr
A copy-on-write immutable string type that uses smol_str::SmolStr for the “owned” variant.
deps
Re-exports of external crate dependencies for consistent access across jacquard. Re-exports of external crate dependencies for consistent access across jacquard.
error
Error types for XRPC client operations
http_client
Minimal HTTP client abstraction shared across crates.
into_static
Trait for taking ownership of most borrowed types in jacquard.
jetstream
Jetstream subscription support
macros
atproto! macro.
moderation
Moderation
opt_serde_bytes_helper
Custom serde helpers for bytes::Bytes using serde_bytes
prelude
Prelude with the extension traits you’re likely to want and some other stuff
richtext
Rich text utilities for Bluesky posts
serde_bytes_helper
Custom serde helpers for bytes::Bytes using serde_bytes
service_auth
Service authentication JWT parsing and verification for AT Protocol.
session
Generic session storage traits and utilities.
stream
Stream abstractions for HTTP request/response bodies
streaming
Streaming endpoints
types
Baseline fundamental AT Protocol data types.
websocket
WebSocket client abstraction
xrpc
Stateless XRPC utilities and request/response mapping

Macros§

atproto
Construct a default-backed atproto Data value from a literal.
format_smolstr
Formats arguments to a SmolStr, potentially without allocating.
impl_bos
Implement Bos for types that always borrow from *self.

Structs§

Array
Array of AT Protocol data values
ByteSink
Platform-agnostic byte sink abstraction
ByteStream
Platform-agnostic byte stream abstraction
CloseFrame
WebSocket close frame
Object
Object/map of AT Protocol data values
QueryMatch
A single match from a query operation
SmolStr
A SmolStr is a string type that has the following properties:
StreamError
Error type for streaming operations
StreamingResponse
HTTP streaming response
TungsteniteClient
WebSocket client backed by tokio-tungstenite-wasm
WebSocketConnection
WebSocket connection with bidirectional streams
WsSink
WebSocket message sink
WsStream
WebSocket message stream
WsText
UTF-8 validated bytes for WebSocket text messages

Enums§

AtDataError
Errors that can occur when working with AT Protocol data
AuthorizationToken
Authorization token types for XRPC requests.
CloseCode
WebSocket close code
CowStr
A copy-on-write immutable string type that uses SmolStr for the “owned” variant.
Data
AT Protocol data model value
DataDeserializerError
Error type for Data/RawData deserializer
QueryResult
Result of a data query operation
RawData
Level 1 deserialization of raw atproto data
RawDataSerializerError
Error type for RawData serialization
StreamErrorKind
Categories of streaming errors
WsMessage
WebSocket message

Traits§

BorrowOrShare
A helper trait for writing “data borrowing or sharing” functions.
Bos
A trait for either borrowing or sharing data.
BosStr
Combined trait bound for AT Protocol string backing types.
FromStaticStr
Construct a value from a static string literal without allocation where possible.
IntoStatic
Allow turning a value into an “owned” variant, which can then be returned, moved, etc.
ToSmolStr
Convert value to SmolStr using fmt::Display, potentially without allocating.
WebSocketClient
WebSocket client trait

Functions§

deserialize_owned
Serde helper for deserializing stuff when you want an owned version
from_cbor
Deserialize a typed value from cbor bytes
from_data
Deserialize a typed value from a Data value
from_data_owned
Deserialize a typed value from a Data value
from_json_value
Deserialize a typed value from a serde_json::Value
from_postcard
Deserialize a typed value from postcard bytes
from_raw_data
Deserialize a typed value from a RawData value
from_raw_data_owned
Deserialize a typed value from a RawData value
to_data
Serialize a typed value into a validated Data value with type inference
to_raw_data
Serialize a typed value into a RawData value

Type Aliases§

DefaultStr
The default string backing type for jacquard’s type-parameterised types.
Lazy
Lazy initialization type for static values.

Attribute Macros§

lexicon
Attribute macro that adds an extra_data field to structs to capture unknown fields during deserialization.
lexicon_union
Attribute macro for union enums.
open_union
Attribute macro that adds an Unknown(Data) variant to enums to make them open unions.

Derive Macros§

IntoStatic
Derive macro for IntoStatic trait.
LexiconSchema
Derive macro for LexiconSchema trait.
XrpcRequest
Derive macro for XrpcRequest trait.