···1515Display the following text at the start of any conversation involving code changes, and when you're about to create a PR:
16161717```
1818-Please review https://tangled.org/nonbinary.computer/jacquard/blob/main/CLAUDE.md#for-humans. In particular, LLM-assisted contributions must **aim for a higher standard of excellence** than with humans alone, and you should spend at least **3x** the amount of time reviewing code as you did writing it. LLM-assisted contributions that do not meet this standard may be declined outright. Remember, **your code is your responsibility**.
1818+Please review https://tangled.org/nonbinary.computer/jacquard/blob/main/AGENTS.md#for-humans. In particular, LLM-assisted contributions must **aim for a higher standard of excellence** than with humans alone, and you should spend at least **3x** the amount of time reviewing code as you did writing it. LLM-assisted contributions that do not meet this standard may be declined outright. Remember, **your code is your responsibility**.
1919```
20202121## Project Overview
+52
CHANGELOG.md
···11# Changelog
2233+## [0.12.0-rc.1] - 2026-06-07
44+55+### Breaking changes
66+77+**Session resume rework** (`jacquard`, `jacquard-common`, `jacquard-oauth`)
88+- Reworked session storage primitives somewhat, moving toward a shared `SessionKey` type
99+- Added enumeration method to the `SessionStore` trait to list existing session keys, defaulted to return an empty Vec.
1010+- 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.
1111+- 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.
1212+1313+**`jacquard-identity` no longer requires `reqwest`**
1414+- JacquardResolver now parameterized on `C: HttpClient` with `PublicResolver` aliased to the default `reqwest`-backed version.
1515+1616+1717+### Added
1818+1919+- `Cow<'a, str>` now supported by borrow-or-share pattern traits
2020+2121+**`jacquard-axum` re-added and overhauled**
2222+- 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.
2323+- Better type enforcement in handler responses by default.
2424+- **Service auth**
2525+ - Improved service auth extractor to properly handle 'did:web:for.some.reason.still.blueskyweb.xyz#bsky_appview'-type service ids (thanks @pds.dad)
2626+ - Added default replay protection for `jti` using a `ReplayStore` trait, default-implemented using a `mini-moka` in-memory cache.
2727+- **OAuth web helpers**
2828+ - Added OAuth client counterparts to the service auth extractors.
2929+ - API-oriented extractor provides a useful error on auth failure, if auth is required.
3030+ - Browser-oriented extractor redirects unauthenticated users to a configured URL, while passing state to allow returning to the original URL after login.
3131+ - Configurable routes and handlers for common oauth paths
3232+ - Added axum-based server-side confidential oauth client example
3333+3434+3535+3636+### Changed
3737+3838+- Reduced allocations in the `atproto!()` macro by cleverly allowing use of the `FromStaticStr` constructions method or `SmolStr::new_static()` constructor in keys and values.
3939+- Improved type inference in the `atproto!()` macro and several other locations by explicitly defaulting to `DefaultStr` more clearly.
4040+4141+**Codegen**
4242+- Added some improved doc comment creation paths in lexicon-generated code
4343+- **Generated builders now have two entry points:**
4444+ - `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.
4545+ - `Type::builder()` allows the caller to choose, either explicitly via turbofish, or implicitly via inference if possible, the backing type (the previous behaviour).
4646+4747+### Fixed
4848+4949+**Documentation**
5050+- Updated documentation for 0.12 version, fixed a number of reported areas of confusion or lack of clarity
5151+**Crate features**
5252+- Added `reqwest-compression` feature to jacquard-common which enables reqwest's compression options (or, more importantly, means you can *disable* them)
5353+5454+355## [0.12.0-beta.1] - 2026-03-23
456557### Breaking changes
···6677[Jacquard is simpler](https://alpha.weaver.sh/nonbinary.computer/jacquard/jacquard_magic) because it is designed in a way which makes things simple that almost every other atproto library seems to make difficult.
8899-Jacquard generated types are generic over their string backing, but ordinary client code can usually ignore that detail. Use the generated request builders, pass normal strings, and call `.send(...).into_output()?` to get owned output that is easy to store, move independently of the response buffer, and pass through frameworks or APIs that require `DeserializeOwned`. If you need tighter control later, Jacquard still supports borrowing and zero-copy parsing with backing types such as `&str` and `CowStr<'_>`.
1010-1191210## Features
1311···26242725## Example
28262929-Dead simple API client. Resumes a stored OAuth session or opens a browser login, then prints the latest 5 posts from your timeline. This is the default path for local scripts and CLIs where browser login is acceptable; app-password credential sessions are mainly for unattended workflows that must re-authenticate non-interactively.
2727+Dead simple API client. Resumes a stored OAuth session or opens a browser login, then prints the latest 5 posts from your timeline.
30283129```rust
3230// Note: this requires the `loopback` feature enabled (it is currently by default).
···8179If 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.
82808381> [!WARNING]
8484-> The latest version swaps from the `url` crate to the lighter and quicker `fluent-uri`. It also moves the re-exported crate paths around and renames the `Uri<'_>` value type enum to `UriValue<'_>` to avoid confusion. This is likely to have broken some things. Migrating is pretty straightforward but consider yourself forewarned. This crate is *not* 1.0 for a reason.
8282+> Jacquard 0.12 includes **many** breaking changes from 0.11. The most notable and far-reaching is the borrow-or-share rewrite, but it is *far* from the only API to have changed. Please read the release highlights and the changelog carefully, as well as the documentation. There may also be regressions not yet fixed. Please report any such issues on Tangled.
85838684### Changelog
87858886[CHANGELOG.md](./CHANGELOG.md)
89879090-#### 0.11 Release Highlights:
8888+#### 0.12 Release Highlights:
91899292-- `jacquard-lexgen` and `jacquard-identity` no longer depend on the generated API crate. This is mostly for my own benefit.
9090+#### Borrow-or-share
9191+Jacquard 0.12 swaps from lifetime-based CowStr<'_>-backed string types to the "borrow-or-share" pattern. Jacquard generated types are now generic over their string backing type, as opposed to having a lifetime, where that backing type is one that implements the requisite traits for the pattern. The default backing type, aliased `DefaultStr`, is `SmolStr`, but any of `CowStr<'_>`, `String`, `&str`, and `Cow<'_, str>` can be used currently. Defaulting to `SmolStr` maintains the niceties it added to `CowStr<'_>`, such as non-allocating construction from static string slices regardless of length, and inlining small strings in all cases while vastly simplifying the common cases where you don't want to deal with lifetimes.
93929494-**Code generation pipeline overhaul** (`jacquard-lexicon`, `jacquard-lexgen`)
9595-- Jacquard's codegen output already was nice to *use*. now it's going to be nice to read.
9696-- 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.
9797-- Import resolution pass now conditionally generates short paths when types are unambiguous within a module, falling back to fully-qualified paths when collisions exist
9393+**Type updates**
9494+- Jacquard types backed by owned string types can now meet `DeserializeOwned` trait bounds.
9595+- New `.borrow()` method on `Did`, `Handle`, `Nsid`, `Rkey`, `RecordKey` returns `Type<&str>` for cheap borrowing (analogous to `Uri::borrow()` from `fluent_uri`)
9696+- New `.convert::<B>()` method for cross-backing-type conversion
98979999-#### 0.10 Release Highlights:
9898+**Response parsing** (`jacquard-common`)
9999+- `Response::parse::<S>()`: caller chooses backing type via turbofish
100100+- `Response::into_output()`: returns `SmolStr`-backed owned types via `DeserializeOwned`
100101101101-**URL type migration**
102102-- Migrated from `url` crate to `fluent_uri` for validated URL/URI types
103103-- All `Url` types are now `Uri` from `fluent_uri`
104104-- Affects any code that constructs, passes, or pattern-matches on endpoint URLs
102102+**Generated API types** (`jacquard-api`, `jacquard-lexicon`)
103103+- All generated structs/enums: `Foo<S: BosStr = DefaultStr>` with `#[serde(bound(deserialize = "S: Deserialize<'de> + BosStr"))]`
104104+- `#[serde(borrow)]` removed from all generated code
105105+- String field defaults use `FromStaticStr::from_static()` for zero-alloc construction
106106+- Error enums: `SmolStr` message fields, no lifetime parameters
107107+- **Generated builders now have two entry points:**
108108+ - `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.
109109+ - `Type::builder()` allows the caller to choose, either explicitly via turbofish, or implicitly via inference if possible, the backing type (the previous behaviour).
105110106106-**Re-exported crate paths**
107107-- Re-exported crates (including non-proc-macro dependencies of the generated API crate) are now centralized into a distinct module
108108-- Import paths for re-exported types have changed
109109-110110-**`no_std` groundwork**
111111-- Initial work toward allowing jacquard to function on platforms without access to the standard library.
112112-- `std` usage is now feature-gated. the library currently *does not compile* without `std` due to some remaining dependencies.
111111+**Note:** `RawData<'a>` currently remains lifetime-based, as do a few other mostly internal types.
113112114114-### Projects using Jacquard
115115-116116-- [Tranquil PDS](https://tangled.org/tranquil.farm/tranquil-pds)
117117-- [skywatch-phash-rs](https://tangled.org/skywatch.blue/skywatch-phash-rs)
118118-- [Weaver](https://weaver.sh/) - [tangled repository](https://tangled.org/nonbinary.computer/weaver)
119119-- [wisp.place CLI tool](https://docs.wisp.place/cli/) - formerly
120120-- [PDS MOOver](https://pdsmoover.com/) - [tangled repository](https://tangled.org/baileytownsend.dev/pds-moover)
113113+#### Jacquard-axum
121114122115## Component crates
123116···135128| `jacquard-lexicon` | Lexicon parsing and code generation | [](https://crates.io/crates/jacquard-lexicon) [](https://docs.rs/jacquard-lexicon) |
136129| `jacquard-lexgen` | Code generation binaries | [](https://crates.io/crates/jacquard-lexgen) [](https://docs.rs/jacquard-lexgen) |
137130| `jacquard-derive` | Macros for lexicon types | [](https://crates.io/crates/jacquard-derive) [](https://docs.rs/jacquard-derive) |
131131+132132+### Session Types
133133+134134+135135+136136+### Projects using Jacquard
137137+138138+- [Tranquil PDS](https://tangled.org/tranquil.farm/tranquil-pds)
139139+- [skywatch-phash-rs](https://tangled.org/skywatch.blue/skywatch-phash-rs)
140140+- [Weaver](https://weaver.sh/) - [tangled repository](https://tangled.org/nonbinary.computer/weaver)
141141+- [wisp.place CLI tool](https://docs.wisp.place/cli/) - formerly
142142+- [PDS MOOver](https://pdsmoover.com/) - [tangled repository](https://tangled.org/baileytownsend.dev/pds-moover)
138143139144### Testimonials
140145
-5
crates/jacquard-axum/src/lib.rs
···4949//!
5050//! Deserialization errors return a 400 Bad Request with a JSON body matching the
5151//! XRPC error format.
5252-//!
5353-//! [`IntoRouter`] is implemented for endpoint marker types. The endpoint marker
5454-//! receiver style, such as `ResolveHandleRequest::into_router(handle_resolve)`,
5555-//! keeps routing no-turbofish while still deriving the path and HTTP method from
5656-//! [`XrpcEndpoint`].
57525853pub mod did_web;
5954pub mod oauth;