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