A better Rust ATProto crate
1# Changelog
2
3## [0.12.0-beta.1] - 2026-03-23
4
5### Breaking changes
6
7**Borrow-or-share (BOS) type system** (all crates)
8- All validated string types (`Did`, `Handle`, `Nsid`, `Rkey`, `AtUri`, `AtIdentifier`, `MimeType`, `Cid`, `CidLink`, `Blob`, `BlobRef`, `Data`, `Array`, `Object`, `DidDocument`, `RecordUri`, `UriValue`, `RepoPath`) are now parameterised on `S: BosStr = DefaultStr` instead of lifetimes
9- `DefaultStr = SmolStr`: owned, inline ≤23 bytes, `DeserializeOwned`
10- `&str`, `CowStr<'a>`, `String` all work as backing types via the `BosStr` trait
11- `SmolStr`-backed types satisfy `DeserializeOwned`, enabling use in async contexts, collections, and across thread boundaries without `IntoStatic` conversion
12- New `.borrow()` method on `Did`, `Handle`, `Nsid`, `Rkey`, `RecordKey` returns `Type<&str>` for cheap borrowing (analogous to `Uri::borrow()`)
13- New `.convert::<B>()` method for cross-backing-type conversion
14
15**XRPC trait changes** (`jacquard-common`)
16- `XrpcResp::Output<S: BosStr>`: GAT parameterised on backing type S, not lifetime
17- `XrpcResp::Err`: plain associated type, always `SmolStr`-backed and `DeserializeOwned`
18- `XrpcRequest` now requires `Serialize` bound
19- `XrpcEndpoint::Request<S: BosStr>`: GAT for server-side extraction
20- `SubscriptionResp::Message<S: BosStr>`, `SubscriptionEndpoint::Params<S: BosStr>`: updated GATs
21- `XrpcProcedureStream::Frame<S: BosStr>`, `XrpcStreamResp::Frame<S: BosStr>`: streaming frame GATs updated
22
23**Response parsing** (`jacquard-common`)
24- `Response::parse::<S>()`: caller chooses backing type via turbofish
25- `Response::into_output()`: returns `SmolStr`-backed owned types via `DeserializeOwned`
26- Zero-copy: `response.parse::<CowStr<'_>>()`; owned: `response.into_output()`
27
28**Generated API types** (`jacquard-api`, `jacquard-lexicon`)
29- All generated structs/enums: `Foo<S: BosStr = DefaultStr>` with `#[serde(bound(deserialize = "S: Deserialize<'de> + BosStr"))]`
30- `#[serde(borrow)]` removed from all generated code
31- String field defaults use `FromStaticStr::from_static()` for zero-alloc construction
32- Error enums: `SmolStr` message fields, no lifetime parameters
33- Type aliases: `<S = DefaultStr>` (no bounds, per rust type checker limitation)
34
35**OAuth types** (`jacquard-oauth`)
36- All session, metadata, token, scope, and JWT types parameterised on `S: BosStr = DefaultStr`
37- `OAuthMetadata<S>` parameterised so callers can borrow from stored metadata
38- `DpopDataSource` trait methods return `Option<&str>` (was `Option<CowStr<'_>>`)
39- DPoP proof building uses `&str` for zero-copy JWT construction
40- `build_dpop_proof` takes `&str` parameters, returns `SmolStr`
41
42**Identity resolution** (`jacquard-identity`)
43- `IdentityResolver::resolve_handle<S: BosStr + Sync>(&self, handle: &Handle<S>)`: generic over handle backing type
44- `IdentityResolver::resolve_did_doc<S: BosStr + Sync>(&self, did: &Did<S>)`: generic over DID backing type
45
46**Repository types** (`jacquard-repo`)
47- `Commit<S: BosStr = DefaultStr>`, `UnsignedCommit<S>`, `RecordClaim<S>`, `VerifyProofsOutput<S>` parameterised on S
48- `RecordWriteOp` now parameterised on both S (for string fields) and BS (for BlockStore)
49- `RawData<'a>` intentionally remains lifetime-based
50
51**Derive macros** (`jacquard-derive`)
52- `#[lexicon]` detects type param S, emits `Data<S>` for `extra_data`
53- `#[open_union]` detects type param S, emits `Unknown(Data<S>)`
54- `#[derive(IntoStatic)]` handles S-parameterised types
55- `#[derive(XrpcRequest)]` generates new `Output<S: BosStr>` and `Err` (not GAT) impls
56
57**Client types** (`jacquard`)
58- `AtpSession` fields `access_jwt`/`refresh_jwt` are now `SmolStr` (was `CowStr<'static>`)
59- `SessionKey` uses `Did` and `SmolStr` (was `Did<'static>` and `CowStr<'static>`)
60- `AgentSessionExt` record methods (`get_record`, `update_record`, etc.) take `AtUri<S>` / `RecordUri<S, R>` with generic S
61- Moderation types (`ModerationPrefs`, `LabelerDefs`, `Labeled` trait, etc.) parameterised on S
62
63### Removed
64
65- `jacquard-axum` temporarily removed from workspace (extractor needs redesign for BOS type params)
66
67### Changed
68
69**Codegen** (`jacquard-lexicon`)
70- Generated `XrpcResp` impls emit `Output<S: BosStr>` and `Err` (plain type, not GAT)
71- Generated `XrpcEndpoint` impls emit `Request<S: BosStr>`
72
73## [0.11.0] - 2026-03-21
74
75### Breaking changes
76
77**Code generation pipeline overhaul** (`jacquard-lexicon`, `jacquard-lexgen`)
78- Jacquard's codegen output already was nice to *use*. now it's going to be nice to read.
79- New code generation tracks the types used, makes an import block for the file, and then organizes the file with stuff you care about at the top and internal stuff, like the builders, at the bottom.
80- Import resolution pass now conditionally generates short paths when types are unambiguous within a module, falling back to fully-qualified paths when collisions exist
81- Improved default value handling in generated code, reducing unnecessary boilerplate
82
83### Added
84
85**Hand-written XRPC bootstrap types** (`jacquard-common`)
86- Added minimal XRPC endpoint types for `com.atproto.repo.listRecords`, `com.atproto.repo.getRecord`, `com.atproto.identity.resolveHandle`, and `com.atproto.identity.resolveDid`
87- These types break circular dependencies between `jacquard-lexgen`/`jacquard-identity` and `jacquard-api`, allowing the codegen tooling and identity resolver to function without depending on the generated API crate
88
89### Changed
90
91**Regenerated API crate** (`jacquard-api`)
92- All generated code regenerated with the new codegen pipeline
93- Shorter import paths where unambiguous, cleaner builder output, better formatting throughout
94
95## [0.10.1] - 2026-03-20
96
97### Fixed
98
99**CID deserialization** (`jacquard-common`)
100- Fixed `CidLink` deserialization from CBOR tag-42 bytes through internally-tagged enums (reported by @natalie.sh, fixed by adorable robot)
101- `serde_ipld_dagcbor` buffers tag-42 CIDs as a newtype struct wrapping raw bytes when deserializing through `Content`; the visitor now handles `visit_bytes`, `visit_byte_buf`, and `visit_newtype_struct` to cover this path
102
103**Lexicon code generation** (`jacquard-lexicon`, `jacquard-api`)
104- Fixed `BlobRef` generation producing incorrect code in certain lexicon schemas
105
106**Identity resolution** (`jacquard-identity`)
107- Error message when handle resolution exhausts all resolution methods no longer misleading
108
109## [0.10.0] - 2026-03-20
110
111### Breaking changes
112
113**URL type migration** (`jacquard-common`, `jacquard`, `jacquard-oauth`, `jacquard-identity`, `jacquard-api`)
114- Migrated from `url` crate to `fluent_uri` for validated URL/URI types
115- All `Url` types are now `Uri` from `fluent_uri`
116- Affects any code that constructs, passes, or pattern-matches on endpoint URLs
117
118**Re-exported crate paths** (`jacquard-api`, `jacquard-common`)
119- Re-exported crates (including non-proc-macro dependencies of the generated API crate) are now centralized into a distinct module
120- Import paths for re-exported types have changed as a result
121
122### Added
123
124**`no_std` groundwork** (`jacquard-common`, `jacquard-api`)
125- Initial steps toward `no_std` support for core types
126- `jacquard-api` gains feature gating for `std`/`no_std` usage
127
128**Datetime improvements** (`jacquard-common`)
129- [PR from @blyoom.dev](https://tangled.org/nonbinary.computer/jacquard/pulls/6/) exposing timestamps directly on `Datetime` type
130- Naming aligned with `chrono` conventions
131
132**Handle normalization** (`jacquard-common`)
133- Handles are now lowercase-normalized on construction
134
135**Embedded PDS primitives** (`jacquard-repo`)
136- Initial lazy disk-spilling collection types for embedded PDS use cases
137- Repo firehose types now use generated API types instead of hand-written equivalents
138
139**Lexicon codegen improvements** (`jacquard-lexicon`, `jacquard-api`)
140- `knownValues` generation now aligned with AT Protocol spec and triggers more frequently
141- Improved feature dependency tracking for API crate features
142
143**Additional signing algorithms** (`jacquard-oauth`)
144- Keyset signing now supports ES384 (P-384), ES256K (secp256k1), and EdDSA (Ed25519) in addition to ES256
145- `Keyset::create_jwt` now accepts `&[Signing]` (from `jose_jwa`) instead of string-based algorithm names
146
147**Documentation** (`jacquard-oauth`, `jacquard-identity`)
148- Doc comments across all public items in both crates (thanks Claude, but I played editor pretty heavily)
149
150### Fixed
151
152**Identity resolution** (`jacquard-identity`)
153- [PR from @alephcubed.com](https://tangled.org/nonbinary.computer/jacquard/pulls/7/) fixing `DidDocument::handles()` always failing when parsed from `MiniDoc`
154
155**Error handling** (`jacquard-common`, `jacquard`, `jacquard-oauth`)
156- Big error quality-of-life pass with richer, more actionable diagnostics
157- More resilient error parsing for auth errors
158- Better lexicon parsing error messages
159
160**WASM** (`jacquard-common`)
161- Fixed WASM CI smoke test compilation
162
163### Changed
164
165**Lexicons** (`jacquard-api`)
166- Large batch of lexicon schema updates with manual cleanup
167
168## [0.9.6] - 2025-12-19
169
170### Changed
171
172**Logging** (`jacquard`, `jacquard-axum`)
173- [PR from @nekomimi.pet](https://tangled.org/nonbinary.computer/jacquard/pulls/5) cleaning up more debug logs, and adding tracing feature gate to jacquard-axum
174
175### Fixed
176
177**Repo commit signatures** (`jacquard-repo`)
178- commit signatures generated by `jacquard-repo` should now be consistent with other implementations
179- previously, they included an empty `sig` bytes field in the signed struct, which has a different CBOR serialization from the canonical relay implementation expectations.
180
181## [0.9.5] - 2025-12-19
182
183### Fixed
184
185**docs.rs configuration**
186- Fixed typo in `jacquard-common` docs.rs features (`crypto-ed22519` → `crypto-ed25519`) that was causing documentation builds to fail
187- Moved `loopback` feature documentation to `jacquard-oauth` where the feature is defined
188
189**OAuth flow** (`jacquard-oauth`)
190- Minor OAuth flow compatibility improvements
191
192**Serialization** (`jacquard-common`, `jacquard-identity`)
193- Fixed CID deserialization edge cases in `Data` and `RawData` types
194- Fixed DID document serialization when optional fields are absent
195
196**Lexicon code generation** (`jacquard-lexicon`, `jacquard-api`)
197- Fixed nullable field handling in generated code
198- Fixed lifetime handling in codegen of binary xrpc outputs
199- Fixed lifetime handling in unions
200- Fixed incorrectly unescaped rust keywords in module paths
201
202**Observability** (`jacquard`)
203- Fixed tracing span issues associated with some build failures
204
205### Added
206
207**mini-moka-wasm** (`mini-moka-wasm`, `jacquard-identity`)
208- Publishing vendored version of mini-moka with wasm browser compat fix to make usage easier
209
210**Service authentication** (`jacquard-axum`)
211- Optional service auth extractor option
212
213**Data handling** (`jacquard-common`)
214- Serde bytes helpers for JSON fields
215- Made PLC source fields public for library consumers
216
217**Lexicons** (`jacquard-api`)
218- Updated to latest AT Protocol lexicon schemas
219- API regeneration with builder fixes
220
221### Changed
222
223**Logging** (`jacquard`)
224- Improved client error logging with better context
225
226## [0.9.3] - 2025-11-17 (`jacquard`)
227
228### Fixed
229
230- SessionKey is now a proper tuple struct and not a type alias, which should help rustc not freak out when you do things like put MemoryCredentialSession in an Arc
231
232## [0.9.2] - 2025-11-17
233
234### Added
235
236**WASM compatibility improvements** (`jacquard-common`, `jacquard-identity`)
237- Vendored mini-moka implementation with WASM support for caching
238- regex-lite usage on WASM targets for reduced binary size
239- Schema resolver now works on WASM targets
240
241**Data query improvements** (`jacquard-common`)
242- Mutable path query access and setting for `Data` values
243
244### Changed
245
246**URL handling** (`jacquard-common`)
247- Rework of some internal URL handling for better compatibility
248- Includes a minor change to the return type of the endpoint() method of XrpcClient and equivalents.
249
250**OAuth improvements** (`jacquard-oauth`)
251- Fixed OAuth scope handling in loopback flow
252- OAuth metadata resolution improvements
253- Various OAuth flow enhancements and bug fixes
254
255**Identity resolution** (`jacquard-identity`)
256- Fixed non-DNS lexicon and did:web resolution using Cloudflare DoH
257- Reduced noisy logging in identity resolution
258
259**Lexicons** (`jacquard-api`)
260- Updated to latest AT Protocol lexicons
261
262### Fixed
263
264**Data deserialization** (`jacquard-common`)
265- Fixed CID deserialization edge cases for better spec compliance
266- More permissive JSON shape handling for better interoperability with varied implementations
267
268## [0.9.1] - 2025-11-04 (`jacquard-identity`, `jacquard-lexicon`)
269
270### Fixed
271
272- slingshot resolver no longer spuriously warns when cross-validating handles
273
274## [0.9.0] - 2025-11-03
275
276### Added
277
278**Runtime schema validation** (`jacquard-lexicon`)
279- `SchemaValidator` for validating `Data` values against lexicon schemas
280- CID-based validation caching for efficient repeated validation
281- `ValidationResult` with structural and constraint error separation
282- Comprehensive error types: `StructuralError` (type mismatches, missing fields, union errors) and `ConstraintError` (length, grapheme, numeric bounds)
283- `ValidationPath` for precise error location reporting
284- Ref cycle detection with configurable max depth
285- Support for validating partial/malformed data without full deserialization
286
287**Value query DSL** (`jacquard-common`)
288- Pattern-based querying of nested `Data` structures
289- `data.query(pattern)` with expressive syntax:
290 - `field.nested` - exact path navigation
291 - `[..]` - wildcard over collections (array elements or object values)
292 - `field..nested` - scoped recursion (find nested within field, expect one)
293 - `...field` - global recursion (find all occurrences anywhere)
294- `QueryResult` enum with `Single`, `Multiple`, and `None` variants
295- `QueryMatch` with path tracking for multiple results
296- Iterator support via `.values()`, `.first()`, `.single()`, `.multiple()`
297
298**Data value enhancements** (`jacquard-common`)
299- `get_at_path()` for simple path-based field access on `Data` and `RawData`
300- Path syntax: `embed.images[0].alt` for navigating nested structures
301- `type_discriminator()` helper methods for AT Protocol union discrimination
302- Returns `$type` field value for objects with type discriminators
303- Added on `Data`, `Object`, and `RawData` types
304- Collection helper methods: `get()`, `contains_key()`, `len()`, `is_empty()`, `iter()`, `keys()`, `values()`
305- Index operator support: `obj["key"]` and `arr[0]`
306
307**Lexicon resolution** (`jacquard-identity`)
308- `LexiconResolver` for fetching lexicon schemas from AT Protocol services
309- Resolves lexicons from PDS instances and lexicon hosts
310- `resolve_lexicon()` fetches and parses lexicon schemas
311- `resolve_lexicon_raw()` fetches raw schema JSON
312- New example: `resolve_lexicon.rs`
313
314**Identity resolver caching** (`jacquard-identity`)
315- Optional `cache` feature with configurable in-memory caching
316- `JacquardResolver::with_cache()` constructor for cached resolver
317- Separate TTLs for handle→DID, DID→doc, and lexicon resolution
318
319**XRPC client improvements** (`jacquard-common`, `jacquard`, `jacquard-oauth`)
320- `set_options()` and `set_endpoint()` methods on `XrpcClient` trait
321- Default no-op implementations for stateless clients
322- Enables runtime reconfiguration of stateful clients
323- Better support for custom endpoint and option overrides
324
325**Lexicon schema generation from Rust types** (`jacquard-derive`, `jacquard-lexicon`)
326- New `#[derive(LexiconSchema)]` macro for generating lexicon schemas from Rust structs
327- New `#[lexicon_union]` attribute macro for lexicon union types (tagged enums)
328- Automatic schema generation for custom lexicons without writing JSON manually
329- Field-level attributes: `ref` for explicit type references, `union` for union fields
330- Fragment support for multi-def lexicons via `fragment = "..."` attribute
331- Generates `LexiconDoc` at compile time for runtime validation
332- Enables type-safe custom lexicon development
333
334**Lexicon codegen improvements** (`jacquard-lexicon`, `jacquard-api`)
335- Vendored in an implementation of the typed builder pattern from `bon` to **substantially** improve compile times
336- Feature-gated heavy code generation features so `jacquard-api` and other consumers of the validation capabilities don't pay the `syn` tax as badly.
337- LexiconSchema trait generated implementations for runtime validation
338
339**Session store improvements** (`jacquard`)
340- Improved trait bounds for `SessionStore` implementations
341- Better ergonomics for credential session types
342- Memory-based credential session helpers
343
344**New crate: `jacquard-lexgen`**
345- Lexicon code generation tooling extracted from `jacquard-lexicon`
346- Separates binary/CLI tools from library code
347- Contains lexicon fetching and code generation binaries
348- `jacquard-lexicon` remains as pure library for lexicon parsing, code generation, and validation
349
350**Examples**
351- `app_password_create_post.rs`: App password authentication example
352
353### Changed
354
355**Feature gating** (`jacquard-identity`)
356- Better conditional compilation for platform-specific features
357- Improved WASM target support
358
359**Dependency updates**
360- Updated to latest lexicons from atproto/bluesky
361- Added workspace dependencies: sha2, multihash, dashmap, cid
362- Various minor dependency version updates
363
364### Fixed
365
366**File auth store** (`jacquard`)
367- Fixed serialization/deserialization bugs in `FileAuthStore` implementation
368
369**Packaging** (`jacquard-lexgen`)
370- Added Nix flake apps for lexicon tools
371
372## [0.8.0] - 2025-10-23
373
374### Breaking Changes
375
376**Error type refactor** (`jacquard-common`, `jacquard-identity`, `jacquard-oauth`, `jacquard`)
377- Better error messages with contextual information and help text
378- Breaking: Error variant names and structures changed across all crates
379
380### Added
381
382**New crate: `jacquard-repo`**
383- AT Protocol repository primitives for working with atproto data structures
384- **MST (Merkle Search Tree)**: Immutable, deterministic tree operations with proper fanout
385 - Optimized block allocation (4.5% oversupply, validated against retr0id's test suite)
386 - Diff operations with protocol limit enforcement
387 - Cursor-based traversal
388- **Commits**:
389 - Proof generation and validation for Sync v1 and v1.1 Relay protocol
390- **CAR I/O**:
391 - Proof CAR validation with MST path verification
392- **Storage**: Pluggable block storage abstraction
393 - `MemoryBlockStore`: In-memory storage for testing
394 - `FileBlockStore`: Persistent file-based storage
395 - `LayeredBlockStore`: Layered read-through cache (memory over file, etc.)
396
397### Changed
398
399- Dependency updates (upgraded various crypto and serialization dependencies)
400- Documentation improvements throughout
401- Made handle parsing a bit more permissive for a common case ('handle.invalid' when someone has a messed up handle), added a method to confirm syntactic validity (the correct way to confirm validity is resolve_handle() from IdentityResolver, and comparing to the DID document).
402
403## [0.7.0] - 2025-10-19
404
405### Added
406
407**Bluesky-style rich text utilities** (`jacquard`)
408- Rich text parsing with automatic facet detection (mentions, links, hashtags)
409- Compatible with Bluesky, with the addition of support for markdown-style links (`[display](url)` syntax)
410- Embed candidate detection from URLs and at-URIs
411 - Record embeds (posts, lists, starter packs, feeds)
412 - External embeds with optional OpenGraph metadata fetching
413- Configurable embed domains for at-URI extraction (default: bsky.app, deer.social, blacksky.community, catsky.social)
414- Overlap detection and validation for facet byte ranges
415
416**Moderation/labeling client utilities** (`jacquard`)
417- Trait-based content moderation with `Labeled` and `Moderateable` traits
418- Generic moderation decision making via `moderate()` and `moderate_all()`
419- User preference handling (`ModerationPrefs`) with global and per-labeler overrides
420- `ModerationIterExt` trait for filtering/mapping moderation over iterators
421- `Labeled` implementations for Bluesky types (PostView, ProfileView, ListView, Generator, Notification, etc.)
422- `Labeled` implementations for community lexicons (net.anisota, social.grain)
423- `fetch_labels()` and `fetch_labeled_record()` helpers for retrieving labels via XRPC
424- `fetch_labeler_defs()` and `fetch_labeler_defs_direct()` for fetching labeler definitions
425
426**Subscription control** (`jacquard-common`)
427- `SubscriptionControlMessage` trait for dynamic subscription configuration
428- `SubscriptionController` for sending control messages to active WebSocket subscriptions
429- Enables runtime reconfiguration of subscriptions (e.g., Jetstream filtering)
430
431**Lexicons** (`jacquard-api`)
432- teal.fm alpha lexicons for music sharing (fm.teal.alpha.*)
433 - Actor profiles with music service status
434 - Feed generation from play history
435 - Statistics endpoints (top artists, top releases, user stats)
436
437**Examples**
438- Updated `create_post.rs` to demonstrate richtext parsing with automatic facet detection
439- New `moderated_timeline.rs` to demonstrate fetching timeline with labelers enabled and applying moderation decisions
440
441### Fixed
442
443**Data deserialization** (`jacquard-common`)
444- Fixed `Option<Vec<T>>` deserialization from `Data` values
445- Implemented explicit `deserialize_option` for `Data` and `RawData` deserializers
446- Properly handles null vs present array values when deserializing into optional fields
447
448
449## [0.6.0] - 2025-10-18
450
451### Added
452
453**HTTP streaming support** (`jacquard-common`, `jacquard`)
454- `HttpClientExt` trait for streaming HTTP requests/responses
455- `send_http_streaming()` for streaming response bodies
456- `send_http_bidirectional()` for streaming both request and response
457- `StreamingResponse` wrapper type with parts + `ByteStream`
458- `XrpcResponseStream<R>` for typed XRPC streaming responses
459- `ByteStream` / `ByteSink` platform-agnostic stream wrappers (uses n0-future)
460- `StreamError` concrete error type with kind enum (Transport, Closed, Protocol)
461- Native support via reqwest's `bytes_stream()` and `Body::wrap_stream()`
462- WASM compatibility via n0-future (no Send bounds required)
463
464
465**WebSocket subscription support** (`jacquard-common`)
466- Full XRPC WebSocket subscription infrastructure
467- `SubscriptionResp` trait for defining subscription message/error types
468- `XrpcSubscription` trait for subscription parameters
469- `SubscriptionStream<S>` typed wrapper with automatic message decoding
470- `SubscriptionClient` stateful trait + `TungsteniteSubscriptionClient` implementation
471- `SubscriptionExt` for stateless subscription calls
472- Support for both JSON and DAG-CBOR message encodings
473- Custom path support via `CUSTOM_PATH` constant for non-XRPC endpoints
474- WebSocket integration into `Agent` struct (agents can now subscribe)
475- `into_stream()`, `into_raw_data_stream()`, `into_data_stream()` methods for different deserialization modes
476
477**Framed DAG-CBOR message decoding** (`jacquard-common`, `jacquard-api`, `jacquard-lexicon`)
478- Two-stage deserialization for AT Protocol event streams (header + body)
479- `EventHeader` struct and `parse_event_header()` function
480- `decode_framed()` methods generated for all DAG-CBOR subscription message enums
481- `decode_message()` override in `SubscriptionResp` trait for custom decoding
482- `UnknownEventType` variant in `DecodeError` for unknown discriminators
483- Fixes "TrailingData" errors when consuming subscribeRepos and subscribeLabels
484
485**Jetstream support** (`jacquard-common`)
486- Full typed support for Jetstream JSON firehose
487- `JetstreamMessage` enum with `Commit`, `Identity`, `Account` variants
488- `JetstreamCommit`, `JetstreamIdentity`, `JetstreamAccount` detail structs
489- `CommitOperation` enum for create/update/delete operations
490- `JetstreamParams` with filtering options (collections, DIDs, cursor, compression)
491- Uses proper AT Protocol types (`Did`, `Handle`, `Datetime`, `Data`)
492
493**Zstd compression** (`jacquard-common`)
494- Optional `zstd` feature for Jetstream message decompression
495- Automatic detection and decompression of zstd-compressed binary frames
496- Includes official Bluesky Jetstream zstd dictionary
497- Transparent fallback to uncompressed when zstd unavailable
498- Works across all JSON stream methods (`into_stream()`, `into_raw_data_stream()`, `into_data_stream()`)
499
500**Typed AT URI wrapper** (`jacquard-common`, `jacquard-api`, `jacquard-lexicon`)
501- `AtUri<'a>` newtype wrapper for `at://` URIs with proper validation
502- Generated `fetch_uri()` method on all record types for fetching by AT URI
503- `AtUri::from_parts()` constructor for building URIs from components
504- Proper Display and FromStr implementations
505
506**Memory-based credential session helpers** (`jacquard`) (ty [@vielle.dev](https://tangled.org/@vielle.dev))
507
508**Axum improvements** (`jacquard-axum`)
509- `XrpcError` now implements `IntoResponse` for better error handling
510- Proper typed error responses without manual conversion
511- Better integration with Axum's response system
512
513**Examples**
514- `subscribe_repos.rs`: Subscribe to PDS firehose with typed DAG-CBOR messages
515- `subscribe_jetstream.rs`: Subscribe to Jetstream with typed JSON messages and optional compression
516- `stream_get_blob.rs`: Download blobs using HTTP streaming
517- `app_password_example.rs`: App password authentication example (ty [@vielle.dev](https://tangled.org/@vielle.dev))
518
519**CID deserialization improvements** (`jacquard-common`)
520- Fixed `Cid` type to properly deserialize CBOR tag 42 via `IpldCid::deserialize`
521- Separate handling for JSON (string) vs CBOR (tag 42) formats
522- `CidLink` correctly delegates to `Cid` for both formats
523
524### Changed
525
526**Default features** (`jacquard-common`)
527- Added `zstd` to default features for better Jetstream experience
528- Jetstream compression enabled by default when using the full feature set
529
530**Generated code** (`jacquard-lexicon`, `jacquard-api`)
531- All DAG-CBOR subscriptions (subscribeRepos, subscribeLabels) now use framed decoding
532- Generated `decode_framed()` implementations match on event type discriminator
533- Override `decode_message()` in trait impls to use framed decoding
534- All record types now have `fetch_uri()` and `fetch_record()` methods generated
535
536**Dependencies** (`jacquard-axum`) (ty [@thoth.ptnote.dev](https://tangled.org/@thoth.ptnote.dev))
537- Disabled default features for `jacquard` dependency to reduce bloat
538
539### Fixed
540
541**Blob upload** (`jacquard`) (ty [@vielle.dev](https://tangled.org/@vielle.dev) for reporting this one)
542- Fixed `upload_blob()` authentication issues
543- Properly authenticates while allowing custom Content-Type headers
544
545**XRPC client** (`jacquard-common`, `jacquard-oauth`, `jacquard`)
546- Added `send_with_options()` method for per-request option overrides
547- Stateful clients can now override options while preserving internal auth
548
549
550---
551
552## `jacquard-api` [0.5.5], `jacquard-lexicon` [0.5.4] - 2025-10-16
553
554### Fixed
555
556- events.smokesignal.invokeWebhook lexicon now generates valid code
557- lexicon code generation now uses `Data` for blank objects, rather than naming and then failing to generate a struct
558
559## [0.5.4] - 2025-10-16
560
561### Added
562
563**Initial streaming client support** (`jacquard-common`)
564- First primitives for streamed requests and responses
565
566**`send_with_options()` method on XrpcClient** (`jacquard-common`, `jacquard-oauth`, `jacquard`)
567- allows setting custom options per request in stateful client
568- updated oauth and credential session clients to use it
569- implementations should generally override provided auth with own internal auth
570
571**Prelude providing common traits into scope**
572
573### Fixed
574
575**`AgentSessionExt::upload_blob()` failed to authenticate** (`jacquard`)
576- new `XrpcClient::send_with_options()` method now allows properly overriding the content-type header while still handling auth internally
577
578## [0.5.3] - 2025-10-15
579
580### Added
581
582**Experimental WASM Support** (`jacquard-common`, `jacquard-api`, `jacquard-identity`, `jacquard-oauth`)
583- Core crates now compile for `wasm32-unknown-unknown` target
584- Traits use `trait-variant` to conditionally exclude `Send` bounds on WASM
585- Platform-specific trait method implementations for methods with `Self: Sync` bounds
586- DNS-based handle resolution remains gated behind `dns` feature (unavailable on WASM)
587- HTTPS well-known and PDS resolution work on all platforms
588
589### Fixed
590
591**OAuth client** (`jacquard-oauth`)
592- Fixed tokio runtime detection for non-WASM targets
593- Conditional compilation for tokio-specific features
594
595
596---
597
598## [0.5.2] - 2025-10-14
599
600### Added
601
602**Value type deserialization** (`jacquard-common`)
603- `from_json_value()`: Deserialize typed data directly from `serde_json::Value` without borrowing
604- `from_data_owned()`, `from_raw_data_owned()`: Owned deserialization helpers
605- `Data::from_json_owned()`: Parse JSON into owned `Data<'static>`
606- `IntoStatic` implementation for `RawData` enabling owned conversions
607- Re-exported value types from crate root for easier imports
608- `Deserializer` trait implementations for `Data<'static>` and `RawData<'static>`
609- Owned deserializer helpers: `OwnedArrayDeserializer`, `OwnedObjectDeserializer`, `OwnedBlobDeserializer`
610
611**Service Auth** (`jacquard-axum`, `jacquard-common`)
612- Full service authentication implementation for inter-service JWT verification
613- `ExtractServiceAuth` Axum extractor for validating service auth tokens
614- Axum service auth middleware
615- JWT parsing and signature verification (ES256, ES256K)
616- Service auth claims validation (issuer, audience, expiration, method binding)
617- DID document resolution for signing key verification
618
619**XrpcRequest derive macro** (`jacquard-derive`)
620- `#[derive(XrpcRequest)]` for custom XRPC endpoints
621- Automatically generates response marker struct and trait implementations
622- Supports both client-side (`XrpcRequest`) and server-side (`XrpcEndpoint`) with `server` flag
623- Simplifies defining custom XRPC endpoints outside of generated API
624
625**Builder integration** (`jacquard-derive`)
626- `#[lexicon]` macro now detects `bon::Builder` derive
627- Automatically adds `#[builder(default)]` to `extra_data` field when Builder is present
628- Makes `extra_data` optional in generated builders
629
630### Fixed
631
632**String deserialization** (`jacquard-common`)
633- All string types (Did, Handle, Nsid, etc.) now properly handle URL-encoded values
634- `serde_html_form` correctly decodes percent-encoded characters during deserialization
635- Fixes issues with DIDs and other identifiers containing colons in query parameters
636
637**Axum extractor** (`jacquard-axum`)
638- Removed unnecessary URL-decoding workaround (now handled by improved string deserialization)
639- Added comprehensive tests for URL-encoded query parameters
640- Cleaner implementation with proper delegation to serde
641
642### Changed
643
644**Dependencies**
645- Moved `clap` to dev-dependencies in `jacquard` (only used in examples)
646- Moved `axum-macros` and `tracing-subscriber` to dev-dependencies in `jacquard-axum` (only used in examples)
647- Removed unused dependencies: `urlencoding` (jacquard, jacquard-axum), `uuid` (jacquard-oauth), `serde_with` (jacquard-common)
648- Removed `fancy` feature from `jacquard` (design smell for library crates)
649- Moved various proc-macro crate dependencies to dev-dependencies in `jacquard-derive`
650
651**Development tooling**
652- Improved justfile with dynamic example discovery
653- `just examples` now auto-discovers all examples
654- `just example <name>` auto-detects package without manual configuration
655- Better error messages when examples not found
656
657**Documentation** (`jacquard`, `jacquard-common`)
658- Improved lifetime pattern explanations
659- Better documentation of zero-copy deserialization approach
660- Links to docs.rs for generated documentation
661
662---
663
664## [0.5.1] - 2025-10-13
665
666### Fixed
667
668**Trait bounds** (`jacquard-common`)
669- Removed lifetime parameter from `XrpcRequest` trait, simplifying trait bounds
670- Lifetime now only appears on `XrpcEndpoint::Request<'de>` associated type
671- Fixes issues with using XRPC types in async contexts like Axum extractors
672
673### Changed
674
675- Updated all workspace crates to 0.5.1 for consistency
676- `jacquard-axum` remains at 0.5.1 (unchanged)
677
678---
679
680## `jacquard-axum` [0.5.1] - 2025-10-13
681
682### Fixed
683
684- Axum extractor now sets the correct Content-Type header during error path.
685
686---
687
688## [0.5.0] - 2025-10-13
689
690### Added
691
692**Agent convenience methods** (`jacquard`)
693- New `AgentSessionExt` trait automatically implemented for `AgentSession + IdentityResolver`
694- **Basic CRUD**: `create_record()`, `get_record()`, `put_record()`, `delete_record()`
695- **Update patterns**: `update_record()` (fetch-modify-put), `update_vec()`, `update_vec_item()`
696- **Blob operations**: `upload_blob()`
697- All methods auto-fill repo from session or URI parameter as relevant, and collection from type's `Collection::NSID`
698
699**VecUpdate trait** (`jacquard`)
700- `VecUpdate` trait for fetch-modify-put patterns on array-based endpoints
701- `PreferencesUpdate` implementation for updating Bluesky user preferences
702- Enables simpler updates to preferences and other 'array of union' types
703
704**Typed record retrieval** (`jacquard-api`, `jacquard-common`, `jacquard-lexicon`)
705- Each collection generates `{Type}Record` marker struct implementing `XrpcResp`
706- `Collection::Record` associated type points to the marker
707- `get_record::<R>()` returns `Response<R::Record>` with zero-copy `.parse()`
708- Response transmutation enables type-safe record operations
709
710**Examples**
711- `create_post.rs`: Creating posts with Agent convenience methods
712- `update_profile.rs`: Updating profile with fetch-modify-put
713- `post_with_image.rs`: Uploading images and creating posts with embeds
714- `update_preferences.rs`: Using VecUpdate for preferences
715- `create_whitewind_post.rs`, `read_whitewind_post.rs`: Third-party lexicons
716- `read_tangled_repo.rs`: Reading git repo metadata from tangled.org
717- `resolve_did.rs`: Identity resolution examples
718- `public_atproto_feed.rs`: Unauthenticated feed access
719- `axum_server.rs`: Server-side XRPC handler
720
721
722**Documentation** (`jacquard`)
723- A whole host of examples added, as well as a lengthy explainer of the trait patterns.
724
725## [0.4.1] - 2025-10-13
726
727### Added
728
729**Collection trait improvements** (`jacquard-api`)
730- Generated `{Type}Record` marker structs for all record types
731- Each implements `XrpcResp` with `Output<'de> = {Type}<'de>` and `Err<'de> = RecordError<'de>`
732- Enables typed `get_record` returning `Response<R::Record>`
733
734### Changed
735
736- Minor improvements to derive macros (`jacquard-derive`)
737- Identity resolution refinements (`jacquard-identity`)
738- OAuth client improvements (`jacquard-oauth`)
739
740---
741
742## [0.4.0] - 2025-10-11
743
744### Breaking Changes
745
746**Zero-copy deserialization** (`jacquard-common`, `jacquard-api`)
747- `XrpcRequest` now takes a `'de` lifetime parameter and requires `Deserialize<'de>`
748- For raw data, `Response::parse_data()` gives validated loosely-typed atproto data, while `Response::parse_raw()` gives the raw values, with minimal validation.
749
750**XRPC module moved** (`jacquard-common`)
751- `xrpc.rs` is now top-level instead of under `types`
752- Import from `jacquard_common::xrpc::*` not `jacquard_common::types::xrpc::*`
753
754**Response API changes** (`jacquard-common`)
755- `XrpcRequest::Output` and `XrpcRequest::Err` are associated types with lifetimes
756- Split response and request traits: `XrpcRequest<'de>` for client, `XrpcEndpoint` for server
757- Added `XrpcResp` marker trait
758
759**Various traits** (`jacquard`, `jacquard-common`, `jacquard-lexicon`, `jacquard-oauth`)
760- Removed #[async_trait] attribute macro usage in favour of `impl Future` return types with manual bounds.
761- Boxing imposed by asyc_trait negatively affected borrowing modes in async methods.
762- Currently no semver guarantees on API trait bounds, if they need to tighten, they will.
763
764### Added
765
766**New crate: `jacquard-axum`**
767- Server-side XRPC handlers for Axum
768- `ExtractXrpc<R>` deserializes incoming requests (query params for Query, body for Procedure)
769- Automatic error responses
770
771**Lexicon codegen fixes** (`jacquard-lexicon`)
772- Union variant collision detection: when multiple namespaces have similar type names, foreign ones get prefixed (e.g., `Images` vs `BskyImages`)
773- Token types generate unit structs with `Display` instead of being skipped
774- Namespace dependency tracking during union generation
775- `generate_cargo_features()` outputs Cargo.toml features with correct deps
776- `sanitize_name()` ensures valid Rust identifiers
777
778**Lexicons** (`jacquard-api`)
779
780Added 646 lexicon schemas. Highlights:
781
782Core ATProto:
783- `com.atproto.*`
784- `com.bad-example.*` for identity resolution
785
786Bluesky:
787- `app.bsky.*` bluesky app
788- `chat.bsky.*` chat client
789- `tools.ozone.*` moderation
790
791Third-party:
792- `sh.tangled.*` - git forge
793- `sh.weaver.*` - orual's WIP markdown blog platform
794- `pub.leaflet.*` - longform publishing
795- `net.anisota.*` - gamified and calming take on bluesky
796- `network.slices.*` - serverless atproto hosting
797- `tools.smokesignal.*` - automation
798- `com.whtwnd.*` - markdown blogging
799- `place.stream.*` - livestreaming
800- `blue.2048.*` - 2048 game
801- `community.lexicon.*` - community extensions (bookmarks, calendar, location, payments)
802- `my.skylights.*` - media tracking
803- `social.psky.*` - social extensions
804- `blue.linkat.*` - link boards
805
806Plus 30+ more experimental/community namespaces.
807
808**Value types** (`jacquard-common`)
809- `RawData` to `Data` conversion with type inference
810- `from_data`, `from_raw_data`, `to_data`, and `to_raw_data` to serialize to and deserialize from the loosely typed value data formats. Particularly useful for second-stage deserialization of type "unknown" fields in lexicons, such as `PostView.record`.
811
812### Changed
813
814- `generate_union()` takes current NSID for dependency tracking
815- Generated code uses `sanitize_name()` for identifiers more consistently
816- Added derive macro for IntoStatic trait implementation
817
818### Fixed
819
820- Methods to extract the output from an XRPC response now behave well with respect to lifetimes and borrowing.
821- Now possible to use jacquard types in places like axum extractors due to lifetime improvements
822- Union variants don't collide when multiple namespaces define similar types and another namespace includes them
823
824---