1[](https://crates.io/crates/jacquard) [](https://docs.rs/jacquard)
2
3# Jacquard
4
5A suite of Rust crates intended to make it much easier to get started with atproto development, without sacrificing flexibility or performance.
6
7[Jacquard is simpler](https://whtwnd.com/nonbinary.computer/3m33efvsylz2s) because it is designed in a way which makes things simple that almost every other atproto library seems to make difficult.
8
9It is also designed around zero-copy/borrowed deserialization: types like [`Post<'_>`](https://tangled.org/@nonbinary.computer/jacquard/blob/main/crates/jacquard-api/src/app_bsky/feed/post.rs) can borrow data (via the [`CowStr<'_>`](https://docs.rs/jacquard/latest/jacquard/cowstr/enum.CowStr.html) type and a host of other types built on top of it) directly from the response buffer instead of allocating owned copies. Owned versions are themselves mostly inlined or reference-counted pointers and are therefore still quite efficient. The `IntoStatic` trait (which is derivable) makes it easy to get an owned version and avoid worrying about lifetimes.
10
11## Goals and Features
12
13- Validated, spec-compliant, easy to work with, and performant baseline types
14- Batteries-included, but easily replaceable batteries.
15 - Easy to extend with custom lexicons using code generation or handwritten api types
16 - Straightforward OAuth
17 - Stateless options (or options where you handle the state) for rolling your own
18 - All the building blocks of the convenient abstractions are available
19 - Server-side convenience features
20- Lexicon Data value type for working with unknown atproto data (dag-cbor or json)
21- An order of magnitude less boilerplate than some existing crates
22- Use as much or as little from the crates as you need
23
24
25## Example
26
27Dead simple API client. Logs in with OAuth and prints the latest 5 posts from your timeline.
28
29```rust
30// Note: this requires the `loopback` feature enabled (it is currently by default)
31use clap::Parser;
32use jacquard::CowStr;
33use jacquard::api::app_bsky::feed::get_timeline::GetTimeline;
34use jacquard::client::{Agent, FileAuthStore};
35use jacquard::oauth::client::OAuthClient;
36use jacquard::oauth::loopback::LoopbackConfig;
37use jacquard::types::xrpc::XrpcClient;
38use miette::IntoDiagnostic;
39
40#[derive(Parser, Debug)]
41#[command(author, version, about = "Jacquard - OAuth (DPoP) loopback demo")]
42struct Args {
43 /// Handle (e.g., alice.bsky.social), DID, or PDS URL
44 input: CowStr<'static>,
45
46 /// Path to auth store file (will be created if missing)
47 #[arg(long, default_value = "/tmp/jacquard-oauth-session.json")]
48 store: String,
49}
50
51#[tokio::main]
52async fn main() -> miette::Result<()> {
53 let args = Args::parse();
54
55 // Build an OAuth client with file-backed auth store and default localhost config
56 let oauth = OAuthClient::with_default_config(FileAuthStore::new(&args.store));
57 // Authenticate with a PDS, using a loopback server to handle the callback flow
58 let session = oauth
59 .login_with_local_server(
60 args.input.clone(),
61 Default::default(),
62 LoopbackConfig::default(),
63 )
64 .await?;
65 // Wrap in Agent and fetch the timeline
66 let agent: Agent<_> = Agent::from(session);
67 let timeline = agent
68 .send(&GetTimeline::new().limit(5).build())
69 .await?
70 .into_output()?;
71 for (i, post) in timeline.feed.iter().enumerate() {
72 println!("\n{}. by {}", i + 1, post.post.author.handle);
73 println!(
74 " {}",
75 serde_json::to_string_pretty(&post.post.record).into_diagnostic()?
76 );
77 }
78
79 Ok(())
80}
81
82```
83
84If you have `just` installed, you can run the [examples](https://tangled.org/@nonbinary.computer/jacquard/tree/main/examples) using `just example {example-name} {ARGS}` or `just examples` to see what's available.
85
86## Component crates
87
88Jacquard is broken up into several crates for modularity. The correct one to use is generally `jacquard` itself, as it re-exports most of the others.
89
90| | | |
91| --- | --- | --- |
92| `jacquard` | Main crate | [](https://crates.io/crates/jacquard) [](https://docs.rs/jacquard) |
93|`jacquard-common` | Foundation crate | [](https://crates.io/crates/jacquard-common) [](https://docs.rs/jacquard-common)|
94| `jacquard-axum` | Axum extractor and other helpers | [](https://crates.io/crates/jacquard-axum) [](https://docs.rs/jacquard-axum) |
95| `jacquard-api` | Autogenerated API bindings | [](https://crates.io/crates/jacquard-api) [](https://docs.rs/jacquard-api) |
96| `jacquard-oauth` | atproto OAuth implementation | [](https://crates.io/crates/jacquard-oauth) [](https://docs.rs/jacquard-oauth) |
97| `jacquard-identity` | Identity resolution | [](https://crates.io/crates/jacquard-identity) [](https://docs.rs/jacquard-identity) |
98| `jacquard-lexicon` | Lexicon parsing and code generation | [](https://crates.io/crates/jacquard-lexicon) [](https://docs.rs/jacquard-lexicon) |
99| `jacquard-derive` | Macros for lexicon types | [](https://crates.io/crates/jacquard-derive) [](https://docs.rs/jacquard-derive) |
100
101## Changelog
102
103[CHANGELOG.md](./CHANGELOG.md)
104
105Highlights:
106
107- initial streaming support
108- experimental WASM support
109- better value type deserialization helpers
110- service auth implementation
111- XrpcRequest derive Macros
112- more builders in generated api to make constructing things easier (lmk if compile time is awful)
113- `AgentSessionExt` trait with a host of convenience methods for working with records and preferences
114- Improvements to the `Collection` trait, code generation, and addition of the `VecUpdate` trait to enable that
115
116
117## Experimental WASM Support
118
119Core crates (`jacquard-common`, `jacquard-api`, `jacquard-identity`, `jacquard-oauth`) compile for `wasm32-unknown-unknown`. Traits use [`trait-variant`](https://docs.rs/trait-variant) to conditionally exclude `Send` bounds on WASM targets. DNS-based handle resolution is gated behind the `dns` feature and unavailable on WASM (HTTPS well-known and PDS resolution still work).
120
121Test WASM compilation:
122```bash
123just check-wasm
124# or: cargo build --target wasm32-unknown-unknown -p jacquard-common --no-default-features
125```
126
127
128### Initial Streaming Support
129
130Jacquard is building out support for efficient streaming for large payloads:
131
132- **Blob uploads/downloads**: Stream media without loading into memory
133- **CAR file streaming**: Efficient repo sync operations
134- **Thin forwarding**: Pipe data between endpoints
135- **WebSocket support**: Bidirectional streaming connections
136
137Enable with the `streaming` feature flag. See `jacquard-common` documentation for details.
138
139## Development
140
141This repo uses [Flakes](https://nixos.asia/en/flakes)
142
143```bash
144# Dev shell
145nix develop
146
147# or run via cargo
148nix develop -c cargo run
149
150# build
151nix build
152```
153
154There's also a [`justfile`](https://just.systems/) for Makefile-esque commands to be run inside of the devShell, and you can generally `cargo ...` or `just ...` whatever just fine if you don't want to use Nix and have the prerequisites installed.
155
156[](./LICENSE)