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 schemasjacquard-axum- Server-side XRPC handler extractors for Axum framework (not re-exported, depends on jacquard)jacquard-oauth- OAuth/DPoP flow implementation with session managementjacquard-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 schemasjacquard-lexgen- Code generation binariesjacquard-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) implementsXrpcExt, which providesxrpc(base: Uri<String>) -> XrpcCallfor per-request calls with optionalCallOptions(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>whereS: SessionStore<(Did, CowStr), AtpSession>andT: 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>andOAuthSession<S, T>whereS: ClientAuthStoreandT: 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 theAgentSessionExttrait.
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::SmolStrfor 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
Datavalue from a literal. - format_
smolstr - Formats arguments to a
SmolStr, potentially without allocating. - impl_
bos - Implement
Bosfor types that always borrow from*self.
Structs§
- Array
- Array of AT Protocol data values
- Byte
Sink - Platform-agnostic byte sink abstraction
- Byte
Stream - Platform-agnostic byte stream abstraction
- Close
Frame - WebSocket close frame
- Object
- Object/map of AT Protocol data values
- Query
Match - A single match from a query operation
- SmolStr
- A
SmolStris a string type that has the following properties: - Stream
Error - Error type for streaming operations
- Streaming
Response - HTTP streaming response
- Tungstenite
Client - WebSocket client backed by tokio-tungstenite-wasm
- WebSocket
Connection - WebSocket connection with bidirectional streams
- WsSink
- WebSocket message sink
- WsStream
- WebSocket message stream
- WsText
- UTF-8 validated bytes for WebSocket text messages
Enums§
- AtData
Error - Errors that can occur when working with AT Protocol data
- Authorization
Token - Authorization token types for XRPC requests.
- Close
Code - WebSocket close code
- CowStr
- A copy-on-write immutable string type that uses
SmolStrfor the “owned” variant. - Data
- AT Protocol data model value
- Data
Deserializer Error - Error type for Data/RawData deserializer
- Query
Result - Result of a data query operation
- RawData
- Level 1 deserialization of raw atproto data
- RawData
Serializer Error - Error type for RawData serialization
- Stream
Error Kind - Categories of streaming errors
- WsMessage
- WebSocket message
Traits§
- Borrow
OrShare - 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.
- From
Static Str - Construct a value from a static string literal without allocation where possible.
- Into
Static - Allow turning a value into an “owned” variant, which can then be returned, moved, etc.
- ToSmol
Str - Convert value to
SmolStrusingfmt::Display, potentially without allocating. - WebSocket
Client - 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
Datavalue - from_
data_ owned - Deserialize a typed value from a
Datavalue - 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
RawDatavalue - from_
raw_ data_ owned - Deserialize a typed value from a
RawDatavalue - to_data
- Serialize a typed value into a validated
Datavalue with type inference - to_
raw_ data - Serialize a typed value into a
RawDatavalue
Type Aliases§
- Default
Str - 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_datafield 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§
- Into
Static - Derive macro for
IntoStatictrait. - Lexicon
Schema - Derive macro for
LexiconSchematrait. - Xrpc
Request - Derive macro for
XrpcRequesttrait.